How to annotate composition of abstract class instance? - java

I have a design of Person as shown in the following picture. The design is to allow a person to change his role from Staff to Student, Student to Faculty and so on.
I want to persist this system to DB using hibernate annotation. Does anyone know how to do that?
Thanks a lot!

So you have 1:N relation between Entity Persona and abstract entity Person Role. Think this may work for you.
#Entity
public class Person {
// Here you have all roles in some collection.
#OneToMany(mappedBy="person", fetch=FetchType.LAZY)
private List<PersonRole> roles;
...
}
#Entity
public abstract class PersonRole {
#ManyToOne
#JoinColumn(name="PERSON_ID")
private Person person;
...
}
#Entity
public class Staff extends PersonRole {
...
}
Also don't forget to set proper
#Inheritance(strategy=InheritanceType.<strategy>)
to define how class model is mapped to relational model.
Edit: Unfortunately #MappedSuperclass can't be used in relations mapping so this is not an option here as long as you would like to have PersonRole collection in Person entity.

Related

How to inherit and replace a #Entity from commons package?

I have an #Entity in a commons package:
#Entity("person")
public class Person {
//fields...
}
In an implementing application, I want to extend this entity with some custom fields.
But I still want to map to table person:
#Entity("person")
public class Person extends org.commons.Person {
//additional fields
}
Result:
Caused by: org.hibernate.DuplicateMappingException: The [Person] and [CustomPerson] entities
share the same JPA entity name: [person] which is not allowed
So how can I extend that entity and tell spring to somehow "forget" about the parent Person entity, so that only my CustomPerson is loaded?
The problem is that you are assigning the same name to both entities. You don't need to do that. You can annotate your parent entity with #Entity, #Table and #Inheritance (which by default employs single table inheritance) and your subclass entities with just #Entity:
#Entity
#Table("person")
#Inheritance
public class Person {
//fields...
}
#Entity
public class CustomPerson extends Person {
//additional fields
}

Hibernate mapping generic class with #MappedSuperClass ancestor

I've run into a problem and I don't know, if is possible to solve it. Let me show you my code and explain the situation. I have an abstract class User mapped as superclass.
#MappedSuperclass
public abstract class User extends AbstractEntity {
}
Then I have two classes, Person and Company, extending the superclass.
#Entity
public class Person extends User {
}
#Entity
public class Company extends User {
}
Since this, everything is ok. I have two tables called by the entity names and it works june fine. But, I have some entity called Invoice, where I have to map Person or Company into.
#Entity
public class Invoice extends AbstractEntity {
#ManyToOne()
#JoinColumn(name="user_id", updatable=false)
private Class<? extends User> user;
}
The problem is that I don't know, which implementation of User will be mapped to this Invoice entity. With this solution, Hibernate gives me an error org.hibernate.AnnotationException: #OneToOne or #ManyToOne on com.xxx.user references an unknown entity: java.lang.Class
I understand it, but please, is there any way to implement this behaviour without an exception ? Or am I completely wrong and nothing similar can be done in ORM ?
I cannot use private User user because User is a #MappedSuperclass, not an #Entity.
Thanks !
Well instead of using lower bound, why not just use the type User:
#Entity
public class Invoice extends AbstractEntity {
#ManyToOne()
#JoinColumn(name="user_id", updatable=false)
private User user;
}
Like this, you should still be able to put any class that extends from the User class as the previous declaration. However this will not work as User is not an Entity.
It should however work, if you declare User as entity but set inheritance strategy to TABLE_PER_CLASS.
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class User extends AbstractEntity {
}
Hibernate won't see the generic "User" at runtime (type erasure).
Use User instead of Class<? extends User>
Note that even if Hibernate could see the generic type, you'd still have a user Class not a User...

JPA 2.0 / Hibernate InheritanceType TABLE_PER_CLASS and OneToMany/ManyToOne bidirectional relation

