How to work with interfaces and JPA - java

I should start out by saying that I am fairly new to Java EE and that I do not have a strong theoretical background in Java yet.
I'm having trouble grasping how to use JPA together with interfaces in Java. To illustrate what I find hard I created a very simple example.
If I have two simple interfaces Person and Pet:
public interface Person
{
public Pet getPet();
public void setPet(Pet pet);
}
public interface Pet
{
public String getName();
}
And an Entity PersonEntity which implements Person as well as a PetEntity which implements Pet:
#Entity
public class PersonEntity implements Person
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private PetEntity pet;
#Override
public void setPet(Pet pet)
{
/* How do i solve this? */
}
}
#Entity
public class PetEntity implements Pet
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
/* Getters and Setters omitted */
}
How do I properly handle the case in the setPet method in which I want to persist the relationships between the two entities above?
The main reason I want to use interfaces is because I want to keep dependencies between modules/layers to the public interfaces. How else do I avoid getting a dependency from e.g. my ManagedBean directly to an Entity?
If someone recommends against using interfaces on entities, then please explain what alternatives methods or patterns there are.

You can use targetEntity property in the relationship annotation.
#Entity
public class PersonEntity implements Person {
private Long id;
private Pet pet;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Override
#OneToOne(targetEntity = PetEntity.class)
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.pet = pet;
}
}

Related

Why can this JpaRepository be used for an entity that it doesn't relate to?

Spring Boot seems to be doing something behind the scenes that shouldn't work.
I'm moving a project to Spring Boot and was trying to get spring-boot-starter-data-jpa to work with the fewest changes to the code-base as I could get away with.
Somewhere in the code base, we marshal an xml or json document into an Entity using polymorphic dispatch, and send that entity straight to a database, without knowing exactly what type the entity was.
We have an interface Entity, from which many of our POJOs inherit:
public interface Entity {
Long getId();
void setId(Long id);
}
Example POJO:
#javax.persistence.Entity
#Table
public class Address implements Entity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
// fields, getters setters
}
Unfortunately, Spring CrudRepository and JpaRepository don't allow interfaces in the generics signature, so I created an AbstractEntity which inherits from Entity.
#javax.persistence.Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name="descriminatorColumn")
public class AbstractEntity implements Entity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Override
public Long getId() {
return id;
}
#Override
public void setId(Long id) {
this.id = id;
}
}
I then created a repository for AbstractEntity:
public interface AbstractEntityRepository<T extends AbstractEntity, ID> extends JpaRepository<T, ID> {
}
I was going to make my Entity POJOs inherit from AbstractEntity, but before I did I noticed that I don't need to.
The following code saves an address to the database:
#SpringBootApplication
#EnableJpaRepositories("com.remsdaq.resque.officersubscription.*")
#EntityScan("com.remsdaq.resque.officersubscription.*")
public class OfficerSubscription implements CommandLineRunner {
private static final Logger LOG = getLogger(OfficerSubscription.class);
#Autowired
private AbstractEntityRepository repository;
public static void main(String[] args) throws Exception {
SpringApplication.run(OfficerSubscription.class, args);
}
#Override
public void run(String... args) throws Exception {
Address address = new Address();
repository.save(address);
}
}
So my question is, how can my AbstractEntityRepository work, when AbstractEntity is not a superclass of Address?
If there's some reasonable reason why this works, and an expectation for this behavior not to change, it might not be too bad to leave it as it is, but without knowing how this is working, it's smelling too much for me to trust it.
Many thanks.

Inheritance in entities, using objectbox

