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;
}
Related
I’m trying to map the inheritance from the superclass LendingLine and the subclasses Line and BlockLine. LendingLine has an ManyToOne association with Lending.
When I try to get the LendingLines from the database without the inheritance it works fine. The association works also. But when i add the inheritance, lendingLines in Lending is empty. I also can't get any LendingLines from the DB with the inheritance.
Can anybody help me?
(Sorry for the bad explanation)
Thanks in advance!
LendingLine:
#Entity
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="TYPE")
#DiscriminatorValue(value="Line")
#Table(name = "LendingLine")
public class LendingLine {
...
public LendingLine(){}
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER, targetEntity=Lending.class)
#JoinColumn(name = "LendingId")
private Lending lending;
...
Lending:
#Entity
#Table(name = "Lending")
public class Lending {
...
public Lending(){}
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER, mappedBy = "lending")
private List<LendingLine> lendingLines;
...
BlockDate:
#Entity
#DiscriminatorValue(value = "BlockLine")
public class BlockLine extends LendingLine {
public BlockLine(){
}
}
LendingLineRepository:
This class only reads from the db because the db was created by another application ( C#) where the objects are added to the db.
public class LendingLineRepository extends JpaUtil implement LendingLineRepositoryInterface {
#Override
protected Class getEntity() {
return LendingLine.class;
}
#Override
public Collection<LendingLine> findAll() {
Query query = getEm().createQuery("SELECT l FROM LendingLine l");
System.out.println(query.getResultList().size());
return (Collection<LendingLine>) query.getResultList();
}
Table LendingLine:
Choose your type of superclass according to your needs:
Concrete Class
public class SomeClass {}
Define your superclass as a concrete class, when you want to query it and when you use a new operator for further logic. You will always be able to persist it directly. In the discriminator column this entity has it's own name. When querying it, it returns just instances of itself and no subclasses.
Abstract Class
public abstract class SomeClass {}
Define your superclass as an abstract class when you want to query it, but don't actually use a new operator, because all logic handled is done by it's subclasses. Those classes are usually persisted by its subclasses but can still be persisted directly. U can predefine abstract methods which any subclass will have to implement (almost like an interface). In the discriminator column this entity won't have a name. When querying it, it returns itself with all subclasses, but without the additional defined information of those.
MappedSuperclass
#MappedSuperclass
public abstract class SomeClass {}
A superclass with the interface #MappedSuperclass cannot be queried. It provides predefined logic to all it's subclasses. This acts just like an interface. You won't be able to persist a mapped superclass.
For further information: JavaEE 7 - Entity Inheritance Tutorial
Original message
Your SuperClass LendingLine needs to define a #DiscriminatorValue as well, since it can be instantiated and u use an existing db-sheme, where this should be defined.
I have currently these 2 entities:
Titular and Familiar (Family/kin/relative).
Familiar [0..*]<------>[1] Titular
These 2 classes has commons attributes of a person (firstName, lastName, birthDate....) and they are 2 kind of Affiliate
I want to unify those in one super class (generalization) Person but what I can't figure out is I should make Titular and Familiar extend Person or add Person as attribute of them.
Person also has to exists by itself (cannot be abstract) and not all Persons are Affiliates
BUT! I also need a way to establish/handle commons behaviors for Titular and Familiar.
Person [1]<------>[0..*] Titular
Person [1]<------>[0..*] Familiar
Titular [1]<------>[0..*] Familiar
So the doubt is:
public class Titular extend Person
public class Familiar extend Person {
or
public class Titular implement Affiliate {
private Person person;
public class Familiar implement Affiliate {
private Titular t;
private Person person;
or (3rd thought)
public class Person {
public abstract class Affiliate {
protected Person person;
public class Titular extends Affiliate {
The way I see it, all Affiliates are persons too (or you have exceptions to this?)! So the right hierarchy is:
Person --- Affiliate --- Titular
\- Familiar
Now, as to inheriting or having a pointer... that is called composition v/s inheritance and there are good arguments for both. The main reasons for chosing composition are
Mutable or optional relationships: Say, the owner of a car can change or a car can have no owner. A cat can not stop to be an animal and be something else.
Different Public API: While more work, composition allows you to manually forward whatever API you want to expose from your internal pointer, hiding or changing things from the "parent" as you will.
In general, you'll find inheritance makes more sense when ClassA IS-A ClassB, so you would not expect that to change nor would you want both classes to present a different API. You'll see this recommendation everywhere and it seems to fit your example like a glove.
After a few test, I had decided for Composition over inheritance.
As one of tag is "JPA", this solution has to be mapped/annotated
..and none of the possible annotations suits
#Inheritance(strategy = InheritanceType.JOINED/SINGLE_TABLE/TABLE_PER_CLASS)
If Titular and Familiar extends from Person, the ORM required a "DTYPE" column in Person, which is useless for me because no matters in how many Titular or Familiar a Person can/will become in its life, it just must be ONE register of Person.
And being an Affiliate is a "concept" or behavior, which I need it to do some polymorphic tasks, it doesn't has any persistable attribute (FOR NOW!), I will make an Interface of it.
#Entity
public class Persona {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Entity
public class Titular implements Afiliado {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JoinColumn(nullable = false)
#ManyToOne(optional = false)
private Persona persona;
#Entity
public class Familiar implements Afiliado {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JoinColumn(nullable = false)
#ManyToOne(optional = false)
private Persona persona;
#ManyToOne
private Titular titular;
public interface Afiliado {
public String getNumero();
//trick but necessary
public Persona getPersona();
//another default Java 8 implementations..
}
Stated in verbose, we want the following behavior (If I understand you correctly):
Titular is a Person who supports the functionality of an Affiliate.
Familiar is a Person who supports the functionality of an Affiliate and is associated with a Titular.
After reading these two lines enough number of times, one possible solution that seems reasonable is:
public class Titular extends Person implements Affiliate {
public class Familiar extends Person implements Affiliate {
private Titular t;
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.
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...
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Problem {
#ManyToOne
private Person person;
}
#Entity
#DiscriminatorValue("UP")
public class UglyProblem extends Problem {}
#Entity
public class Person {
#OneToMany(mappedBy="person")
private List< UglyProblem > problems;
}
I think it is pretty clear what I am trying to do. I expect #ManyToOne person to be inherited by UglyProblem class. But there will be an exception saying something like: "There is no such property found in UglyProblem class (mappedBy="person")".
All I found is this. I was not able to find the post by Emmanuel Bernard explaining reasons behind this.
Unfortunately, according to the Hibernate documentation "Properties from superclasses not mapped as #MappedSuperclass are ignored."
Well I think this means that if I have these two classes:
public class A {
private int foo;
}
#Entity
public class B extens A {
}
then field foo will not be mapped for class B. Which makes sense. But if I have something like this:
#Entity
public class Problem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Entity
public class UglyProblem extends Problem {
private int levelOfUgliness;
public int getLevelOfUgliness() {
return levelOfUgliness;
}
public void setLevelOfUgliness(int levelOfUgliness) {
this.levelOfUgliness = levelOfUgliness;
}
}
I expect the class UglyProblem to have fileds id and name and both classes to be mapped using same table. (In fact, this is exactly what happens, I have just checked again). I have got this table:
CREATE TABLE "problem" (
"DTYPE" varchar(31) NOT NULL,
"id" bigint(20) NOT NULL auto_increment,
"name" varchar(255) default NULL,
"levelOfUgliness" int(11) default NULL,
PRIMARY KEY ("id")
) AUTO_INCREMENT=2;
Going back to my question:
I expect #ManyToOne person to be inherited by UglyProblem class.
I expect that because all other mapped fields are inherited and I do not see any reason to make this exception for ManyToOne relationships.
Yeah, I saw that. In fact, I used Read-Only solution for my case. But my question was "Why..." :). I know that there is an explanation given by a member of hibernate team. I was not able to find it and that is why I asked.
I want to find out the motivation of this design decision.
(if you interested how I have faced this problem: I inherited a project built using hibernate 3. It was Jboss 4.0.something + hibernate was already there (you'd download it all together). I was moving this project to Jboss 4.2.2 and I found out that there are inherited mappings of "#OneToMany mappedBy" and it worked fine on old setup...)
In my case I wanted to use the SINGLE_TABLE inheritance type, so using #MappedSuperclass wasn't an option.
What works, although not very clean, is to add the Hibernate proprietary #Where clause to the #OneToMany association to force the type in queries:
#OneToMany(mappedBy="person")
#Where(clause="DTYPE='UP'")
private List< UglyProblem > problems;
I think it's a wise decision made by the Hibernate team. They could be less arrogante and make it clear why it was implemented this way, but that's just how Emmanuel, Chris and Gavin works. :)
Let's try to understand the problem. I think your concepts are "lying". First you say that many Problems are associated to People. But, then you say that one Person have many UglyProblems (and does not relate to other Problems). Something is wrong with that design.
Imagine how it's going to be mapped to the database. You have a single table inheritance, so:
_____________
|__PROBLEMS__| |__PEOPLE__|
|id <PK> | | |
|person <FK> | -------->| |
|problemType | |_________ |
--------------
How is hibernate going to enforce the database to make Problem only relate to People if its problemType is equal UP? That's a very difficult problem to solve. So, if you want this kind of relation, every subclass must be in it's own table. That's what #MappedSuperclass does.
PS.: Sorry for the ugly drawing :D
Unfortunately, according to the Hibernate documentation "Properties from superclasses not mapped as #MappedSuperclass are ignored." I ran up against this too. My solution was to represent the desired inheritance through interfaces rather than the entity beans themselves.
In your case, you could define the following:
public interface Problem {
public Person getPerson();
}
public interface UglyProblem extends Problem {
}
Then implement these interfaces using an abstract superclass and two entity subclasses:
#MappedSuperclass
public abstract class AbstractProblemImpl implements Problem {
#ManyToOne
private Person person;
public Person getPerson() {
return person;
}
}
#Entity
public class ProblemImpl extends AbstractProblemImpl implements Problem {
}
#Entity
public class UglyProblemImpl extends AbstractProblemImpl implements UglyProblem {
}
As an added benefit, if you code using the interfaces rather than the actual entity beans that implement those interfaces, it makes it easier to change the underlying mappings later on (less risk of breaking compatibility).
I think you need to annotate your Problem super-class with #MappedSuperclass instead of #Entity.
I figured out how to do the OneToMany mappedBy problem.
In the derived class UglyProblem from the original post. The callback method needs to be in the derived class not the parent class.
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#ForceDiscriminator
public class Problem {
}
#Entity
#DiscriminatorValue("UP")
public class UglyProblem extends Problem {
#ManyToOne
private Person person;
}
#Entity
public class Person {
#OneToMany(mappedBy="person")
private List< UglyProblem > problems;
}
Found the secret sauce for using Hibernate at least. http://docs.jboss.org/hibernate/stable/annotations/api/org/hibernate/annotations/ForceDiscriminator.html The #ForceDiscriminator makes the #OneToMany honor the discriminator
Requires Hibernate Annotations.
In my opinion #JoinColumn should at least provide an option to apply the #DiscriminatorColumn = #DiscriminatorValue to the SQL "where" clause, although I would prefer this behaviour to be a default one.
I am very surprised that in the year 2020 this is still an issue.
Since this object design pattern is not so rare, I think it is a disgrace for JPA not yet covering this simple feature in the specs, thus still forcing us to search for ugly workarounds.
Why must this be so difficult? It is just an additional where clause and yes, I do have a db index prepared for #JoinColumn, #DiscriminatorColumn combo.
.i.. JPA
Introduce your own custom annotations and write code that generates native queries. It will be a good exercise.