We have 2 tables (an active table and an archive table) which have the same structure (ex. Employee and EmployeeArchive). To be able to leverage common code to use results for both tables we have an abstract parent class that defines all the methods and annotations.
We like to be able to perform queries that will use the same query for both tables and union the results together.
We have another entity/table (ex. Organization) with a onetomany/manytoone bidirectional relationship with Employee; Organization has a List of Employees and every employee has an organization.
When getting the employees of an organization via the association we only want the employees from the active table not the archive.
Is there a way to achieve what we are attempting or a viable workaround?
We have tried various implementations of #MappedSuperclass, #Entity/#InheritanceType.TABLE_PER_CLASS to try to achieve what we want. Each implementation would nearly achieve what we want but not completely. For example to be able to query both tables we could have an abstract parent Entity with InheritanceType.TABLE_PER_CLASS but then we could not have the mappedBy relationship to Employee in the Organization. We can use a MappedSuperclass as the parent to be able to have the correct relationship but then we cannot query both the Archive and Active tables via the union.
Here is basically what we are trying to layout:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class AbstractEmployee {
#ManyToOne
#JoinColumn(name="employeeId", nullable=false)
Organization org;
...
}
#Entity
public class Employee extends AbstractEmployee {
}
#Entity
public class EmployeeArchive extends AbstractEmployee {
}
#Entity
public class Organization {
#OneToMany(cascade=ALL, mappedBy="org")
List<Employee> employees;
...
}
Code
public List<AbstractEmployee> getAllEmployees()
{
Query query = em.createQuery("SELECT e FROM AbstractEmployee e where e.name = ‘John’", AbstractEmployee.class);
return query.getResultList();
}
public List<Organization> getOrganizations()
{
Query query = em.createQuery("SELECT e FROM Organization o ", Organization.class);
List<Organization> orgs = query.getResultList();
// fetch or eager fetch the Employees but only get the ones from the active employee table
return orgs;
}
We also tried to have the parent class extend the MappedSuperclass and put the implementation and annotations in the MappedSuperclass but we get an AnnotationException for the relationship of the Organization
#MappedSuperclass
public abstract class AbstractMapped {
#ManyToOne
#JoinColumn(name="employeeId", nullable=false)
Organization org;
}
#Entity
#Inheritance(#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS))
public abstract class AbstractEmployee extends AbstractMapped {
... `Constructors` ...
}
On deployment we get the following exception:
Caused by org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: Employee.org in Organizaztion.employees
at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:685)
You can do this by changing the mapping of Organization to Employee so that it uses a relationship table, rather than having the org field in the Employee table. See the example in the Hibernate documentation, which for you would look something like:
#Entity
public class Organization {
#OneToMany(cascade=ALL)
#JoinTable(
name="ACTIVE_EMPLOYEES",
joinColumns = #JoinColumn( name="ORGANIZATION_ID"),
inverseJoinColumns = #JoinColumn( name="EMPLOYEE_ID")
)
List<Employee> employees;
...
}
However, I have to say that I think having two tables to represent current vs archived Employees is a bad idea. This sounds to me like a 'soft delete' kind of situation, which is better handled with an in-table flag (IS_ACTIVE, or something). Then you don't have these odd abstract classes to do your queries, multiple tables with the same kind of data, etc etc. A bit of a description of this strategy is here.
Then you can use the non-join table mapping that you've already got, and use the #Where annotation to limit the employees in an organization to ones that have IS_ACTIVE set to true. An example of this approach is here.
This is one of the annoying things about hibernate. The way to do this is to have another abstract class, AbstractMapped, which simply looks like this:
#MappedSuperclass
public abstract class AbstractMapped {
}
and then have AbstractEmployee extend AbstractMapped. Then you have AbstractEmployee as both an Entity and a Mapped Superclass, even though the two tags are mutually exclusive.
AbstractEmployee should be the #MappedSuperClass, and should not be an #Entity, which creates a table for the class.
Organization should contain a List<AbstractEmployee> not of Employee.

JPA Inheritance mapping multiple implementations

