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?
TestClass.getTests(), what result and what was expected, maketoString()all objects, see how correct they are in your case. - MrFylypenkoTestChildjoins the table forTestParentwith the help ofLEFT 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