Skip to content

Commit ecd0acb

Browse files
dreab8mbladel
authored andcommitted
HHH-17704 Query using detached Proxy as parameter fails with LazyInitializationException
1 parent 9421b94 commit ecd0acb

File tree

2 files changed

+74
-24
lines changed

2 files changed

+74
-24
lines changed

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaType.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@
66
*/
77
package org.hibernate.type.descriptor.java.spi;
88

9-
import org.hibernate.Hibernate;
9+
import org.hibernate.proxy.LazyInitializer;
1010
import org.hibernate.type.descriptor.WrapperOptions;
1111
import org.hibernate.type.descriptor.java.AbstractClassJavaType;
1212
import org.hibernate.type.descriptor.java.IncomparableComparator;
1313
import org.hibernate.type.descriptor.java.MutabilityPlan;
1414
import org.hibernate.type.descriptor.jdbc.JdbcType;
1515
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
1616

17+
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
18+
1719
/**
1820
* Uses object identity for {@code equals}/{@code hashCode} as we ensure that internally.
1921
*
@@ -44,7 +46,15 @@ public boolean areEqual(T one, T another) {
4446

4547
@Override
4648
public boolean isInstance(Object value) {
47-
return getJavaTypeClass().isAssignableFrom( Hibernate.getClassLazy( value ) );
49+
final LazyInitializer lazyInitializer = extractLazyInitializer( value );
50+
final Class<T> javaTypeClass = getJavaTypeClass();
51+
if ( lazyInitializer != null ) {
52+
return javaTypeClass.isAssignableFrom( lazyInitializer.getPersistentClass() )
53+
|| javaTypeClass.isAssignableFrom( lazyInitializer.getImplementationClass() );
54+
}
55+
else {
56+
return javaTypeClass.isAssignableFrom( value.getClass() );
57+
}
4858
}
4959

5060
@Override

hibernate-core/src/test/java/org/hibernate/orm/test/proxy/ProxyAsQueryParameterTest.java

+62-22
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
package org.hibernate.orm.test.proxy;
88

9+
import java.util.List;
10+
911
import org.hibernate.Hibernate;
1012

1113
import org.hibernate.testing.orm.junit.DomainModel;
@@ -23,24 +25,34 @@
2325

2426
import static org.assertj.core.api.Assertions.assertThat;
2527

26-
@DomainModel( annotatedClasses = {
28+
@DomainModel(annotatedClasses = {
2729
ProxyAsQueryParameterTest.Product.class,
2830
ProxyAsQueryParameterTest.Vendor.class,
2931
ProxyAsQueryParameterTest.CarVendor.class,
32+
ProxyAsQueryParameterTest.LuxuryCarVendor.class,
3033
ProxyAsQueryParameterTest.Producer.class,
31-
} )
34+
})
3235
@SessionFactory
33-
@Jira( "https://hibernate.atlassian.net/browse/HHH-17467" )
36+
@Jira("https://hibernate.atlassian.net/browse/HHH-17467")
3437
public class ProxyAsQueryParameterTest {
38+
39+
private static final Integer PRODUCT_ID = 1;
40+
private static final Integer LUXURY_PRODUCT_ID = 2;
41+
3542
@BeforeAll
3643
public void setUp(SessionFactoryScope scope) {
3744
scope.inTransaction( session -> {
3845
final CarVendor vendor = new CarVendor( 1, "vendor_1", "dealership_1" );
3946
session.persist( vendor );
4047
final Producer producer = new Producer( 1, "producer_1" );
4148
session.persist( producer );
42-
final Product product = new Product( 1, vendor, producer );
49+
final Product product = new Product( PRODUCT_ID, vendor, producer );
4350
session.persist( product );
51+
52+
final LuxuryCarVendor luxuryCarVendor = new LuxuryCarVendor( 2, "vendor_2", "luxury" );
53+
session.persist( luxuryCarVendor );
54+
final Product luxuryProduct = new Product( LUXURY_PRODUCT_ID, luxuryCarVendor, producer );
55+
session.persist( luxuryProduct );
4456
} );
4557
}
4658

@@ -55,49 +67,66 @@ public void tearDown(SessionFactoryScope scope) {
5567
@Test
5668
public void testProxyParam(SessionFactoryScope scope) {
5769
scope.inTransaction( session -> {
58-
final Product product = session.createQuery( "from Product p", Product.class ).getSingleResult();
70+
final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class )
71+
.setParameter( "productId", PRODUCT_ID )
72+
.getSingleResult();
5973
assertThat( Hibernate.isInitialized( product.getProducer() ) ).isFalse();
60-
final Product result = session.createQuery(
74+
final List<Product> results = session.createQuery(
6175
"from Product p where p.producer = :producer",
6276
Product.class
63-
).setParameter( "producer", product.getProducer() ).getSingleResult();
64-
// The proxy should not have been initialized since Producer doesn't have subclasses
65-
assertThat( Hibernate.isInitialized( product.getProducer() ) ).isFalse();
66-
assertThat( result.getProducer().getId() ).isEqualTo( product.getProducer().getId() );
77+
).setParameter( "producer", product.getProducer() ).getResultList();
78+
assertThat( results.size() ).isEqualTo( 2 );
79+
assertThat( results.get( 0 ).getProducer().getId() ).isEqualTo( product.getProducer().getId() );
80+
assertThat( results.get( 1 ).getProducer().getId() ).isEqualTo( product.getProducer().getId() );
6781
} );
6882
}
6983

7084
@Test
7185
public void testProxyParamWithSubclasses(SessionFactoryScope scope) {
7286
scope.inTransaction( session -> {
73-
final Product product = session.createQuery( "from Product p", Product.class ).getSingleResult();
87+
final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class )
88+
.setParameter( "productId", PRODUCT_ID )
89+
.getSingleResult();
7490
assertThat( Hibernate.isInitialized( product.getVendor() ) ).isFalse();
7591
final Product result = session.createQuery(
7692
"from Product p where p.vendor = :vendor",
7793
Product.class
7894
).setParameter( "vendor", product.getVendor() ).getSingleResult();
79-
// The proxy will have been initialized since Vendor has subclasses
80-
assertThat( Hibernate.isInitialized( product.getVendor() ) ).isTrue();
8195
assertThat( result.getVendor().getId() ).isEqualTo( product.getVendor().getId() );
8296
} );
8397
}
8498

8599
@Test
86100
public void testSubclassProxyParam(SessionFactoryScope scope) {
87101
scope.inTransaction( session -> {
88-
final Product product = session.createQuery( "from Product p", Product.class ).getSingleResult();
102+
final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class )
103+
.setParameter( "productId", PRODUCT_ID )
104+
.getSingleResult();
89105
assertThat( Hibernate.isInitialized( product.getVendor() ) ).isFalse();
90106
final CarVendor result = session.createQuery(
91107
"from CarVendor v where v = :vendor",
92108
CarVendor.class
93109
).setParameter( "vendor", product.getVendor() ).getSingleResult();
94-
// The proxy should have been initialized since Vendor has subclasses
95-
assertThat( Hibernate.isInitialized( product.getVendor() ) ).isTrue();
96110
assertThat( result.getId() ).isEqualTo( product.getVendor().getId() );
97111
} );
98112
}
99113

100-
@Entity( name = "Producer" )
114+
@Test
115+
public void testSubSubclassProxyParam(SessionFactoryScope scope) {
116+
scope.inTransaction( session -> {
117+
final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class )
118+
.setParameter( "productId", LUXURY_PRODUCT_ID )
119+
.getSingleResult();
120+
assertThat( Hibernate.isInitialized( product.getVendor() ) ).isFalse();
121+
final LuxuryCarVendor result = session.createQuery(
122+
"from CarVendor v where v = :vendor",
123+
LuxuryCarVendor.class
124+
).setParameter( "vendor", product.getVendor() ).getSingleResult();
125+
assertThat( result.getId() ).isEqualTo( product.getVendor().getId() );
126+
} );
127+
}
128+
129+
@Entity(name = "Producer")
101130
public static class Producer {
102131
@Id
103132
private Integer id;
@@ -120,7 +149,7 @@ public String getName() {
120149
}
121150
}
122151

123-
@Entity( name = "Vendor" )
152+
@Entity(name = "Vendor")
124153
public static class Vendor {
125154
@Id
126155
private Integer id;
@@ -143,7 +172,7 @@ public String getName() {
143172
}
144173
}
145174

146-
@Entity( name = "CarVendor" )
175+
@Entity(name = "CarVendor")
147176
public static class CarVendor extends Vendor {
148177
private String dealership;
149178

@@ -160,7 +189,18 @@ public String getDealership() {
160189
}
161190
}
162191

163-
@Entity( name = "Product" )
192+
@Entity(name = "LuxuryCarVendor")
193+
public static class LuxuryCarVendor extends CarVendor {
194+
195+
public LuxuryCarVendor() {
196+
}
197+
198+
public LuxuryCarVendor(int id, String name, String dealership) {
199+
super( id, name, dealership );
200+
}
201+
}
202+
203+
@Entity(name = "Product")
164204
public static final class Product {
165205
private Integer id;
166206
private Vendor vendor;
@@ -184,7 +224,7 @@ public void setId(Integer id) {
184224
this.id = id;
185225
}
186226

187-
@ManyToOne( fetch = FetchType.LAZY )
227+
@ManyToOne(fetch = FetchType.LAZY)
188228
public Vendor getVendor() {
189229
return vendor;
190230
}
@@ -193,7 +233,7 @@ public void setVendor(Vendor vendor) {
193233
this.vendor = vendor;
194234
}
195235

196-
@ManyToOne( fetch = FetchType.LAZY )
236+
@ManyToOne(fetch = FetchType.LAZY)
197237
public Producer getProducer() {
198238
return producer;
199239
}

0 commit comments

Comments
 (0)