HHH-15150 EmbeddedId CacheKeys are no longer Serializable

This commit is contained in:
Andrea Boriero 2022-04-27 11:59:46 +02:00 committed by Steve Ebersole
parent 1194e7f23b
commit 6c5870ee0b
10 changed files with 175 additions and 24 deletions

View File

@ -26,7 +26,7 @@ import org.hibernate.type.Type;
@Internal @Internal
public final class CacheKeyImplementation implements Serializable { public final class CacheKeyImplementation implements Serializable {
private final Object id; private final Object id;
private final Type type; private final CacheKeyValueDescriptor cacheKeyValueDescriptor;
private final String entityOrRoleName; private final String entityOrRoleName;
private final String tenantId; private final String tenantId;
private final int hashCode; private final int hashCode;
@ -50,15 +50,15 @@ public final class CacheKeyImplementation implements Serializable {
final String tenantId, final String tenantId,
final SessionFactoryImplementor factory) { final SessionFactoryImplementor factory) {
this.id = id; this.id = id;
this.type = type; this.cacheKeyValueDescriptor = type.toCacheKeyDescriptor( factory );
this.entityOrRoleName = entityOrRoleName; this.entityOrRoleName = entityOrRoleName;
this.tenantId = tenantId; this.tenantId = tenantId;
this.hashCode = calculateHashCode( type, factory ); this.hashCode = calculateHashCode( );
} }
private int calculateHashCode(Type type, SessionFactoryImplementor factory) { private int calculateHashCode() {
int result = type.getHashCode( id, factory ); int result = cacheKeyValueDescriptor.getHashCode( id );
result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0); result = 31 * result + ( tenantId != null ? tenantId.hashCode() : 0 );
return result; return result;
} }
@ -80,7 +80,7 @@ public final class CacheKeyImplementation implements Serializable {
} }
final CacheKeyImplementation that = (CacheKeyImplementation) other; final CacheKeyImplementation that = (CacheKeyImplementation) other;
return Objects.equals( entityOrRoleName, that.entityOrRoleName ) return Objects.equals( entityOrRoleName, that.entityOrRoleName )
&& type.isEqual( id, that.id ) && cacheKeyValueDescriptor.isEqual( id, that.id )
&& Objects.equals( tenantId, that.tenantId ); && Objects.equals( tenantId, that.tenantId );
} }

View File

