HHH-16281 Inconsistent Behaivor of L2 cache between Hibernate 5 and 6

This commit is contained in:
Andrea Boriero 2023-03-27 12:06:41 +02:00 committed by Andrea Boriero
parent 52e95aec46
commit c9640c2ee3
9 changed files with 125 additions and 5 deletions

View File

@ -56,7 +56,13 @@ public interface BasicValuedMapping extends ValueMapping, SqlExpressible {
}
@Override
default void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session){
default void addToCacheKey(
MutableCacheKeyBuilder cacheKey,
Object value,
SharedSessionContractImplementor session) {
if ( value == null ) {
return;
}
final JdbcMapping jdbcMapping = getJdbcMapping();
final BasicValueConverter converter = jdbcMapping.getValueConverter();
final Serializable disassemble;

View File

@ -320,6 +320,9 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
@Override
public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session) {
if ( value == null ) {
return;
}
final Serializable[] result = new Serializable[ getNumberOfAttributeMappings() ];
for ( int i = 0; i < result.length; i++ ) {
final AttributeMapping attributeMapping = getAttributeMapping( i );

View File

@ -423,6 +423,9 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
@Override
public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session) {
if ( value == null ) {
return;
}
final JdbcMapping jdbcMapping = getJdbcMapping();
final BasicValueConverter converter = jdbcMapping.getValueConverter();
final Serializable disassemble;

View File

@ -44,6 +44,9 @@ public class TupleMappingModelExpressible implements MappingModelExpressible {
@Override
public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session) {
if ( value == null ) {
return;
}
for ( int i = 0; i < components.length; i++ ) {
components[i].addToCacheKey( cacheKey, value, session );
}

View File

@ -317,6 +317,9 @@ public class AnonymousTupleBasicValuedModelPart implements ModelPart, MappingTyp
@Override
public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session) {
if ( value == null ) {
return;
}
cacheKey.addValue( value );
cacheKey.addHashCode( ( (JavaType) getExpressibleJavaType() ).extractHashCode( value ) );
}

View File

@ -123,6 +123,9 @@ public class JdbcLiteral<T> implements Literal, MappingModelExpressible<T>, Doma
@Override
public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session) {
if ( value == null ) {
return;
}
final Serializable disassemble = ( (MutabilityPlan<Object>) jdbcMapping.getJdbcJavaType().getMutabilityPlan() )
.disassemble( value, session );
final int hashCode = jdbcMapping.getJavaTypeDescriptor().extractHashCode( value );

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.sql.exec.internal;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@ -158,20 +159,27 @@ public abstract class AbstractJdbcParameter
@Override
public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session) {
if ( value == null ) {
return;
}
final JdbcMapping jdbcMapping = getJdbcMapping();
final BasicValueConverter converter = jdbcMapping.getValueConverter();
final Serializable disassemble;
final int hashCode;
if ( converter == null ) {
final JavaType javaTypeDescriptor = jdbcMapping.getJavaTypeDescriptor();
cacheKey.addValue( javaTypeDescriptor.getMutabilityPlan().disassemble( value, session ) );
cacheKey.addHashCode( javaTypeDescriptor.extractHashCode( value ) );
disassemble = javaTypeDescriptor.getMutabilityPlan().disassemble( value, session );
hashCode = javaTypeDescriptor.extractHashCode( value );
}
else {
final Object relationalValue = converter.toRelationalValue( value );
final JavaType relationalJavaType = converter.getRelationalJavaType();
cacheKey.addValue( relationalJavaType.getMutabilityPlan().disassemble( relationalValue, session ) );
cacheKey.addHashCode( relationalJavaType.extractHashCode( relationalValue ) );
disassemble = relationalJavaType.getMutabilityPlan().disassemble( relationalValue, session );
hashCode = relationalJavaType.extractHashCode( relationalValue );
}
cacheKey.addValue( disassemble );
cacheKey.addHashCode( hashCode );
}
@Override

View File

@ -220,6 +220,9 @@ public class CustomType<J>
@Override
public void addToCacheKey(MutableCacheKeyBuilder cacheKey, Object value, SharedSessionContractImplementor session) {
if ( value == null ) {
return;
}
final Serializable disassembled = getUserType().disassemble( (J) value );
// Since UserType#disassemble is an optional operation,
// we have to handle the fact that it could produce a null value,

View File

@ -95,6 +95,44 @@ public class QueryCacheWithObjectParameterTest {
);
}
@Test
public void testQueryWithEmbeddableParameterWithANull(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
evictQueryRegion( session );
Query<Parent> queryParent = session.createQuery(
"from Parent p where p.address = :address",
Parent.class
);
queryParent.setParameter( "address", new Address( "via Milano", null ) );
queryParent.setCacheable( true );
List<Parent> resultList = queryParent.getResultList();
assertThat( resultList ).hasSize( 0 );
CacheRegionStatistics defaultQueryCacheRegionStatistics = getQueryCacheRegionStatistics( session );
assertThat( defaultQueryCacheRegionStatistics.getHitCount() ).isEqualTo( 0 );
}
);
scope.inTransaction(
session -> {
Query<Parent> queryParent = session.createQuery(
"from Parent p where p.address = :address",
Parent.class
);
queryParent.setParameter( "address", new Address( "via Milano", null ) );
queryParent.setCacheable( true );
List<Parent> resultList = queryParent.getResultList();
assertThat( resultList ).hasSize( 0 );
CacheRegionStatistics defaultQueryCacheRegionStatistics = getQueryCacheRegionStatistics( session );
assertThat( defaultQueryCacheRegionStatistics.getHitCount() ).isEqualTo( 1 );
}
);
}
@Test
public void testQueryCacheHits(SessionFactoryScope scope) {
scope.inTransaction(
@ -189,6 +227,56 @@ public class QueryCacheWithObjectParameterTest {
);
}
@Test
public void testQueryCacheHitsNullParameter(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
evictQueryRegion( session );
Query<Parent> queryParent = session.createQuery(
"from Parent p where p.name = 'John'",
Parent.class
);
List<Parent> p = queryParent.getResultList();
assertThat( p ).hasSize( 1 );
Query<Child> queryChildren = session.createQuery(
"from Child c where c.parent.id = ?1",
Child.class
);
queryChildren.setParameter( 1, null );
queryChildren.setCacheable( true );
List<Child> c = queryChildren.getResultList();
assertThat( c ).hasSize( 0 );
CacheRegionStatistics defaultQueryCacheRegionStatistics = getQueryCacheRegionStatistics( session );
assertThat( defaultQueryCacheRegionStatistics.getHitCount() ).isEqualTo( 0 );
}
);
scope.inTransaction(
session -> {
Query<Parent> queryParent = session.createQuery(
"from Parent p where p.name = 'John'",
Parent.class
);
List<Parent> p = queryParent.getResultList();
assertThat( p ).hasSize( 1 );
Query<Child> queryChildren = session.createQuery(
"from Child c where c.parent.id = ?1",
Child.class
);
queryChildren.setParameter( 1, null );
queryChildren.setCacheable( true );
List<Child> c = queryChildren.getResultList();
assertThat( c ).hasSize( 0 );
CacheRegionStatistics defaultQueryCacheRegionStatistics = getQueryCacheRegionStatistics( session );
assertThat( defaultQueryCacheRegionStatistics.getHitCount() ).isEqualTo( 1 );
}
);
}
private static void evictQueryRegion(SessionImplementor session) {
session.getSessionFactory()
.getCache()