HHH-16092 Trim allocation size of CacheKeyImplementation, avoid Objects::deepEquals
This commit is contained in:
parent
4ca5902672
commit
1652102c1a
91
hibernate-core/src/main/java/org/hibernate/cache/internal/BasicCacheKeyImplementation.java
vendored
Normal file
91
hibernate-core/src/main/java/org/hibernate/cache/internal/BasicCacheKeyImplementation.java
vendored
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* 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;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.hibernate.Internal;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Key produced by DefaultCacheKeysFactory; this is the specialized implementation
|
||||
* for the case in which the disassembled identifier is not an array and the tenantId
|
||||
* is not being defined.
|
||||
* The goal of this specialized representation is to be more efficient for this most common
|
||||
* scenario, and save memory by omitting some fields.
|
||||
* When making changes to this class, please be aware of its memory footprint.
|
||||
*
|
||||
* @author Sanne Grinovero
|
||||
* @since 6.2
|
||||
*/
|
||||
@Internal
|
||||
final class BasicCacheKeyImplementation implements Serializable {
|
||||
|
||||
final Serializable id;
|
||||
private final String entityOrRoleName;
|
||||
private final int hashCode;
|
||||
|
||||
/**
|
||||
* Being an internal contract the arguments are not being checked.
|
||||
* @param originalId
|
||||
* @param disassembledKey this must be the "disassembled" form of an ID
|
||||
* @param type
|
||||
* @param entityOrRoleName
|
||||
*/
|
||||
@Internal
|
||||
public BasicCacheKeyImplementation(
|
||||
final Object originalId,
|
||||
final Serializable disassembledKey,
|
||||
final Type type,
|
||||
final String entityOrRoleName) {
|
||||
assert disassembledKey != null;
|
||||
assert entityOrRoleName != null;
|
||||
this.id = disassembledKey;
|
||||
this.entityOrRoleName = entityOrRoleName;
|
||||
this.hashCode = calculateHashCode( originalId, type );
|
||||
}
|
||||
|
||||
private static int calculateHashCode(Object disassembledKey, Type type) {
|
||||
return type.getHashCode( disassembledKey );
|
||||
}
|
||||
|
||||
public Object getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object other) {
|
||||
if ( other == null ) {
|
||||
return false;
|
||||
}
|
||||
else if ( this == other ) {
|
||||
return true;
|
||||
}
|
||||
else if ( other.getClass() != BasicCacheKeyImplementation.class ) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
final BasicCacheKeyImplementation o = (BasicCacheKeyImplementation) other;
|
||||
return this.id.equals( o.id ) &&
|
||||
this.entityOrRoleName.equals( o.entityOrRoleName );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// Used to be required for OSCache
|
||||
return entityOrRoleName + '#' + id.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -22,6 +22,7 @@ import org.hibernate.type.Type;
|
|||
*
|
||||
* @author Gavin King
|
||||
* @author Steve Ebersole
|
||||
* @author Sanne Grinovero
|
||||
*/
|
||||
@Internal
|
||||
public final class CacheKeyImplementation implements Serializable {
|
||||
|
@ -30,28 +31,35 @@ public final class CacheKeyImplementation implements Serializable {
|
|||
private final String tenantId;
|
||||
private final int hashCode;
|
||||
|
||||
//because of object alignmnet, we had "free space" in this key:
|
||||
//this field isn't strictly necessary but convenient: watch for
|
||||
//class layout changes.
|
||||
private final boolean requiresDeepEquals;
|
||||
|
||||
/**
|
||||
* Construct a new key for a collection or entity instance.
|
||||
* Note that an entity name should always be the root entity
|
||||
* name, not a subclass entity name.
|
||||
*
|
||||
* @param id The identifier associated with the cached data
|
||||
* @param disassembledKey
|
||||
* @param type The Hibernate type mapping
|
||||
* @param entityOrRoleName The entity or collection-role name.
|
||||
* @param tenantId The tenant identifier associated with this data.
|
||||
* @param factory The session factory for which we are caching
|
||||
*/
|
||||
@Internal
|
||||
public CacheKeyImplementation(
|
||||
final Object id,
|
||||
final Serializable disassembledKey,
|
||||
final Type type,
|
||||
final String entityOrRoleName,
|
||||
final String tenantId,
|
||||
final SessionFactoryImplementor factory) {
|
||||
this.id = type.disassemble( id, factory );
|
||||
final String tenantId) {
|
||||
assert entityOrRoleName != null;
|
||||
this.id = disassembledKey;
|
||||
this.entityOrRoleName = entityOrRoleName;
|
||||
this.tenantId = tenantId;
|
||||
this.tenantId = tenantId; //might actually be null
|
||||
this.hashCode = calculateHashCode( id, type, tenantId );
|
||||
this.requiresDeepEquals = disassembledKey.getClass().isArray();
|
||||
}
|
||||
|
||||
private static int calculateHashCode(Object id, Type type, String tenantId) {
|
||||
|
@ -69,17 +77,30 @@ public final class CacheKeyImplementation implements Serializable {
|
|||
if ( other == null ) {
|
||||
return false;
|
||||
}
|
||||
if ( this == other ) {
|
||||
else if ( this == other ) {
|
||||
return true;
|
||||
}
|
||||
if ( hashCode != other.hashCode() || !( other instanceof CacheKeyImplementation ) ) {
|
||||
//hashCode is part of this check since it is pre-calculated and hash must match for equals to be true
|
||||
else if ( other.getClass() != CacheKeyImplementation.class ) {
|
||||
return false;
|
||||
}
|
||||
final CacheKeyImplementation that = (CacheKeyImplementation) other;
|
||||
return entityOrRoleName.equals( that.entityOrRoleName )
|
||||
&& Objects.deepEquals( id, that.id )
|
||||
&& Objects.equals( tenantId, that.tenantId );
|
||||
else {
|
||||
CacheKeyImplementation o = (CacheKeyImplementation) other;
|
||||
//check this first, so we can short-cut following checks in a different order
|
||||
if ( requiresDeepEquals ) {
|
||||
//only in this case, leverage the hashcode comparison check first;
|
||||
//this is typically unnecessary, still far cheaper than the other checks we need to perform
|
||||
//so it should be worth it.
|
||||
return this.hashCode == o.hashCode &&
|
||||
entityOrRoleName.equals( o.entityOrRoleName ) &&
|
||||
Objects.equals( this.tenantId, o.tenantId ) &&
|
||||
Objects.deepEquals( this.id, o.id );
|
||||
}
|
||||
else {
|
||||
return this.id.equals( o.id ) &&
|
||||
entityOrRoleName.equals( o.entityOrRoleName ) &&
|
||||
( this.tenantId != null ? this.tenantId.equals( o.tenantId ) : o.tenantId == null );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -6,11 +6,14 @@
|
|||
*/
|
||||
package org.hibernate.cache.internal;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.cache.spi.CacheKeysFactory;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Second level cache providers now have the option to use custom key implementations.
|
||||
|
@ -43,11 +46,27 @@ public class DefaultCacheKeysFactory implements CacheKeysFactory {
|
|||
public static final DefaultCacheKeysFactory INSTANCE = new DefaultCacheKeysFactory();
|
||||
|
||||
public static Object staticCreateCollectionKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
|
||||
return new CacheKeyImplementation( id, persister.getKeyType(), persister.getRole(), tenantIdentifier, factory );
|
||||
final Type keyType = persister.getKeyType();
|
||||
final Serializable disassembledKey = keyType.disassemble( id, factory );
|
||||
final boolean idIsArray = disassembledKey.getClass().isArray();
|
||||
if ( tenantIdentifier == null && ! idIsArray ) {
|
||||
return new BasicCacheKeyImplementation( id, disassembledKey, keyType, persister.getRole() );
|
||||
}
|
||||
else {
|
||||
return new CacheKeyImplementation( id, disassembledKey, keyType, persister.getRole(), tenantIdentifier );
|
||||
}
|
||||
}
|
||||
|
||||
public static Object staticCreateEntityKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
|
||||
return new CacheKeyImplementation( id, persister.getIdentifierType(), persister.getRootEntityName(), tenantIdentifier, factory );
|
||||
final Type keyType = persister.getIdentifierType();
|
||||
final Serializable disassembledKey = keyType.disassemble( id, factory );
|
||||
final boolean idIsArray = disassembledKey.getClass().isArray();
|
||||
if ( tenantIdentifier == null && ! idIsArray ) {
|
||||
return new BasicCacheKeyImplementation( id, disassembledKey, keyType, persister.getRootEntityName() );
|
||||
}
|
||||
else {
|
||||
return new CacheKeyImplementation( id, disassembledKey, keyType, persister.getRootEntityName(), tenantIdentifier );
|
||||
}
|
||||
}
|
||||
|
||||
public static Object staticCreateNaturalIdKey(Object naturalIdValues, EntityPersister persister, SharedSessionContractImplementor session) {
|
||||
|
@ -55,44 +74,49 @@ public class DefaultCacheKeysFactory implements CacheKeysFactory {
|
|||
}
|
||||
|
||||
public static Object staticGetEntityId(Object cacheKey) {
|
||||
return ((CacheKeyImplementation) cacheKey).getId();
|
||||
if ( cacheKey.getClass() == BasicCacheKeyImplementation.class ) {
|
||||
return ( (BasicCacheKeyImplementation) cacheKey ).id;
|
||||
}
|
||||
else {
|
||||
return ( (CacheKeyImplementation) cacheKey ).getId();
|
||||
}
|
||||
}
|
||||
|
||||
public static Object staticGetCollectionId(Object cacheKey) {
|
||||
return ((CacheKeyImplementation) cacheKey).getId();
|
||||
return staticGetEntityId( cacheKey );
|
||||
}
|
||||
|
||||
public static Object staticGetNaturalIdValues(Object cacheKey) {
|
||||
return ((NaturalIdCacheKey) cacheKey).getNaturalIdValues();
|
||||
return ( (NaturalIdCacheKey) cacheKey ).getNaturalIdValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createCollectionKey(Object id, CollectionPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
|
||||
return staticCreateCollectionKey(id, persister, factory, tenantIdentifier);
|
||||
return staticCreateCollectionKey( id, persister, factory, tenantIdentifier );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createEntityKey(Object id, EntityPersister persister, SessionFactoryImplementor factory, String tenantIdentifier) {
|
||||
return staticCreateEntityKey(id, persister, factory, tenantIdentifier);
|
||||
return staticCreateEntityKey( id, persister, factory, tenantIdentifier );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createNaturalIdKey(Object naturalIdValues, EntityPersister persister, SharedSessionContractImplementor session) {
|
||||
return staticCreateNaturalIdKey(naturalIdValues, persister, session);
|
||||
return staticCreateNaturalIdKey( naturalIdValues, persister, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEntityId(Object cacheKey) {
|
||||
return staticGetEntityId(cacheKey);
|
||||
return staticGetEntityId( cacheKey );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getCollectionId(Object cacheKey) {
|
||||
return staticGetCollectionId(cacheKey);
|
||||
return staticGetCollectionId( cacheKey );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getNaturalIdValues(Object cacheKey) {
|
||||
return staticGetNaturalIdValues(cacheKey);
|
||||
return staticGetNaturalIdValues( cacheKey );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.hibernate.boot.MetadataSources;
|
|||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cache.internal.CacheKeyImplementation;
|
||||
import org.hibernate.cache.internal.DefaultCacheKeysFactory;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.service.spi.ServiceRegistryImplementor;
|
||||
|
@ -66,7 +67,7 @@ public class CacheKeyImplementationHashCodeTest {
|
|||
int id,
|
||||
EntityPersister persister,
|
||||
SessionFactoryImplementor sfi) {
|
||||
return new CacheKeyImplementation( id, persister.getIdentifierType(), persister.getRootEntityName(), null, sfi );
|
||||
return (CacheKeyImplementation) DefaultCacheKeysFactory.staticCreateEntityKey( id, persister, sfi, "tenant" );
|
||||
}
|
||||
|
||||
@Entity(name = "AnEntity")
|
||||
|
|
Loading…
Reference in New Issue