JPA Inheritance Problem

Working with JPA 1 (version hibernate-core 3.3.0.SP1 and hibernate-entitymanager version 3.4.0.GA): I have some entities similar to those defined below, where ChildOne and ChildTwo extend from the Father object.

@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 have one entity that has an element of the Father, and the other is a collection of elements of the father. In both cases, child objects should appear.

 @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 expect to always get children, but when I get the getElement() element, it returns the father element and, on the other hand, when I get the getElementCollection() collection, the children appear.

Apparently, the reason for this behavior is @JoinColumn , making a join to the paternal table and forgetting the children elements. The collection works as expected.

How can I get the children element with getElement() call? Any ideas or desktop? Thanks in advance.

+6
source share
1 answer

The problem is not caused by @JoinColumn . The reason is that Lazy Loading. I am able to pinpoint your problem in a simpler example. Forgive me for changing the agreement from Father to Parent.

In the example below, the uninitialized element is of type jpa.inheritance.issue.Parent_$$_javassist_1 . This is Hibernate Proxy - a dynamically created subclass of Parent. You can disable it by calling the Hibernate getHibernateLazyInitializer().getImplementation() .

The elementCollection also lazy. A type of the org.hibernate.collection.PersistentBag collection that is initialized with the correct data during the first access. The collection is initialized immediately. See Test that 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 do not need @DiscriminatorColumn and @DiscriminatorValue because these annotations are needed with InheritanceType.SINGLE_TABLE as the only way to determine the type. Using InheritanceType.JOINED Hibernate can determine the polymorphic type by checking whether there is a record in both (parent and child) tables with the same identifier. You can enable hibernation logging to see what the request for type determination looks like. It works as follows:

 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=? 
+2
source

Source: https://habr.com/ru/post/971706/


All Articles