JPA Inheritance issue
hibernate, inheritance, java, jpa
Solution
The problem is not caused by `@JoinColumn`. The reason is Lazy Loading. I manage to pinpoint your problem in simpler example. Forgive me for changing convention from Father to Parent.
In the example below, uninitialized Element is type of `jpa.inheritance.issue.Parent_$$_javassist_1`. It is a Hibernate Proxy - dynamically created subclass of Parent. You can "unproxy" it by invoking Hibernate proprietary API `getHibernateLazyInitializer().getImplementation()`.
Collection of `elementCollection` is also Lazy Initialized. The type of the collection is `org.hibernate.collection.PersistentBag` which is being initilized with correct data at the time of first access. Collection is initialized all at once. Please see the test which successfully passed green with your exact version of Hibernate (3.3.0.SP1/3.4.0.GA).
@Test
public void test() {
Child c = new Child();
em.persist(c);
Another a = new Another();
a.setElement(c);
Collection<Parent> col = new ArrayList<Parent>();
col.add(c);
a.setElementCollection(col);
em.persist(a);
c.setAnother(a);
long idx = a.getId();
tx.commit();
// I'm cleaning the cache to be sure that call to a.getElement() will return proxy.
em.clear();
tx = em.getTransaction();
tx.begin();
a = em.find(Another.class, idx);
Assert.assertNotNull(a);
Parent p = a.getElement();
// At this point p is a type of jpa.inheritance.issue.Parent_$$_javassist_1
Assert.assertTrue(p instanceof Parent);
Assert.assertFalse(p instanceof Child);
// At this point a.elements is a not initialized (empty) collection of type org.hibernate.collection.PersistentBag
// When we access this collection for the first time, records are read from the database
Assert.assertEquals(1, a.getElementCollection().size());
if (p instanceof HibernateProxy) {
p =
(Parent) ((HibernateProxy) p).getHibernateLazyInitializer()
.getImplementation();
}
// At this point p is a type of jpa.inheritance.issue.Child
Assert.assertTrue(p instanceof Child);
}
@Entity
public class Another {
@JoinColumn(name = "element_id", referencedColumnName = "id", nullable = false)
@ManyToOne(fetch=FetchType.LAZY)
private Parent element;
public Parent getElement() {
return element;
}
public void setElement(Parent element) {
this.element = element;
}
@OneToMany(cascade = CascadeType.ALL, mappedBy = "another", fetch = FetchType.LAZY)
public Collection<Parent> elements;
public Collection<Parent> getElementCollection() {
return elements;
}
public void setElementCollection(Collection<Parent> elementCollection) {
this.elements = elementCollection;
}
// @Id ...
}
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Parent {
@ManyToOne
private Another another;
public Another getAnother() {
return another;
}
public void setAnother(Another another) {
this.another = another;
}
// @Id ...
}
@Entity
public class Child extends Parent {
}
You don't need `@DiscriminatorColumn` nor `@DiscriminatorValue` because those annotations are needed with `InheritanceType.SINGLE_TABLE` as an only resort to determine the type. With `InheritanceType.JOINED` Hibernate is able to determine polymorphic type by checking if there is a record in both (Parent and Child) tables with the same Id. You can turn on hibernate logging to see how the query to determine the type looks like. It works like this:
select
another0_.id as id0_1_,
another0_.element_id as element2_0_1_,
parent1_.id as id1_0_,
parent1_1_.name as name2_0_,
case
when parent1_1_.id is not null then 1
when parent1_.id is not null then 0
else -1
end as clazz_0_
from
Another another0_
inner join
Parent parent1_
on another0_.element_id=parent1_.id
left outer join
Child parent1_1_
on parent1_.id=parent1_1_.id
where
another0_.id=?
Problem
Working with JPA 1 (hibernate-core version 3.3.0.SP1 and hibernate-entitymanager version 3.4.0.GA) : I've some entities similar to those defined below, where ChildOne and ChildTwo extends from the Father entity. ``` @Entity @Table(name = "TABLE_FATHER") @Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorColumn(discriminatorType = DiscriminatorType.INTEGER, name = Father.C_ID_CTG) public class Father { @Id @GeneratedValue(strategy = GenerationType.AUTO, generator = "sq") @Column(name = "ID_PK", nullable = false) @BusinessId private Long id; ... } @Entity @Table(name = "TABLE_CHILD_ONE") @Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorValue(Categories.ID_CTG_ONE) public class ChildOne extends Father { ... } @Entity @Table(name = "TABLE_CHILD_TWO") @Inheritance(strategy = InheritanceType.JOINED) @DiscriminatorValue(Categories.ID_CTG_TWO) public class ChildTwo extends Element { ... } ``` Let's say I've one entity having a Father element, and another having a collection of father elements. In both cases, should go the children entities. ``` @Entity @Table(name = "TABLE_ONE") public class OneTable { @JoinColumn(name = "ID_PK", referencedColumnName = "ID_PK", nullable = false) @ManyToOne(optional = false, fetch = FetchType.LAZY) private Father element; ... } @Entity @Table(name = "TABLE_ANOTHER") public class Another { @Fetch(FetchMode.JOIN) @OneToMany(cascade = CascadeType.ALL, mappedBy = "id", fetch = FetchType.LAZY) private Collection<Father> elementCollection; ... } ``` I'm expecting to obtain always the children elements but when I get the element `getElement()` returns the father element and, on the other hand, when I get the collection `getElementCollection()` the children elements are coming. Apparently, the `@JoinColumn` is the cause of this behaviour, doing the join with the father table and forgetting the children elements. The collection is working as expected. How could I get the children element with a `getElement()` call? Any ideas or workarround? Thanks in advance.