HHH-15150 - EmbeddedId CacheKeys are no longer Serializable
This commit is contained in:
parent
6c5870ee0b
commit
e2258b3232
|
@ -8,6 +8,11 @@ package org.hibernate.cache.internal;
|
|||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Used to handle hash-code and equality check for cache key values
|
||||
*
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public interface CacheKeyValueDescriptor extends Serializable {
|
||||
int getHashCode(Object key);
|
||||
boolean isEqual(Object key1, Object key2);
|
||||
|
|
|
@ -6,45 +6,47 @@
|
|||
*/
|
||||
package org.hibernate.cache.internal;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.usertype.CompositeUserType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
||||
/**
|
||||
* CacheKeyValueDescriptor used to describe normal composite mappings
|
||||
*
|
||||
* @see CustomComponentCacheKeyValueDescriptor
|
||||
*/
|
||||
public class ComponentCacheKeyValueDescriptor implements CacheKeyValueDescriptor {
|
||||
private final NavigableRole role;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
|
||||
private SessionFactoryImplementor sessionFactory;
|
||||
private NavigableRole role;
|
||||
private Type[] propertyTypes;
|
||||
private CompositeUserType<Object> compositeUserType;
|
||||
private int propertySpan;
|
||||
private transient EmbeddableValuedModelPart embeddedMapping;
|
||||
|
||||
public ComponentCacheKeyValueDescriptor(
|
||||
SessionFactoryImplementor sessionFactory,
|
||||
NavigableRole role,
|
||||
Type[] propertyTypes,
|
||||
CompositeUserType<Object> compositeUserType,
|
||||
int propertySpan) {
|
||||
this.sessionFactory = sessionFactory;
|
||||
public ComponentCacheKeyValueDescriptor(NavigableRole role, SessionFactoryImplementor sessionFactory) {
|
||||
this.role = role;
|
||||
this.propertyTypes = propertyTypes;
|
||||
this.compositeUserType = compositeUserType;
|
||||
this.propertySpan = propertySpan;
|
||||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHashCode(Object key) {
|
||||
if ( compositeUserType != null ) {
|
||||
return compositeUserType.hashCode( key );
|
||||
}
|
||||
final List<AttributeMapping> attrs = getEmbeddedMapping().getEmbeddableTypeDescriptor().getAttributeMappings();
|
||||
int result = 17;
|
||||
for ( int i = 0; i < propertySpan; i++ ) {
|
||||
Object y = getPropertyValue( key, i );
|
||||
|
||||
for ( int i = 0; i < attrs.size(); i++ ) {
|
||||
final AttributeMapping attr = attrs.get( i );
|
||||
final Object attrValue = getAttributeValue( key, i, attr );
|
||||
result *= 37;
|
||||
if ( y != null ) {
|
||||
result += propertyTypes[i].getHashCode( y );
|
||||
if ( attrValue != null ) {
|
||||
//noinspection rawtypes
|
||||
final JavaType javaType = attr.getJavaType();
|
||||
//noinspection unchecked
|
||||
result += javaType.extractHashCode( attrValue );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -53,34 +55,38 @@ public class ComponentCacheKeyValueDescriptor implements CacheKeyValueDescriptor
|
|||
if ( key1 == key2 ) {
|
||||
return true;
|
||||
}
|
||||
if ( compositeUserType != null ) {
|
||||
return compositeUserType.equals( key1, key2 );
|
||||
}
|
||||
// null value and empty component are considered equivalent
|
||||
for ( int i = 0; i < propertySpan; i++ ) {
|
||||
if ( !propertyTypes[i].isEqual( getPropertyValue( key1, i ), getPropertyValue( key2, i ) ) ) {
|
||||
|
||||
final List<AttributeMapping> attrs = getEmbeddedMapping().getEmbeddableTypeDescriptor().getAttributeMappings();
|
||||
for ( int i = 0; i < attrs.size(); i++ ) {
|
||||
final AttributeMapping attr = attrs.get( i );
|
||||
|
||||
final Object value1 = getAttributeValue( key1, i, attr );
|
||||
final Object value2 = getAttributeValue( key2, i, attr );
|
||||
|
||||
//noinspection unchecked
|
||||
final JavaType<Object> javaType = (JavaType<Object>) attr.getJavaType();
|
||||
if ( ! javaType.areEqual( value1, value2 ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Object getPropertyValue(Object component, int i) {
|
||||
if ( component == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getAttributeValue(Object component, int i, AttributeMapping attr) {
|
||||
if ( component instanceof Object[] ) {
|
||||
// A few calls to hashCode pass the property values already in an
|
||||
// Object[] (ex: QueryKey hash codes for cached queries).
|
||||
// It's easiest to just check for the condition here prior to
|
||||
// trying reflection.
|
||||
return ( (Object[]) component )[i];
|
||||
}
|
||||
else {
|
||||
return sessionFactory.getRuntimeMetamodels().getEmbedded( role )
|
||||
.getEmbeddableTypeDescriptor()
|
||||
.getValue( component, i );
|
||||
return attr.getValue( component );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private EmbeddableValuedModelPart getEmbeddedMapping() {
|
||||
if ( embeddedMapping == null ) {
|
||||
embeddedMapping = sessionFactory.getRuntimeMetamodels().getEmbedded( role );
|
||||
}
|
||||
return embeddedMapping;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.cache.internal;
|
||||
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.usertype.CompositeUserType;
|
||||
|
||||
/**
|
||||
* CacheKeyValueDescriptor used to describe CompositeUserType mappings
|
||||
*/
|
||||
public class CustomComponentCacheKeyValueDescriptor implements CacheKeyValueDescriptor {
|
||||
private final NavigableRole role;
|
||||
private final CompositeUserType<Object> compositeUserType;
|
||||
|
||||
public CustomComponentCacheKeyValueDescriptor(
|
||||
NavigableRole role,
|
||||
CompositeUserType<Object> compositeUserType) {
|
||||
this.role = role;
|
||||
this.compositeUserType = compositeUserType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHashCode(Object key) {
|
||||
return compositeUserType.hashCode( key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqual(Object key1, Object key2) {
|
||||
return compositeUserType.equals( key1, key2 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CustomComponentCacheKeyValueDescriptor(" + role + ")";
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ package org.hibernate.type;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.cache.internal.CacheKeyValueDescriptor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.mapping.IndexedConsumer;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedMapping;
|
||||
|
@ -25,7 +26,7 @@ import org.hibernate.type.descriptor.java.JavaType;
|
|||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface BasicType<T> extends Type, BasicDomainType<T>, MappingType, BasicValuedMapping, JdbcMapping {
|
||||
public interface BasicType<T> extends Type, BasicDomainType<T>, MappingType, BasicValuedMapping, JdbcMapping, CacheKeyValueDescriptor {
|
||||
/**
|
||||
* Get the names under which this type should be registered in the type registry.
|
||||
*
|
||||
|
|
|
@ -23,6 +23,7 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
|
|||
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
|
||||
import org.hibernate.cache.internal.CacheKeyValueDescriptor;
|
||||
import org.hibernate.cache.internal.ComponentCacheKeyValueDescriptor;
|
||||
import org.hibernate.cache.internal.CustomComponentCacheKeyValueDescriptor;
|
||||
import org.hibernate.engine.spi.CascadeStyle;
|
||||
import org.hibernate.engine.spi.Mapping;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -31,9 +32,9 @@ import org.hibernate.internal.util.StringHelper;
|
|||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.mapping.Component;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
|
||||
import org.hibernate.property.access.spi.PropertyAccess;
|
||||
import org.hibernate.query.sqm.SqmExpressible;
|
||||
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
|
||||
|
@ -66,6 +67,7 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
|
|||
private final CompositeUserType<Object> compositeUserType;
|
||||
|
||||
private EmbeddableValuedModelPart mappingModelPart;
|
||||
private CacheKeyValueDescriptor cacheKeyValueDescriptor;
|
||||
|
||||
public ComponentType(Component component, int[] originalPropertyOrder, MetadataBuildingContext buildingContext) {
|
||||
this.componentClass = component.isDynamic()
|
||||
|
@ -703,13 +705,22 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
|
|||
|
||||
@Override
|
||||
public CacheKeyValueDescriptor toCacheKeyDescriptor(SessionFactoryImplementor sessionFactory) {
|
||||
return new ComponentCacheKeyValueDescriptor(
|
||||
sessionFactory,
|
||||
mappingModelPart.getNavigableRole(),
|
||||
propertyTypes,
|
||||
compositeUserType,
|
||||
propertySpan
|
||||
);
|
||||
if ( cacheKeyValueDescriptor == null ) {
|
||||
if ( compositeUserType != null ) {
|
||||
cacheKeyValueDescriptor = new CustomComponentCacheKeyValueDescriptor(
|
||||
mappingModelPart.getNavigableRole(),
|
||||
compositeUserType
|
||||
);
|
||||
}
|
||||
else {
|
||||
cacheKeyValueDescriptor = new ComponentCacheKeyValueDescriptor(
|
||||
mappingModelPart.getNavigableRole(),
|
||||
sessionFactory
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return cacheKeyValueDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -35,7 +35,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
@Internal
|
||||
public interface Type extends CacheKeyValueDescriptor {
|
||||
public interface Type extends Serializable {
|
||||
/**
|
||||
* Return true if the implementation is castable to {@link AssociationType}. This does not necessarily imply that
|
||||
* the type actually represents an association. Essentially a polymorphic version of
|
||||
|
@ -433,6 +433,10 @@ public interface Type extends CacheKeyValueDescriptor {
|
|||
boolean[] toColumnNullness(Object value, Mapping mapping);
|
||||
|
||||
default CacheKeyValueDescriptor toCacheKeyDescriptor(SessionFactoryImplementor sessionFactory) {
|
||||
return this;
|
||||
if ( this instanceof CacheKeyValueDescriptor ) {
|
||||
return (CacheKeyValueDescriptor) this;
|
||||
}
|
||||
|
||||
throw new HibernateException( "Type does not support use as a cache-key" );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||
|
||||
|
||||
/**
|
||||
* @author Gail Badner
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@BaseUnitTest
|
||||
public class CacheKeySerializationTest {
|
||||
|
|
Loading…
Reference in New Issue