[JIRA] (HHH-16569) Batch fetch leads to some collections to be wrongly initialized with empty due to wrong generated SQL
by Jones (JIRA)
Jones ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *updated* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiZDZjMzAzM2Jk... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16569?atlOrigin=eyJpIjoiZDZjMz... ) HHH-16569 ( https://hibernate.atlassian.net/browse/HHH-16569?atlOrigin=eyJpIjoiZDZjMz... ) Batch fetch leads to some collections to be wrongly initialized with empty due to wrong generated SQL ( https://hibernate.atlassian.net/browse/HHH-16569?atlOrigin=eyJpIjoiZDZjMz... )
Change By: Jones ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... )
I tried to create a minimal entity mapping to reproduce this issue. Changing any {{FetchMode}} configuration in the mapping would "avoid" the issue. Also disabling batch fetch would "avoid" the issue.
This is the minimal mapping scenario that I was able to create from my real case:
{code:java}@Entity
@Table(name = "ENTITY_A")
public class EntityA {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
Integer id;
@JoinColumn(name = "ENTITY_B")
@ManyToOne
EntityB entityB;
}
@Entity
@Table(name = "ENTITY_B")
public class EntityB {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
Integer id;
@OneToMany(mappedBy = "entityB")
@Fetch(FetchMode.SUBSELECT)
List<EntityC> listOfEntitiesC = new ArrayList<>();
@JoinColumn(name = "ENTITY_D")
@ManyToOne
EntityD entityD;
}
@Entity
@Table(name = "ENTITY_C")
public class EntityC {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
Integer id;
@JoinColumn(name = "ENTITY_B")
@ManyToOne
EntityB entityB;
}
@Entity
@Table(name = "ENTITY_D")
public class EntityD {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
Integer id;
@JoinColumn(name = "ENTITY_E")
@ManyToOne
@Fetch(FetchMode.SELECT)
EntityE entityE;
@JoinColumn(name = "OPENING_B")
@ManyToOne
EntityB openingB;
@JoinColumn(name = "CLOSING_B")
@ManyToOne
EntityB closingB;
@OneToMany(mappedBy = "entityD")
List<EntityB> listOfEntitiesB = new ArrayList<>();
}
@Entity
@Table(name = "ENTITY_E")
public class EntityE {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID")
Integer id;
@JoinColumn(name = "ENTITY_D")
@ManyToOne
EntityD entityD;
}{code}
{code:java} @Override
protected void configure(Configuration configuration) {
super.configure(configuration);
configuration.setProperty(AvailableSettings.SHOW_SQL, Boolean.TRUE.toString());
configuration.setProperty(AvailableSettings.FORMAT_SQL, Boolean.TRUE.toString());
configuration.setProperty(AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "10");
}
@Test
public void hhhXXXXTest() throws Exception {
try (Session s = openSession()) {
Transaction tx = s.beginTransaction();
EntityA entityA = new EntityA();
EntityB entityB = new EntityB();
EntityC entityC = new EntityC();
EntityD entityD = new EntityD();
EntityE entityE = new EntityE();
entityA.entityB = entityB;
entityB.entityD = entityD;
entityB.listOfEntitiesC.add(entityC);
entityC.entityB = entityB;
entityD.entityE = entityE;
entityD.listOfEntitiesB.add(entityB);
entityD.openingB = entityB;
entityE.entityD = entityD;
s.persist(entityA);
s.persist(entityB);
s.persist(entityC);
s.persist(entityD);
s.persist(entityE);
tx.commit();
}
try (Session s = openSession()) {
List<EntityE> entitiesE = s.createQuery("select e from EntityE e", EntityE.class).getResultList();
assertThat(entitiesE).hasSize(1);
List<EntityA> entitiesA = s.createQuery("select a from EntityA a", EntityA.class).getResultList();
assertThat(entitiesA).hasSize(1);
assertThat(entitiesA.get(0).entityB.listOfEntitiesC).hasSize(1);
}
}{code}
{noformat}java.lang.AssertionError:
Expected size:<1> but was:<0> in:
<[]>
at org.hibernate.bugs.ORMUnitTestCase.hhhXXXXTest(ORMUnitTestCase.java:97)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.hibernate.testing.junit4.ExtendedFrameworkMethod.invokeExplosively(ExtendedFrameworkMethod.java:45)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:299)
at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:293)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.lang.Thread.run(Thread.java:833){noformat}
The generated query to load {{EntityE.listOfEntitiesC}} is wrong since {{c1_0.ID}} is the wrong path to be used
{code:sql}Hibernate:
select
l1_0.ENTITY_B,
l1_0.ID
from
ENTITY_C l1_0
where
l1_0.ENTITY_B in(select
c1_0.ID
from
ENTITY_D e1_0
left join
ENTITY_B c1_0
on c1_0.ID=e1_0.CLOSING_B
left join
ENTITY_D e2_0
on e2_0.ID=c1_0.ENTITY_D
left join
ENTITY_B o1_0
on o1_0.ID=e2_0.OPENING_B
left join
ENTITY_B o2_0
on o2_0.ID=e1_0.OPENING_B
left join
ENTITY_D e6_0
on e6_0.ID=o2_0.ENTITY_D
left join
ENTITY_B c3_0
on c3_0.ID=e6_0.CLOSING_B
where
array_contains(?,e1_0.ID)){code}
A failing test scenario is to be attached and also available at [https://github.com/ratoaq2/HHH-16569|https://github.com/ratoaq2/HHH-16569...]
( https://hibernate.atlassian.net/browse/HHH-16569#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16569#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100225- sha1:5ff5564 )
2 years, 11 months
[JIRA] (HHH-16569) Batch fetch leads to some collections to be wrongly initialized with empty due to wrong generated SQL
by Jones (JIRA)
Jones ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... ) *created* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiNDM5OWYzOGNh... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16569?atlOrigin=eyJpIjoiNDM5OW... ) HHH-16569 ( https://hibernate.atlassian.net/browse/HHH-16569?atlOrigin=eyJpIjoiNDM5OW... ) Batch fetch leads to some collections to be wrongly initialized with empty due to wrong generated SQL ( https://hibernate.atlassian.net/browse/HHH-16569?atlOrigin=eyJpIjoiNDM5OW... )
Issue Type: Bug Affects Versions: 6.2.2 Assignee: Unassigned Created: 08/May/2023 02:53 AM Priority: Critical Reporter: Jones ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=557058%... )
I tried to create a minimal entity mapping to reproduce this issue. Changing any FetchMode configuration in the mapping would "avoid" the issue. Also disabling batch fetch would "avoid" the issue.
This is the minimal mapping scenario that I was able to create from my real case:
@Entity
@Table(name = "ENTITY_A" )
public class EntityA {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID" )
Integer id;
@JoinColumn(name = "ENTITY_B" )
@ManyToOne
EntityB entityB;
}
@Entity
@Table(name = "ENTITY_B" )
public class EntityB {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID" )
Integer id;
@OneToMany(mappedBy = "entityB" )
@Fetch(FetchMode.SUBSELECT)
List<EntityC> listOfEntitiesC = new ArrayList<>();
@JoinColumn(name = "ENTITY_D" )
@ManyToOne
EntityD entityD;
}
@Entity
@Table(name = "ENTITY_C" )
public class EntityC {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID" )
Integer id;
@JoinColumn(name = "ENTITY_B" )
@ManyToOne
EntityB entityB;
}
@Entity
@Table(name = "ENTITY_D" )
public class EntityD {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID" )
Integer id;
@JoinColumn(name = "ENTITY_E" )
@ManyToOne
@Fetch(FetchMode.SELECT)
EntityE entityE;
@JoinColumn(name = "OPENING_B" )
@ManyToOne
EntityB openingB;
@JoinColumn(name = "CLOSING_B" )
@ManyToOne
EntityB closingB;
@OneToMany(mappedBy = "entityD" )
List<EntityB> listOfEntitiesB = new ArrayList<>();
}
@Entity
@Table(name = "ENTITY_E" )
public class EntityE {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "ID" )
Integer id;
@JoinColumn(name = "ENTITY_D" )
@ManyToOne
EntityD entityD;
}
@Override
protected void configure(Configuration configuration) {
super.configure(configuration);
configuration.setProperty(AvailableSettings.SHOW_SQL, Boolean.TRUE.toString());
configuration.setProperty(AvailableSettings.FORMAT_SQL, Boolean.TRUE.toString());
configuration.setProperty(AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, "10" );
}
@Test
public void hhhXXXXTest() throws Exception {
try (Session s = openSession()) {
Transaction tx = s.beginTransaction();
EntityA entityA = new EntityA();
EntityB entityB = new EntityB();
EntityC entityC = new EntityC();
EntityD entityD = new EntityD();
EntityE entityE = new EntityE();
entityA.entityB = entityB;
entityB.entityD = entityD;
entityB.listOfEntitiesC.add(entityC);
entityC.entityB = entityB;
entityD.entityE = entityE;
entityD.listOfEntitiesB.add(entityB);
entityD.openingB = entityB;
entityE.entityD = entityD;
s.persist(entityA);
s.persist(entityB);
s.persist(entityC);
s.persist(entityD);
s.persist(entityE);
tx.commit();
}
try (Session s = openSession()) {
List<EntityE> entitiesE = s.createQuery( "select e from EntityE e" , EntityE.class).getResultList();
assertThat(entitiesE).hasSize(1);
List<EntityA> entitiesA = s.createQuery( "select a from EntityA a" , EntityA.class).getResultList();
assertThat(entitiesA).hasSize(1);
assertThat(entitiesA.get(0).entityB.listOfEntitiesC).hasSize(1);
}
}
java.lang.AssertionError:
Expected size:<1> but was:<0> in:
<[]>
at org.hibernate.bugs.ORMUnitTestCase.hhhXXXXTest(ORMUnitTestCase.java:97)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.hibernate.testing.junit4.ExtendedFrameworkMethod.invokeExplosively(ExtendedFrameworkMethod.java:45)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:299)
at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:293)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.lang.Thread.run(Thread.java:833)
The generated query to load EntityE.listOfEntitiesC is wrong since c1_0.ID is the wrong path to be used
Hibernate:
select
l1_0.ENTITY_B,
l1_0.ID
from
ENTITY_C l1_0
where
l1_0.ENTITY_B in ( select
c1_0.ID
from
ENTITY_D e1_0
left join
ENTITY_B c1_0
on c1_0.ID=e1_0.CLOSING_B
left join
ENTITY_D e2_0
on e2_0.ID=c1_0.ENTITY_D
left join
ENTITY_B o1_0
on o1_0.ID=e2_0.OPENING_B
left join
ENTITY_B o2_0
on o2_0.ID=e1_0.OPENING_B
left join
ENTITY_D e6_0
on e6_0.ID=o2_0.ENTITY_D
left join
ENTITY_B c3_0
on c3_0.ID=e6_0.CLOSING_B
where
array_contains(?,e1_0.ID))
A failing test scenario is to be attached
( https://hibernate.atlassian.net/browse/HHH-16569#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16569#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100225- sha1:5ff5564 )
2 years, 11 months
[JIRA] (HHH-16568) Incorrect DDL with Postgres, @MappedSuperclass, Generics and UserType
by Christoph Laudenbach (JIRA)
Christoph Laudenbach ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=712020%... ) *created* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiZmZlOTc4MjU2... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16568?atlOrigin=eyJpIjoiZmZlOT... ) HHH-16568 ( https://hibernate.atlassian.net/browse/HHH-16568?atlOrigin=eyJpIjoiZmZlOT... ) Incorrect DDL with Postgres, @MappedSuperclass, Generics and UserType ( https://hibernate.atlassian.net/browse/HHH-16568?atlOrigin=eyJpIjoiZmZlOT... )
Issue Type: Bug Affects Versions: 6.1.7 Assignee: Unassigned Created: 08/May/2023 02:32 AM Environment: Hibernate 6.1.7, Java 17, Linux, Postgres Priority: Major Reporter: Christoph Laudenbach ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=712020%... )
This problem is similar to HHH-15970 ( https://hibernate.atlassian.net/browse/HHH-15970 ) Closed , but still exists with Hibernate 6.1.7.
It occurs when you have a generic @MappedSuperclass with an enum field that is mapped via a custom UserType. The databse field is generated as a bytea , where an integer type is expected. When saving an object, there is of course a type conflict.
Please have a look at the attached test case. With Hibernate 6.1.7 and Postgres it fails as follows:
Caused by: org.postgresql.util.PSQLException: ERROR: column "myenum" is of type bytea but expression is of type integer
Hint: You will need to rewrite or cast the expression.
Position: 93
at app//org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2676)
at app//org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2366)
at app//org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:356)
at app//org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:496)
at app//org.postgresql.jdbc.PgStatement.execute(PgStatement.java:413)
at app//org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:190)
at app//org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:152)
at app//org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:197)
With Hibernate 6.2, however, it is successful. I couldn't find a suitable JIRA issue for it though.
I would like to ask for a fix for 6.1. Because we are currently limited to JakartaEE 9.1, the use of Hibernate 6.2 is unfortunately not yet an option for us.
( https://hibernate.atlassian.net/browse/HHH-16568#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16568#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100225- sha1:5ff5564 )
2 years, 11 months