I have a problem with JPA inheritance. See my entities below. I have a Person that can be in either a House or a Car, never at the same time of course. Both Car and House implement the PersonHoldable interface. I know I cannot map an Entity directly to an interface.
This is my model:
#Entity
public class Person{
private PersonHoldable personHoldable; // either a Car or a House
// This does not work of course because it's an interface
// This would be the way to link objects without taking JPA into consideration.
#OneToOne
public PersonHoldable getPersonHoldable() {
return this.personHoldable;
}
public void setPersonHoldable(PersonHoldable personHoldable) {
this.personHoldable = personHoldable;
}
}
#Entity
public class Car implements PersonHoldable{}
#Entity
public class House implements PersonHoldable{}
public interface PersonHoldable{}
How can I map this correctly in JPA taking the following into consideration?
I tried #MappedSuperclass on an abstract implementation of PersonHoldable. Although it will work for this particular setup, the problem with this is that Car and House in reality implement more interfaces. And they are mapped to other entities as well.
The Person could have a property for every possible PersonHoldable, so in this case it could have a getCar() and getHouse() property. That does not seem very flexible to me. If I would add a Bike implementation of the PersonHoldable I would have to change my Person class.
I can map the other way around, so having a OneToOne relation only on the PersonHoldable implementation side. This would mean adding a getPerson() property to the PersonHoldable. But then it's not very easy from a Person perspective to see what PersonHoldable it is linked to.
I'm using default JPA, so no Hibernate specific tags if possible.
If this is not possible with default JPA, what would be best practice in this case?
A slight variation on your second point would be to make Person have an inheritance type and implement a CarPerson and HousePerson (and later a BikePerson) whose whole purpose is to define the specific join relationship to a specific PersonHolder implementation. That keeps the relationship intact and more easily queryable from the Person side.
#Inheritance(strategy = JOINED)
#DiscriminatorColumn(name="holdableType", discriminatorType=CHAR, length=1)
#Entity
public class Person {
// common fields
}
#Entity
#DiscriminatorValue("C")
public class CarPerson extends Person {
#OneToOne
private Car car;
}
#Entity
#DiscriminatorValue("H")
public class HousePerson extends Person {
#OneToOne
private House house;
}

Hibernate (JPA) inheritance mapping of abstract super classes

My data model represents legal entities, such as a Business or a Person. Both are tax-paying entities, and both have a TaxID, a collection of phone numbers, and a collection of mailing addresses.
I have a Java model with two concrete classes that extend an abstract class. The abstract class has properties and collections that are common to both concrete classes.
AbstractLegalEntity ConcreteBusinessEntity ConcretePersonEntity
------------------- ---------------------- --------------------
Set<Phone> phones String name String first
Set<Address> addresses BusinessType type String last
String taxId String middle
Address Phone
------- -----
AbsractLegalEntity owner AbstractLegalEntity owner
String street1 String number
String street2
String city
String state
String zip
I'm using Hibernate JPA Annotations on a MySQL database, with classes like this:
#MappedSuperclass
public abstract class AbstractLegalEntity {
private Long id; // Getter annotated with #Id #Generated
private Set<Phone> phones = new HashSet<Phone>(); // #OneToMany
private Set<Address> address = new HashSet<Address>(); // #OneToMany
private String taxId;
}
#Entity
public class ConcretePersonEntity extends AbstractLegalEntity {
private String first;
private String last;
private String middle;
}
#Entity
public class Phone {
private AbstractLegalEntity owner; // Getter annotated #ManyToOne #JoinColumn
private Long id;
private String number;
}
The problem is that Phone and Address objects need to refer to their owner, which is an AbstractLegalEntity. Hibernate complains:
#OneToOne or #ManyToOne on Phone references an unknown
entity: AbstractLegalEntity
It seems like this would be a fairly common Java inheritance scenario, so I hope that Hibernate would support it. I've tried changing the mapping for AbstractLegalEntity based on a Hibernate forum question, no longer using #MappedSuperclass:
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
However, now I get the following error. When reading up on this inheritance mapping type, it looks like I have to use SEQUENCE not IDENTITY, and MySQL doesn't support SEQUENCE.
Cannot use identity column key generation with <union-subclass>
mapping for: ConcreteBusinessEntity
I'm making more progress toward getting things working when I use the following mapping.
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="entitytype",
discriminatorType=DiscriminatorType.STRING
)
I'm thinking I should continue down this path. My concern is that I'm mapping it as an #Entity when I really don't ever want an instance of AbstractLegalEntity to ever exist. I'd like to know if this is the right approach. What is the correct approach I should be taking for this situation?
Use:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
AbstractLegalEntity
In the database you will have one table for AbstractLegalEntity, and tables for classes, which extend AbstractLegalEntity class. You won't have instances of AbstractLegalEntity if it's abstract. Polymorphism can be used here.
When you use:
#MappedSuperclass
AbstractLegalEntity
#Entity
ConcretePersonEntity extends AbstractLegalEntity
This will create only one table in your database called ConcretePersonEntity, containing columns from both classes.
Add #Entity annotation to AbstractLegalEntity. Instance of AbstractLegalEntity will never exist - hibernate will load appropriate extending instances - ConcreteBusinessEntity or ConcretePersonEntity according to Id field.
You have to declare AbstracLegalEntity as an #Entity. Even with the #Entity annotation, your class remains abstract. consequently, you will only have instance of concrete subclasses.

Categories