Skip to content

Commit

Permalink
HHH-17704 Query using detached Proxy as parameter fails with LazyInit…
Browse files Browse the repository at this point in the history
…ializationException
  • Loading branch information
dreab8 authored and mbladel committed Feb 8, 2024
1 parent d35cd46 commit e98503a
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@
*/
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;
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.
*
Expand Down Expand Up @@ -44,7 +46,15 @@ public boolean areEqual(T one, T another) {

@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -23,24 +25,34 @@

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 -> {
final CarVendor vendor = new CarVendor( 1, "vendor_1", "dealership_1" );
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 );
} );
}

Expand All @@ -55,49 +67,66 @@ public void tearDown(SessionFactoryScope scope) {
@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() );
} );
}

@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;
Expand All @@ -120,7 +149,7 @@ public String getName() {
}
}

@Entity( name = "Vendor" )
@Entity(name = "Vendor")
public static class Vendor {
@Id
private Integer id;
Expand All @@ -143,7 +172,7 @@ public String getName() {
}
}

@Entity( name = "CarVendor" )
@Entity(name = "CarVendor")
public static class CarVendor extends Vendor {
private String dealership;

Expand All @@ -160,7 +189,18 @@ public String getDealership() {
}
}

@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;
Expand All @@ -184,7 +224,7 @@ public void setId(Integer id) {
this.id = id;
}

@ManyToOne( fetch = FetchType.LAZY )
@ManyToOne(fetch = FetchType.LAZY)
public Vendor getVendor() {
return vendor;
}
Expand All @@ -193,7 +233,7 @@ public void setVendor(Vendor vendor) {
this.vendor = vendor;
}

@ManyToOne( fetch = FetchType.LAZY )
@ManyToOne(fetch = FetchType.LAZY)
public Producer getProducer() {
return producer;
}
Expand Down

0 comments on commit e98503a

Please sign in to comment.