In my code I put some base fields in base abstract class BaseEntity:
public abstract class BaseEntity {
#Id
private long id;
public BaseEntity() {
}
public BaseEntity(long id) {
this.id = id;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
So, in child class User I am not define an id field:
#Entity
public class User extends BaseEntity {
private String name;
private String login;
private String gender;
private String email;
private String phoneNumber;
private Date registrationDate;
private Date lastActivityDate;
private long systemId;
public User() {
}
...Getters and Setters
}
because it defined in superclass. I don't want to create in every class an id field, and don't want to persist in database BaseEntity class. And I get an error:
Error:[ObjectBox] Code generation failed: No ID property found for Entity User (package:...)
How can I use objectbox with an inheritance?
Polymorphism of entities is not supported at this time, but there is a feature request.
If it helps, you can go for an interface. E.g.:
public interface BaseEntity {
long getId();
}
Note: you could have a setter for the ID, but my personal advice would be to have the id field package private (so ObjectBox has access from generated classes) and not have a setter because it would suggest it's OK to change the ID at any time.
Update: ObjectBox 1.4 introduced entity inheritance (non-polymorphic).
#Oleg Objectbox don't support inheritance in entity class as it will map every entity to a separate table in db and use this #Id variable as unique id to identify a row(entity instance) in that table. Thus #Id variable is must for every entity class.
In general,
for each for Child class to access Parent class variables it have to be either protected or public but in your id is private so change it to either protected it will work.
protected long id;
if you mark it is as protected only the parent and its child class can access it and when you mark it as public any class can access it.
marking it as private means only parent class can access it

Why can I not override and annotate a getter method for entity mapping?

Given this class:
#MappedSuperclass
public abstract class AbstractEntity {
int id;
public void setId(int id) { this.id = id; }
public int getId() { return id; }
// other mappings
}
I want to define an entity:
#Entity
public class SomeEntity extends AbstractEntity {
#Override
#Id // or #OneToOne etc.
public int getId() { return id; }
}
But this fails with a "No identifier specified"
(or a "Could not determine type for") error on SomeEntity. If I remove the getter from the superclass it works. Can't I do this override strategy? Why not, or if yes - how?
Adding
#AttributeOverride(name = "id", column = #Column(name = "ID"))
to the subclass does not change the error.
For you to create an entity class there are requirements that the class must meet. Ex. must have a public/private constructor.
Here is the list of requirements:
http://docs.oracle.com/javaee/5/tutorial/doc/bnbqa.html
Hope this helps.

Use save() method in Play! with inheritance (JPA)

I have my super abstract class :
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class User {
#Id
public int id;
public String name;
}
And two other classes extends User:
Customer
#Entity
#Table(name = "customers")
public class Customer extends User{
#Id
public int id;
public String role;
public Customer(String role){
super();
this.role = role;
}
}
Seller
#Entity
#Table(name = "sellers")
public class Seller extends User{
#Id
#GeneratedValue
public int id;
public String role; // Seller
public Seller(String role){
super();
this.role = role;
}
}
I would like to be able to use the save() method in play, so I wrote this :
public static Result saveCustomer(){
Customer customer = Form.form(Customer.class).bindFromRequest().get();
customer.save();
return ok();
}
But, save() is not defined.
What would be the way to solve this issue ?
Actually... to get the entityManager in play 2.x you hava a Helper.
JPA.em().persist(object);
you can see more information in this link.
save() method is a part of GenericModel which belongs to Play 1.x. Since, you use Play 2.x you should use JPA object and entityManager.persist() method.

MappedSuperclass - Change SequenceGenerator in Subclass

I'm using JPA2 with Hibernate and try to introduce a common base class for my entities. So far it looks like that:
#MappedSuperclass
public abstract class BaseEntity {
#Id
private Long id;
#Override
public int hashCode() {
// ...
}
#Override
public boolean equals(Object obj) {
// ...
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
}
However, for every table theres a sequence $entityname_seq which I want to use as my sequence generator. How can I set that from my subclass? I think I need to override #GeneratedValue and create a new SequenceGenerator with #SequenceGenerator.
Yes, it is possible. You can override the default generator name with the #SequenceGenerator annotation.
Base class
#MappedSuperclass
public abstract class PersistentEntity implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "default_gen")
protected Long id = 0L;
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
}
Sequence (SQL)
create sequence role_seq;
Derived class
#Entity
#Table(name = "role")
#SequenceGenerator(name = "default_gen", sequenceName = "role_seq", allocationSize = 1)
public class Role extends PersistentEntity implements Serializable
{
private static final long serialVersionUID = 1L;
#NotNull
#Size(max = 32)
private String name;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
This approach worked fine in Hibernate 4.1.x, but it didn't in EclipseLink 2.x.
edit
As per the comment, it seems to be working with EclipseLink 2.6.1-RC1.
In JPA that cannot be done with annotations. Annotation itself cannot be overridden. Entity inherits all the mapping information from MappedSuperClass. There is only two annotations that can be used to redefine mappings inherited from mapped superClass:
AttributeOverride to override column mappings and
AssociationOverride to override join columns / table.
Neither of them helps with GeneratedValue.
With EclipseLink, you can use a Customizer. DescriptorCustomizer interface defines a way to customize all the information about a jpa descriptor (aka a persistent entity).
public class SequenceCustomizer implements DescriptorCustomizer {
#Override
public void customize(ClassDescriptor descriptor) throws Exception {
descriptor.setSequenceNumberName(descriptor.getTableName());
}
}
and in your mapped superclass:
#MappedSuperclass
#Customizer(SequenceCustomizer.class)
public abstract class AbstractEntity implements Serializable {
...
}
I'm writing this as it gets too unreadable as the comment on the accepted answer:
I have a BaseEntity that every other Entity inherits from:
BaseEntity.java:
#MappedSuperclass
public abstract class BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ID")
private Long id;
I then have two Entities User and Order that both inherit from BaseEntity whilst also having the #SequenceGenerator annotation:
User.java:
#SequenceGenerator(name = "SEQ_ID", sequenceName = "SEQ_USER", allocationSize = 1)
public class User extends BaseEntity { ... }
Order.java:
#SequenceGenerator(name = "SEQ_ID", sequenceName = "SEQ_ORDER", allocationSize = 1)
public class Order extends BaseEntity { ... }
It works on H2 at least with 2 Sequences SEQ_USER and SEQ_ORDERS:
select SEQ_USER.nextval from dual;
select SEQ_ORDERS.nextval from dual;

Categories

Resources