Trying to figure out Hibernate's "table per subclass" inheritance strategy (5.2.2).

Classes with mapping (getters and setters are not listed for compactness):

//Класс, содержащий поля, общие для всех классов @MappedSuperclass public class DBObject { @Id @GeneratedValue(strategy = GenerationType.AUTO) protected Integer id; @Column(name = "correctdt") @Temporal(TemporalType.TIMESTAMP) protected Date correctDate; @Override public boolean equals(Object o) { boolean result = false; if (o != null && (o instanceof DBObject)) { DBObject oo = (DBObject) o; if (id != null) { result = id.equals(oo.id); } } return result; } @Override public int hashCode() { return (id != null) ? id.hashCode() : 0; } } //Класс, содержащий коллекцию элементов с наследованием. @Entity @Inheritance(strategy = InheritanceType.JOINED) @Table(name = "tmp_test_class") public class TestClass extends DBObject implements Serializable{ @OneToMany(mappedBy = "testClass") protected Collection<TestParent> tests = new ArrayList<>(); } //Родительский класс @Entity @Table(name = "tmp_test_parent") @Inheritance(strategy = InheritanceType.JOINED) public class TestParent extends DBObject implements Serializable{ @Column private String name; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "test_id") protected TestClass testClass; } //Подкласс @Entity @Table(name = "tmp_test_child1") public class TestChild extends TestParent{ @Column private String field1; } 

The tests collection from the TestClass class should only be loaded from the database when it is accessed (Lazy loading).

But when trying to access a collection, an incorrect SQL query with a Cartesian product is formed, in which the tables tmp_test_parent and tmp_test_class are not related:

 SELECT tests0_.test_id AS test_id4_2_0_, tests0_.id AS id1_2_0_, tests0_.id AS id1_2_1_, tests0_.correctdt AS correctdt2_2_1_, tests0_.name AS name3_2_1_, tests0_.test_id AS test_id4_2_1_, tests0_1_.field1 AS field1_0_1_, CASE WHEN tests0_1_.id IS NOT NULL THEN 1 WHEN tests0_.id IS NOT NULL THEN 0 END AS clazz_1_ FROM tmp_test_parent tests0_, tmp_test_child1 tests0_1_ WHERE tests0_.test_id=? 

In fact, there should be a LEFT OUTER JOIN.

If the collection set FetchType = EAGER, then everything works as it should. But in the absolute majority of cases, I don’t need to immediately ship this collection.

 //Класс для тестирования public class MainClass { public static void main(String[] args) throws Exception { Session session = HibernateSessionFactory.getSessionFactory().openSession(); //Грузим класс с коллекцией TestClass test = session.get(TestClass.class, 1); Collection<TestParent> tests = test.getTests(); System.out.println(tests.size()); //Из-за декартова произведения результат неверен //Грузим те же объекты напрямую Collection<TestParent> tests2 = session.createCriteria(TestParent.class) .add(Restrictions.eq("testClass.id", 1)) .list(); System.out.println(tests2.size()); //А тут всё верно } } 

Here is what is stored in the database: tmp_test_parent table (3 entries)

 "ID" "NAME" "TEST_ID" "CORRECTDT" 1 "11" 1 14.09.16 12:31:40 2 "22" 1 21.09.16 12:31:46 3 "33" 1 21.09.16 12:31:51 

Table tmp_test_child (2 entries)

 "ID" "FIELD1" "CORRECTDT" 1 111 21.09.16 12:32:26 3 333 21.09.16 12:32:28 

That's what happened after the download:

 TestClass{ tests=[ TestChild{ name=11, field1=111 }, TestChild{ name=22, field1=111 }, TestChild{ name=33, field1=111 }, TestChild{ name=11, field1=111 }, TestChild{ name=22, field1=111 }, TestChild{ name=33, field1=111 } ] }; 

I expect 3 objects in the collection, not 6.

No one with a similar problem met? What can this happen?

  • Indicate what is not correct when calling TestClass.getTests() , what result and what was expected, make toString() all objects, see how correct they are in your case. - MrFylypenko
  • @MrFylypenko Added the information above. - Andrey1990
  • You get the same elements, I don’t know how correct it is, but instead of Collection <TestParent> it’s better to use Set <TestParent> to avoid duplicate elements. You got the TestChild collection from TestClass, without TestParent, should it be so? - MrFylypenko
  • Changing types does not solve the problem. Identical elements are a consequence of an incorrect sql query that Hibernate generates. There should be a query in which the table for TestChild joins the table for TestParent with the help of LEFT OUTER JOIN , and here we get a Cartesian product (each row of one table with each row of another table). Where there is no inheritance - there are no problems. In another place today they said that everything works great with the same entities. I do not know what to think anymore. I will try as an experiment in this test project to replace Oracle with MySQL. - Andrey1990

1 answer 1

Experimentally, it turned out that the problem exists only on the Oracle DBMS. On MySQL, Hibernate generates a valid sql query:

 SELECT tests0_.test_id AS test_id4_2_0_, tests0_.id AS id1_2_0_, tests0_.id AS id1_2_1_, tests0_.correctdt AS correctd2_2_1_, tests0_.name AS name3_2_1_, tests0_.test_id AS test_id4_2_1_, tests0_1_.field1 AS field1_0_1_, CASE WHEN tests0_1_.id IS NOT NULL THEN 1 WHEN tests0_.id IS NOT NULL THEN 0 END AS clazz_1_ FROM tmp_test_parent tests0_ LEFT OUTER JOIN tmp_test_child1 tests0_1_ ON tests0_.id =tests0_1_.id WHERE tests0_.test_id=?