@ -0,0 +1,14 @@
/*
* 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 java.io.Serializable;
public interface CacheKeyValueDescriptor extends Serializable {
int getHashCode(Object key);
boolean isEqual(Object key1, Object key2);
}

View File

@ -0,0 +1,86 @@
/*
* 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.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.type.Type;
import org.hibernate.usertype.CompositeUserType;
public class ComponentCacheKeyValueDescriptor implements CacheKeyValueDescriptor {
private SessionFactoryImplementor sessionFactory;
private NavigableRole role;
private Type[] propertyTypes;
private CompositeUserType<Object> compositeUserType;
private int propertySpan;
public ComponentCacheKeyValueDescriptor(
SessionFactoryImplementor sessionFactory,
NavigableRole role,
Type[] propertyTypes,
CompositeUserType<Object> compositeUserType,
int propertySpan) {
this.sessionFactory = sessionFactory;
this.role = role;
this.propertyTypes = propertyTypes;
this.compositeUserType = compositeUserType;
this.propertySpan = propertySpan;
}
@Override
public int getHashCode(Object key) {
if ( compositeUserType != null ) {
return compositeUserType.hashCode( key );
}
int result = 17;
for ( int i = 0; i < propertySpan; i++ ) {
Object y = getPropertyValue( key, i );
result *= 37;
if ( y != null ) {
result += propertyTypes[i].getHashCode( y );
}
}
return result;
}
@Override
public boolean isEqual(Object key1, Object key2) {
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 ) ) ) {
return false;
}
}
return true;
}
public Object getPropertyValue(Object component, int i) {
if ( component == null ) {
return null;
}
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 );
}
}
}

View File

@ -14,6 +14,7 @@ import java.util.stream.Stream;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.graph.RootGraph; import org.hibernate.graph.RootGraph;
import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.query.BindableType; import org.hibernate.query.BindableType;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
@ -91,6 +92,16 @@ public interface MappingMetamodel {
*/ */
EntityPersister getEntityDescriptor(NavigableRole name); EntityPersister getEntityDescriptor(NavigableRole name);
/**
* Get an EmbeddableMappingType based on its NavigableRole.
*
* @throws IllegalArgumentException if the role does not refer to an entity
*
* @see #findEntityDescriptor
*/
EmbeddableValuedModelPart getEmbeddableValuedModelPart(NavigableRole role);
/** /**
* Get an entity mapping descriptor based on its Class. * Get an entity mapping descriptor based on its Class.
* *

View File

@ -11,6 +11,7 @@ import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.model.domain.JpaMetamodel; import org.hibernate.metamodel.model.domain.JpaMetamodel;
import org.hibernate.metamodel.model.domain.NavigableRole;
/** /**
* Access to Hibernate's runtime metamodels which includes its domain-model (JPA impl) and its * Access to Hibernate's runtime metamodels which includes its domain-model (JPA impl) and its
@ -45,8 +46,12 @@ public interface RuntimeMetamodels {
return getMappingMetamodel().findCollectionDescriptor( role ).getAttributeMapping(); return getMappingMetamodel().findCollectionDescriptor( role ).getAttributeMapping();
} }
// todo (6.0) : I think we might need a form of mapping-model look-up for embeddables, something like: /**
@deprecated Use {@link #getEmbedded(NavigableRole)} instead
*/
@Deprecated
EmbeddableValuedModelPart getEmbedded(String role); EmbeddableValuedModelPart getEmbedded(String role);
EmbeddableValuedModelPart getEmbedded(NavigableRole role);
default String getImportedName(String name) { default String getImportedName(String name) {
return getMappingMetamodel().getImportedName( name ); return getMappingMetamodel().getImportedName( name );

View File

@ -11,6 +11,7 @@ import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.internal.SessionFactoryImpl; import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl; import org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl;
import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor; import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
@ -41,6 +42,11 @@ public class RuntimeMetamodelsImpl implements RuntimeMetamodelsImplementor {
throw new NotYetImplementedFor6Exception( getClass() ); throw new NotYetImplementedFor6Exception( getClass() );
} }
@Override
public EmbeddableValuedModelPart getEmbedded(NavigableRole role) {
return mappingMetamodel.getEmbeddableValuedModelPart( role );
}
/** /**
* Chicken-and-egg because things try to use the SessionFactory (specifically the MappingMetamodel) * Chicken-and-egg because things try to use the SessionFactory (specifically the MappingMetamodel)
* before it is ready. So we do this fugly code... * before it is ready. So we do this fugly code...

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.metamodel.model.domain; package org.hibernate.metamodel.model.domain;
import java.io.Serializable;
import java.util.Objects; import java.util.Objects;
import org.hibernate.spi.DotIdentifierSequence; import org.hibernate.spi.DotIdentifierSequence;
@ -21,7 +22,7 @@ import org.hibernate.spi.NavigablePath;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class NavigableRole implements DotIdentifierSequence { public class NavigableRole implements DotIdentifierSequence, Serializable {
public static final String IDENTIFIER_MAPPER_PROPERTY = NavigablePath.IDENTIFIER_MAPPER_PROPERTY; public static final String IDENTIFIER_MAPPER_PROPERTY = NavigablePath.IDENTIFIER_MAPPER_PROPERTY;
private final NavigableRole parent; private final NavigableRole parent;

View File

@ -49,6 +49,7 @@ import org.hibernate.mapping.PersistentClass;
import org.hibernate.metamodel.MappingMetamodel; import org.hibernate.metamodel.MappingMetamodel;
import org.hibernate.metamodel.internal.JpaMetaModelPopulationSetting; import org.hibernate.metamodel.internal.JpaMetaModelPopulationSetting;
import org.hibernate.metamodel.internal.JpaStaticMetaModelPopulationSetting; import org.hibernate.metamodel.internal.JpaStaticMetaModelPopulationSetting;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.model.domain.BasicDomainType; import org.hibernate.metamodel.model.domain.BasicDomainType;
@ -73,6 +74,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral; import org.hibernate.query.sqm.tree.expression.SqmFieldLiteral;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.ComponentType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.JdbcType; import org.hibernate.type.descriptor.jdbc.JdbcType;
@ -116,7 +118,7 @@ public class MappingMetamodelImpl implements MappingMetamodelImplementor, Metamo
private final Map<String, CollectionPersister> collectionPersisterMap = new ConcurrentHashMap<>(); private final Map<String, CollectionPersister> collectionPersisterMap = new ConcurrentHashMap<>();
private final Map<String, Set<String>> collectionRolesByEntityParticipant = new ConcurrentHashMap<>(); private final Map<String, Set<String>> collectionRolesByEntityParticipant = new ConcurrentHashMap<>();
private final Map<NavigableRole, EmbeddableValuedModelPart> embeddableValuedModelPart = new ConcurrentHashMap<>();
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// DomainMetamodel // DomainMetamodel
@ -236,6 +238,7 @@ public class MappingMetamodelImpl implements MappingMetamodelImplementor, Metamo
collectionPersisterMap.values().forEach( CollectionPersister::postInstantiate ); collectionPersisterMap.values().forEach( CollectionPersister::postInstantiate );
registerEmbeddableMappingType( bootModel );
( (JpaMetamodelImpl) this.jpaMetamodel ).processJpa( ( (JpaMetamodelImpl) this.jpaMetamodel ).processJpa(
bootModel, bootModel,
@ -248,6 +251,18 @@ public class MappingMetamodelImpl implements MappingMetamodelImplementor, Metamo
); );
} }
private void registerEmbeddableMappingType(MetadataImplementor bootModel) {
bootModel.visitRegisteredComponents(
composite -> {
final EmbeddableValuedModelPart mappingModelPart = ((ComponentType) composite.getType()).getMappingModelPart();
embeddableValuedModelPart.put(
mappingModelPart.getNavigableRole(),
mappingModelPart
);
}
);
}
private void processBootEntities( private void processBootEntities(
java.util.Collection<PersistentClass> entityBindings, java.util.Collection<PersistentClass> entityBindings,
CacheImplementor cacheImplementor, CacheImplementor cacheImplementor,
@ -400,6 +415,15 @@ public class MappingMetamodelImpl implements MappingMetamodelImplementor, Metamo
throw new NotYetImplementedFor6Exception( getClass() ); throw new NotYetImplementedFor6Exception( getClass() );
} }
@Override
public EmbeddableValuedModelPart getEmbeddableValuedModelPart(NavigableRole role){
EmbeddableValuedModelPart embeddableMappingType = embeddableValuedModelPart.get( role );
if ( embeddableMappingType == null ) {
throw new IllegalArgumentException( "Unable to locate EmbeddableValuedModelPart: " + role );
}
return embeddableMappingType;
}
@Override @Override
public EntityPersister findEntityDescriptor(String entityName) { public EntityPersister findEntityDescriptor(String entityName) {
return entityPersisterMap.get( entityName ); return entityPersisterMap.get( entityName );

View File

@ -21,15 +21,14 @@ import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.PropertyNotFoundException; import org.hibernate.PropertyNotFoundException;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer;
import org.hibernate.cfg.Environment; import org.hibernate.cache.internal.CacheKeyValueDescriptor;
import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.cache.internal.ComponentCacheKeyValueDescriptor;
import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.mapping.Component; import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.metamodel.spi.EmbeddableInstantiator; import org.hibernate.metamodel.spi.EmbeddableInstantiator;
@ -64,7 +63,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
private final boolean isKey; private final boolean isKey;
private boolean hasNotNullProperty; private boolean hasNotNullProperty;
private final boolean createEmptyCompositesEnabled;
private final CompositeUserType<Object> compositeUserType; private final CompositeUserType<Object> compositeUserType;
private EmbeddableValuedModelPart mappingModelPart; private EmbeddableValuedModelPart mappingModelPart;
@ -100,11 +98,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
i++; i++;
} }
this.createEmptyCompositesEnabled = ConfigurationHelper.getBoolean(
Environment.CREATE_EMPTY_COMPOSITES_ENABLED,
buildingContext.getBootstrapContext().getServiceRegistry().getService( ConfigurationService.class ).getSettings(),
false
);
if ( component.getTypeName() != null ) { if ( component.getTypeName() != null ) {
//noinspection unchecked //noinspection unchecked
this.compositeUserType = (CompositeUserType<Object>) buildingContext.getBootstrapContext() this.compositeUserType = (CompositeUserType<Object>) buildingContext.getBootstrapContext()
@ -708,6 +701,17 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
return result; return result;
} }
@Override
public CacheKeyValueDescriptor toCacheKeyDescriptor(SessionFactoryImplementor sessionFactory) {
return new ComponentCacheKeyValueDescriptor(
sessionFactory,
mappingModelPart.getNavigableRole(),
propertyTypes,
compositeUserType,
propertySpan
);
}
@Override @Override
public boolean isEmbedded() { public boolean isEmbedded() {
return false; return false;
@ -825,10 +829,6 @@ public class ComponentType extends AbstractType implements CompositeTypeImplemen
return hasNotNullProperty; return hasNotNullProperty;
} }
private boolean isCreateEmptyCompositesEnabled() {
return createEmptyCompositesEnabled;
}
@Override @Override
public Class<?> getBindableJavaType() { public Class<?> getBindableJavaType() {
return getReturnedClass(); return getReturnedClass();

View File

@ -14,6 +14,7 @@ import java.util.Map;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.Internal; import org.hibernate.Internal;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.cache.internal.CacheKeyValueDescriptor;
import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -34,7 +35,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@Internal @Internal
public interface Type extends Serializable { public interface Type extends CacheKeyValueDescriptor {
/** /**
* Return true if the implementation is castable to {@link AssociationType}. This does not necessarily imply that * 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 * the type actually represents an association. Essentially a polymorphic version of
@ -431,4 +432,7 @@ public interface Type extends Serializable {
*/ */
boolean[] toColumnNullness(Object value, Mapping mapping); boolean[] toColumnNullness(Object value, Mapping mapping);
default CacheKeyValueDescriptor toCacheKeyDescriptor(SessionFactoryImplementor sessionFactory) {
return this;
}
} }