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.
JPA supports the following inheritance models: - SINGLE_TABLE – One table per class hierarchy. In terms of performance and easy implementation, this is the best strategy. The downside is that all properties from the subclass must be nullable.
JPA Inheritence Overview Inheritence is a key feature of object-oriented programming language in which a child class can acquire the properties of its parent class. This feature enhances reusability of the code. The relational database doesn't support the mechanism of inheritance.
There are three inheritance strategies defined from the InheritanceType enum, SINGLE_TABLE , TABLE_PER_CLASS and JOINED . Single table inheritance is the default, and table per class is an optional feature of the JPA spec, so not all providers may support it.
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=?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With