HHH-17704 Query using detached Proxy as parameter fails with LazyInitializationException
This commit is contained in:
parent
9421b94bca
commit
ecd0acb735
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.type.descriptor.java.spi;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
import org.hibernate.type.descriptor.java.AbstractClassJavaType;
|
||||
import org.hibernate.type.descriptor.java.IncomparableComparator;
|
||||
|
@ -14,6 +14,8 @@ import org.hibernate.type.descriptor.java.MutabilityPlan;
|
|||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;
|
||||
|
||||
import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer;
|
||||
|
||||
/**
|
||||
* Uses object identity for {@code equals}/{@code hashCode} as we ensure that internally.
|
||||
*
|
||||
|
@ -44,7 +46,15 @@ public class EntityJavaType<T> extends AbstractClassJavaType<T> {
|
|||
|
||||
@Override
|
||||
public boolean isInstance(Object value) {
|
||||
return getJavaTypeClass().isAssignableFrom( Hibernate.getClassLazy( value ) );
|
||||
final LazyInitializer lazyInitializer = extractLazyInitializer( value );
|
||||
final Class<T> javaTypeClass = getJavaTypeClass();
|
||||
if ( lazyInitializer != null ) {
|
||||
return javaTypeClass.isAssignableFrom( lazyInitializer.getPersistentClass() )
|
||||
|| javaTypeClass.isAssignableFrom( lazyInitializer.getImplementationClass() );
|
||||
}
|
||||
else {
|
||||
return javaTypeClass.isAssignableFrom( value.getClass() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.orm.test.proxy;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
|
@ -23,15 +25,20 @@ import jakarta.persistence.ManyToOne;
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DomainModel( annotatedClasses = {
|
||||
@DomainModel(annotatedClasses = {
|
||||
ProxyAsQueryParameterTest.Product.class,
|
||||
ProxyAsQueryParameterTest.Vendor.class,
|
||||
ProxyAsQueryParameterTest.CarVendor.class,
|
||||
ProxyAsQueryParameterTest.LuxuryCarVendor.class,
|
||||
ProxyAsQueryParameterTest.Producer.class,
|
||||
} )
|
||||
})
|
||||
@SessionFactory
|
||||
@Jira( "https://hibernate.atlassian.net/browse/HHH-17467" )
|
||||
@Jira("https://hibernate.atlassian.net/browse/HHH-17467")
|
||||
public class ProxyAsQueryParameterTest {
|
||||
|
||||
private static final Integer PRODUCT_ID = 1;
|
||||
private static final Integer LUXURY_PRODUCT_ID = 2;
|
||||
|
||||
@BeforeAll
|
||||
public void setUp(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
|
@ -39,8 +46,13 @@ public class ProxyAsQueryParameterTest {
|
|||
session.persist( vendor );
|
||||
final Producer producer = new Producer( 1, "producer_1" );
|
||||
session.persist( producer );
|
||||
final Product product = new Product( 1, vendor, producer );
|
||||
final Product product = new Product( PRODUCT_ID, vendor, producer );
|
||||
session.persist( product );
|
||||
|
||||
final LuxuryCarVendor luxuryCarVendor = new LuxuryCarVendor( 2, "vendor_2", "luxury" );
|
||||
session.persist( luxuryCarVendor );
|
||||
final Product luxuryProduct = new Product( LUXURY_PRODUCT_ID, luxuryCarVendor, producer );
|
||||
session.persist( luxuryProduct );
|
||||
} );
|
||||
}
|
||||
|
||||
|
@ -55,29 +67,31 @@ public class ProxyAsQueryParameterTest {
|
|||
@Test
|
||||
public void testProxyParam(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final Product product = session.createQuery( "from Product p", Product.class ).getSingleResult();
|
||||
final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class )
|
||||
.setParameter( "productId", PRODUCT_ID )
|
||||
.getSingleResult();
|
||||
assertThat( Hibernate.isInitialized( product.getProducer() ) ).isFalse();
|
||||
final Product result = session.createQuery(
|
||||
final List<Product> results = session.createQuery(
|
||||
"from Product p where p.producer = :producer",
|
||||
Product.class
|
||||
).setParameter( "producer", product.getProducer() ).getSingleResult();
|
||||
// The proxy should not have been initialized since Producer doesn't have subclasses
|
||||
assertThat( Hibernate.isInitialized( product.getProducer() ) ).isFalse();
|
||||
assertThat( result.getProducer().getId() ).isEqualTo( product.getProducer().getId() );
|
||||
).setParameter( "producer", product.getProducer() ).getResultList();
|
||||
assertThat( results.size() ).isEqualTo( 2 );
|
||||
assertThat( results.get( 0 ).getProducer().getId() ).isEqualTo( product.getProducer().getId() );
|
||||
assertThat( results.get( 1 ).getProducer().getId() ).isEqualTo( product.getProducer().getId() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProxyParamWithSubclasses(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final Product product = session.createQuery( "from Product p", Product.class ).getSingleResult();
|
||||
final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class )
|
||||
.setParameter( "productId", PRODUCT_ID )
|
||||
.getSingleResult();
|
||||
assertThat( Hibernate.isInitialized( product.getVendor() ) ).isFalse();
|
||||
final Product result = session.createQuery(
|
||||
"from Product p where p.vendor = :vendor",
|
||||
Product.class
|
||||
).setParameter( "vendor", product.getVendor() ).getSingleResult();
|
||||
// The proxy will have been initialized since Vendor has subclasses
|
||||
assertThat( Hibernate.isInitialized( product.getVendor() ) ).isTrue();
|
||||
assertThat( result.getVendor().getId() ).isEqualTo( product.getVendor().getId() );
|
||||
} );
|
||||
}
|
||||
|
@ -85,19 +99,34 @@ public class ProxyAsQueryParameterTest {
|
|||
@Test
|
||||
public void testSubclassProxyParam(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final Product product = session.createQuery( "from Product p", Product.class ).getSingleResult();
|
||||
final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class )
|
||||
.setParameter( "productId", PRODUCT_ID )
|
||||
.getSingleResult();
|
||||
assertThat( Hibernate.isInitialized( product.getVendor() ) ).isFalse();
|
||||
final CarVendor result = session.createQuery(
|
||||
"from CarVendor v where v = :vendor",
|
||||
CarVendor.class
|
||||
).setParameter( "vendor", product.getVendor() ).getSingleResult();
|
||||
// The proxy should have been initialized since Vendor has subclasses
|
||||
assertThat( Hibernate.isInitialized( product.getVendor() ) ).isTrue();
|
||||
assertThat( result.getId() ).isEqualTo( product.getVendor().getId() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity( name = "Producer" )
|
||||
@Test
|
||||
public void testSubSubclassProxyParam(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> {
|
||||
final Product product = session.createQuery( "from Product p where p.id = :productId", Product.class )
|
||||
.setParameter( "productId", LUXURY_PRODUCT_ID )
|
||||
.getSingleResult();
|
||||
assertThat( Hibernate.isInitialized( product.getVendor() ) ).isFalse();
|
||||
final LuxuryCarVendor result = session.createQuery(
|
||||
"from CarVendor v where v = :vendor",
|
||||
LuxuryCarVendor.class
|
||||
).setParameter( "vendor", product.getVendor() ).getSingleResult();
|
||||
assertThat( result.getId() ).isEqualTo( product.getVendor().getId() );
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "Producer")
|
||||
public static class Producer {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
@ -120,7 +149,7 @@ public class ProxyAsQueryParameterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Entity( name = "Vendor" )
|
||||
@Entity(name = "Vendor")
|
||||
public static class Vendor {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
@ -143,7 +172,7 @@ public class ProxyAsQueryParameterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Entity( name = "CarVendor" )
|
||||
@Entity(name = "CarVendor")
|
||||
public static class CarVendor extends Vendor {
|
||||
private String dealership;
|
||||
|
||||
|
@ -160,7 +189,18 @@ public class ProxyAsQueryParameterTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Entity( name = "Product" )
|
||||
@Entity(name = "LuxuryCarVendor")
|
||||
public static class LuxuryCarVendor extends CarVendor {
|
||||
|
||||
public LuxuryCarVendor() {
|
||||
}
|
||||
|
||||
public LuxuryCarVendor(int id, String name, String dealership) {
|
||||
super( id, name, dealership );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "Product")
|
||||
public static final class Product {
|
||||
private Integer id;
|
||||
private Vendor vendor;
|
||||
|
@ -184,7 +224,7 @@ public class ProxyAsQueryParameterTest {
|
|||
this.id = id;
|
||||
}
|
||||
|
||||
@ManyToOne( fetch = FetchType.LAZY )
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
public Vendor getVendor() {
|
||||
return vendor;
|
||||
}
|
||||
|
@ -193,7 +233,7 @@ public class ProxyAsQueryParameterTest {
|
|||
this.vendor = vendor;
|
||||
}
|
||||
|
||||
@ManyToOne( fetch = FetchType.LAZY )
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
public Producer getProducer() {
|
||||
return producer;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue