HHH-6974 - Add caching to new "load access" api for natural id loading

This commit is contained in:
Steve Ebersole 2012-02-07 09:25:50 -06:00
parent 1569e6194b
commit ae872ed898
6 changed files with 117 additions and 127 deletions

View File

@ -103,6 +103,7 @@ public interface Cache {
* *
* @param naturalIdClass The naturalId class. * @param naturalIdClass The naturalId class.
*/ */
@SuppressWarnings( {"UnusedDeclaration"})
public void evictNaturalIdRegion(Class naturalIdClass); public void evictNaturalIdRegion(Class naturalIdClass);
/** /**
@ -131,6 +132,7 @@ public interface Cache {
* *
* @return True if the underlying cache contains corresponding data; false otherwise. * @return True if the underlying cache contains corresponding data; false otherwise.
*/ */
@SuppressWarnings( {"UnusedDeclaration"})
public boolean containsCollection(String role, Serializable ownerIdentifier); public boolean containsCollection(String role, Serializable ownerIdentifier);
/** /**
@ -143,7 +145,7 @@ public interface Cache {
/** /**
* Evicts all entity data from the given region (i.e. evicts cached data * Evicts all entity data from the given region (i.e. evicts cached data
* for all of the specified c9ollection role). * for all of the specified collection role).
* *
* @param role The "collection role" (in form [owner-entity-name].[collection-property-name]). * @param role The "collection role" (in form [owner-entity-name].[collection-property-name]).
*/ */
@ -164,6 +166,7 @@ public interface Cache {
* *
* @return True if the underlying cache contains corresponding data; false otherwise. * @return True if the underlying cache contains corresponding data; false otherwise.
*/ */
@SuppressWarnings( {"UnusedDeclaration"})
public boolean containsQuery(String regionName); public boolean containsQuery(String regionName);
/** /**

View File

@ -1,7 +1,7 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as * Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc. * distributed under license by Red Hat Inc.
@ -24,48 +24,45 @@
package org.hibernate.cache.spi; package org.hibernate.cache.spi;
import java.io.Serializable; import java.io.Serializable;
import java.util.Arrays;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.compare.EqualsHelper;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.type.Type; import org.hibernate.type.Type;
/** /**
* Allows multiple entity classes / collection roles to be * Defines a key for caching natural identifier resolutions into the second level cache.
* stored in the same cache region. Also allows for composite
* keys which do not properly implement equals()/hashCode().
* *
* @author Gavin King * @author Eric Dalquist
* @author Steve Ebersole
*/ */
public class NaturalIdCacheKey implements Serializable { public class NaturalIdCacheKey implements Serializable {
private final Serializable[] naturalId; private final Serializable[] naturalIdValues;
private final Type[] naturalIdTypes;
private final String entityName; private final String entityName;
private final String tenantId; private final String tenantId;
private final int hashCode; private final int hashCode;
private final String toString; private final String toString;
/** /**
* Construct a new key for a collection or entity instance. * Construct a new key for a caching natural identifier resolutions into the second level cache.
* Note that an entity name should always be the root entity * Note that an entity name should always be the root entity name, not a subclass entity name.
* name, not a subclass entity name.
* *
* @param naturalId The naturalId associated with the cached data * @param naturalIdValues The naturalIdValues associated with the cached data
* @param persister The persister for the entity * @param persister The persister for the entity
* @param session The session for which we are caching * @param session The originating session
*/ */
public NaturalIdCacheKey( public NaturalIdCacheKey(
final Object[] naturalId, final Object[] naturalIdValues,
final EntityPersister persister, final EntityPersister persister,
final SessionImplementor session) { final SessionImplementor session) {
this.entityName = persister.getEntityName(); this.entityName = persister.getRootEntityName();
this.tenantId = session.getTenantIdentifier(); this.tenantId = session.getTenantIdentifier();
final Serializable[] disassembledNaturalId = new Serializable[naturalId.length]; final Serializable[] disassembledNaturalId = new Serializable[naturalIdValues.length];
final Type[] naturalIdTypes = new Type[naturalId.length]; final StringBuilder toStringBuilder = new StringBuilder( entityName ).append( "##NaturalId[" );
final StringBuilder str = new StringBuilder(entityName).append( "##NaturalId[" );
final SessionFactoryImplementor factory = session.getFactory(); final SessionFactoryImplementor factory = session.getFactory();
final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties(); final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties();
@ -75,39 +72,39 @@ public class NaturalIdCacheKey implements Serializable {
int result = 1; int result = 1;
result = prime * result + ( ( this.entityName == null ) ? 0 : this.entityName.hashCode() ); result = prime * result + ( ( this.entityName == null ) ? 0 : this.entityName.hashCode() );
result = prime * result + ( ( this.tenantId == null ) ? 0 : this.tenantId.hashCode() ); result = prime * result + ( ( this.tenantId == null ) ? 0 : this.tenantId.hashCode() );
for ( int i = 0; i < naturalId.length; i++ ) { for ( int i = 0; i < naturalIdValues.length; i++ ) {
final Type type = propertyTypes[naturalIdPropertyIndexes[i]]; final Type type = propertyTypes[naturalIdPropertyIndexes[i]];
final Object value = naturalId[i]; final Object value = naturalIdValues[i];
result = prime * result + type.getHashCode( value, factory ); result = prime * result + type.getHashCode( value, factory );
disassembledNaturalId[i] = type.disassemble( value, session, null ); disassembledNaturalId[i] = type.disassemble( value, session, null );
naturalIdTypes[i] = type; toStringBuilder.append( type.toLoggableString( value, factory ) );
if (i + 1 < naturalIdValues.length) {
str.append( type.toLoggableString( value, factory ) ); toStringBuilder.append( ", " );
if (i + 1 < naturalId.length) {
str.append( ", " );
} }
} }
str.append( "]" ); toStringBuilder.append( "]" );
this.naturalId = disassembledNaturalId; this.naturalIdValues = disassembledNaturalId;
this.naturalIdTypes = naturalIdTypes;
this.hashCode = result; this.hashCode = result;
this.toString = str.toString(); this.toString = toStringBuilder.toString();
} }
@SuppressWarnings( {"UnusedDeclaration"})
public String getEntityName() { public String getEntityName() {
return entityName; return entityName;
} }
@SuppressWarnings( {"UnusedDeclaration"})
public String getTenantId() { public String getTenantId() {
return tenantId; return tenantId;
} }
public Serializable[] getNaturalId() { @SuppressWarnings( {"UnusedDeclaration"})
return naturalId; public Serializable[] getNaturalIdValues() {
return naturalIdValues;
} }
@Override @Override
@ -121,38 +118,18 @@ public class NaturalIdCacheKey implements Serializable {
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object o) {
if ( this == obj ) if ( this == o ) {
return true;
if ( obj == null )
return false;
if ( getClass() != obj.getClass() )
return false;
NaturalIdCacheKey other = (NaturalIdCacheKey) obj;
if ( entityName == null ) {
if ( other.entityName != null )
return false;
}
else if ( !entityName.equals( other.entityName ) )
return false;
if ( tenantId == null ) {
if ( other.tenantId != null )
return false;
}
else if ( !tenantId.equals( other.tenantId ) )
return false;
if ( naturalId == other.naturalId )
return true;
if ( naturalId == null || other.naturalId == null )
return false;
int length = naturalId.length;
if ( other.naturalId.length != length )
return false;
for ( int i = 0; i < length; i++ ) {
if ( !this.naturalIdTypes[i].isEqual( naturalId[i], other.naturalId[i] ) ) {
return false;
}
}
return true; return true;
} }
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final NaturalIdCacheKey other = (NaturalIdCacheKey) o;
return entityName.equals( other.entityName )
&& EqualsHelper.equals( tenantId, other.tenantId )
&& Arrays.equals( naturalIdValues, other.naturalIdValues );
}
} }

View File

@ -1,7 +1,7 @@
/* /*
* Hibernate, Relational Persistence for Idiomatic Java * Hibernate, Relational Persistence for Idiomatic Java
* *
* Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as * Copyright (c) 2012, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution * indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are * statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc. * distributed under license by Red Hat Inc.
@ -32,6 +32,7 @@ import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
* store naturalId data. * store naturalId data.
* *
* @author Eric Dalquist * @author Eric Dalquist
* @author Steve Ebersole
*/ */
public interface NaturalIdRegion extends TransactionalDataRegion { public interface NaturalIdRegion extends TransactionalDataRegion {

View File

@ -30,13 +30,12 @@ import org.hibernate.engine.spi.TypedValue;
/** /**
* @author Gavin King * @author Gavin King
* @deprecated Use {@link Session#byNaturalId(Class)} *
* @see Session#byNaturalId(Class) * @see Session#byNaturalId(Class)
* @see Session#byNaturalId(String) * @see Session#byNaturalId(String)
* @see Session#bySimpleNaturalId(Class) * @see Session#bySimpleNaturalId(Class)
* @see Session#bySimpleNaturalId(String) * @see Session#bySimpleNaturalId(String)
*/ */
@Deprecated
public class NaturalIdentifier implements Criterion { public class NaturalIdentifier implements Criterion {
private Junction conjunction = new Conjunction(); private Junction conjunction = new Conjunction();

View File

@ -385,13 +385,14 @@ public class Restrictions {
} }
/** /**
* @deprecated Use {@link Session#byNaturalId(Class)} * Consider using any of the natural id based loading stuff from session instead, especially in cases
* where the restriction is the full set of natural id values.
*
* @see Session#byNaturalId(Class) * @see Session#byNaturalId(Class)
* @see Session#byNaturalId(String) * @see Session#byNaturalId(String)
* @see Session#bySimpleNaturalId(Class) * @see Session#bySimpleNaturalId(Class)
* @see Session#bySimpleNaturalId(String) * @see Session#bySimpleNaturalId(String)
*/ */
@Deprecated
public static NaturalIdentifier naturalId() { public static NaturalIdentifier naturalId() {
return new NaturalIdentifier(); return new NaturalIdentifier();
} }

View File

@ -1785,22 +1785,19 @@ public class StatefulPersistenceContext implements PersistenceContext {
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if ( this == obj ) if ( this == obj ) {
return true; return true;
if ( obj == null ) }
return false; if ( obj == null ) {
if ( getClass() != obj.getClass() )
return false;
LocalNaturalIdCacheKey other = (LocalNaturalIdCacheKey) obj;
if ( persister == null ) {
if ( other.persister != null )
return false; return false;
} }
else if ( !persister.equals( other.persister ) ) if ( getClass() != obj.getClass() ) {
return false; return false;
if ( !Arrays.equals( values, other.values ) ) }
return false;
return true; final LocalNaturalIdCacheKey other = (LocalNaturalIdCacheKey) obj;
return persister.equals( other.persister )
&& Arrays.equals( values, other.values );
} }
} }
@ -1858,11 +1855,13 @@ public class StatefulPersistenceContext implements PersistenceContext {
// Found in session cache // Found in session cache
if ( pk != null ) { if ( pk != null ) {
if ( LOG.isTraceEnabled() ) if ( LOG.isTraceEnabled() ) {
LOG.tracev( LOG.trace(
"Resolved primary key in session cache: {0}", "Resolved natural key -> primary key resolution in session cache: " +
MessageHelper.infoString( persister, Arrays.toString( naturalIdValues ), persister.getRootEntityName() + "#[" +
session.getFactory() ) ); Arrays.toString( naturalIdValues ) + "]"
);
}
return pk; return pk;
} }
@ -1884,12 +1883,16 @@ public class StatefulPersistenceContext implements PersistenceContext {
if ( pk != null ) { if ( pk != null ) {
if ( factory.getStatistics().isStatisticsEnabled() ) { if ( factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatisticsImplementor().naturalIdCacheHit( factory.getStatisticsImplementor().naturalIdCacheHit(
naturalIdCacheAccessStrategy.getRegion().getName() ); naturalIdCacheAccessStrategy.getRegion().getName()
);
} }
if ( LOG.isTraceEnabled() ) if ( LOG.isTraceEnabled() )
LOG.tracev( "Resolved primary key in second-level cache: {0}", LOG.trace(
MessageHelper.infoString( persister, naturalIdCacheKey.getNaturalId(), session.getFactory() ) ); "Resolved natural key -> primary key resolution in second-level cache: " +
persister.getRootEntityName() + "#[" +
Arrays.toString( naturalIdValues ) + "]"
);
if ( entityNaturalIdResolutionCache == null ) { if ( entityNaturalIdResolutionCache == null ) {
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister ); entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
@ -1930,11 +1933,16 @@ public class StatefulPersistenceContext implements PersistenceContext {
switch ( valueSource ) { switch ( valueSource ) {
case LOAD: { case LOAD: {
final boolean put = naturalIdCacheAccessStrategy.putFromLoad( naturalIdCacheKey, pk, session.getTimestamp(), null ); final boolean put = naturalIdCacheAccessStrategy.putFromLoad(
naturalIdCacheKey,
pk,
session.getTimestamp(),
null
);
if ( put && factory.getStatistics().isStatisticsEnabled() ) { if ( put && factory.getStatistics().isStatisticsEnabled() ) {
factory.getStatisticsImplementor().naturalIdCachePut( factory.getStatisticsImplementor()
naturalIdCacheAccessStrategy.getRegion().getName() ); .naturalIdCachePut( naturalIdCacheAccessStrategy.getRegion().getName() );
} }
break; break;
@ -1953,13 +1961,13 @@ public class StatefulPersistenceContext implements PersistenceContext {
naturalIdCacheAccessStrategy.getRegion().getName() ); naturalIdCacheAccessStrategy.getRegion().getName() );
} }
} }
} ); }
);
break; break;
} }
case UPDATE: { case UPDATE: {
final SoftLock lock = naturalIdCacheAccessStrategy.lockItem( naturalIdCacheKey, null ); final SoftLock lock = naturalIdCacheAccessStrategy.lockItem( naturalIdCacheKey, null );
naturalIdCacheAccessStrategy.update( naturalIdCacheKey, pk ); naturalIdCacheAccessStrategy.update( naturalIdCacheKey, pk );
( (EventSource) this.session ).getActionQueue().registerProcess( ( (EventSource) this.session ).getActionQueue().registerProcess(
@ -1973,7 +1981,8 @@ public class StatefulPersistenceContext implements PersistenceContext {
naturalIdCacheAccessStrategy.getRegion().getName() ); naturalIdCacheAccessStrategy.getRegion().getName() );
} }
} }
} ); }
);
break; break;
} }