HHH-6974 Complete second level caching of natural id resolution
This commit is contained in:
parent
ef22e31068
commit
1569e6194b
|
@ -97,6 +97,27 @@ public interface Cache {
|
|||
*/
|
||||
public void evictEntityRegions();
|
||||
|
||||
/**
|
||||
* Evicts all naturalId data from the given region (i.e. for all entities of
|
||||
* type).
|
||||
*
|
||||
* @param naturalIdClass The naturalId class.
|
||||
*/
|
||||
public void evictNaturalIdRegion(Class naturalIdClass);
|
||||
|
||||
/**
|
||||
* Evicts all naturalId data from the given region (i.e. for all entities of
|
||||
* type).
|
||||
*
|
||||
* @param naturalIdName The naturalId name.
|
||||
*/
|
||||
public void evictNaturalIdRegion(String naturalIdName);
|
||||
|
||||
/**
|
||||
* Evict data from all naturalId regions.
|
||||
*/
|
||||
public void evictNaturalIdRegions();
|
||||
|
||||
/**
|
||||
* Determine whether the cache contains data for the given collection.
|
||||
* <p/>
|
||||
|
|
|
@ -24,9 +24,10 @@
|
|||
package org.hibernate.cache.spi;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
|
@ -38,7 +39,7 @@ import org.hibernate.type.Type;
|
|||
*/
|
||||
public class NaturalIdCacheKey implements Serializable {
|
||||
private final Serializable[] naturalId;
|
||||
private final Type[] types;
|
||||
private final Type[] naturalIdTypes;
|
||||
private final String entityName;
|
||||
private final String tenantId;
|
||||
private final int hashCode;
|
||||
|
@ -50,52 +51,68 @@ public class NaturalIdCacheKey implements Serializable {
|
|||
* name, not a subclass entity name.
|
||||
*
|
||||
* @param naturalId The naturalId associated with the cached data
|
||||
* @param types The Hibernate type mappings
|
||||
* @param entityOrRoleName The entity name.
|
||||
* @param tenantId The tenant identifier associated this data.
|
||||
* @param factory The session factory for which we are caching
|
||||
* @param persister The persister for the entity
|
||||
* @param session The session for which we are caching
|
||||
*/
|
||||
public NaturalIdCacheKey(
|
||||
final Serializable[] naturalId,
|
||||
final Type[] types,
|
||||
final String entityOrRoleName,
|
||||
final String tenantId,
|
||||
final SessionFactoryImplementor factory) {
|
||||
final Object[] naturalId,
|
||||
final EntityPersister persister,
|
||||
final SessionImplementor session) {
|
||||
|
||||
this.naturalId = naturalId;
|
||||
this.types = types;
|
||||
this.entityName = entityOrRoleName;
|
||||
this.tenantId = tenantId;
|
||||
this.entityName = persister.getEntityName();
|
||||
this.tenantId = session.getTenantIdentifier();
|
||||
|
||||
this.hashCode = this.generateHashCode( this.naturalId, this.types, factory );
|
||||
this.toString = entityOrRoleName + "##NaturalId" + Arrays.toString( this.naturalId );
|
||||
}
|
||||
final Serializable[] disassembledNaturalId = new Serializable[naturalId.length];
|
||||
final Type[] naturalIdTypes = new Type[naturalId.length];
|
||||
final StringBuilder str = new StringBuilder(entityName).append( "##NaturalId[" );
|
||||
|
||||
private int generateHashCode(final Serializable[] naturalId, final Type[] types,
|
||||
final SessionFactoryImplementor factory) {
|
||||
|
||||
final SessionFactoryImplementor factory = session.getFactory();
|
||||
final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties();
|
||||
final Type[] propertyTypes = persister.getPropertyTypes();
|
||||
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ( ( entityName == null ) ? 0 : entityName.hashCode() );
|
||||
result = prime * result + ( ( tenantId == null ) ? 0 : tenantId.hashCode() );
|
||||
result = prime * result + ( ( this.entityName == null ) ? 0 : this.entityName.hashCode() );
|
||||
result = prime * result + ( ( this.tenantId == null ) ? 0 : this.tenantId.hashCode() );
|
||||
for ( int i = 0; i < naturalId.length; i++ ) {
|
||||
result = prime * result + types[i].getHashCode( naturalId[i], factory );
|
||||
final Type type = propertyTypes[naturalIdPropertyIndexes[i]];
|
||||
final Object value = naturalId[i];
|
||||
|
||||
result = prime * result + type.getHashCode( value, factory );
|
||||
|
||||
disassembledNaturalId[i] = type.disassemble( value, session, null );
|
||||
|
||||
naturalIdTypes[i] = type;
|
||||
|
||||
str.append( type.toLoggableString( value, factory ) );
|
||||
if (i + 1 < naturalId.length) {
|
||||
str.append( ", " );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
str.append( "]" );
|
||||
|
||||
this.naturalId = disassembledNaturalId;
|
||||
this.naturalIdTypes = naturalIdTypes;
|
||||
this.hashCode = result;
|
||||
this.toString = str.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// Mainly for OSCache
|
||||
return this.toString;
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
public String getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public Serializable[] getNaturalId() {
|
||||
return naturalId;
|
||||
}
|
||||
|
||||
public Type[] getTypes() {
|
||||
return types;
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.toString;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -112,36 +129,30 @@ public class NaturalIdCacheKey implements Serializable {
|
|||
if ( getClass() != obj.getClass() )
|
||||
return false;
|
||||
NaturalIdCacheKey other = (NaturalIdCacheKey) obj;
|
||||
if (this.hashCode != other.hashCode) {
|
||||
//Short circuit on hashCode, if they aren't equal there is no chance the keys are equal
|
||||
return false;
|
||||
}
|
||||
if ( entityName == null ) {
|
||||
if ( other.entityName != null )
|
||||
return false;
|
||||
}
|
||||
else if ( !entityName.equals( other.entityName ) )
|
||||
return false;
|
||||
if ( !Arrays.equals( naturalId, other.naturalId ) )
|
||||
return false;
|
||||
if ( tenantId == null ) {
|
||||
if ( other.tenantId != null )
|
||||
return false;
|
||||
}
|
||||
else if ( !tenantId.equals( other.tenantId ) )
|
||||
return false;
|
||||
|
||||
for ( int i = 0; i < naturalId.length; i++ ) {
|
||||
if ( !types[i].isEqual( naturalId[i], other.naturalId[i] ) ) {
|
||||
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;
|
||||
}
|
||||
|
||||
public String getEntityOrRoleName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -575,7 +575,7 @@ public final class AnnotationBinder {
|
|||
entityBinder.setBatchSize( clazzToProcess.getAnnotation( BatchSize.class ) );
|
||||
entityBinder.setWhere( clazzToProcess.getAnnotation( Where.class ) );
|
||||
entityBinder.setCache( determineCacheSettings( clazzToProcess, mappings ) );
|
||||
entityBinder.setNaturalIdCache( clazzToProcess.getAnnotation( NaturalIdCache.class ) );
|
||||
entityBinder.setNaturalIdCache( clazzToProcess, clazzToProcess.getAnnotation( NaturalIdCache.class ) );
|
||||
|
||||
//Filters are not allowed on subclasses
|
||||
if ( !inheritanceState.hasParents() ) {
|
||||
|
|
|
@ -831,14 +831,14 @@ public class EntityBinder {
|
|||
}
|
||||
}
|
||||
|
||||
public void setNaturalIdCache(NaturalIdCache naturalIdCacheAnn) {
|
||||
public void setNaturalIdCache(XClass clazzToProcess, NaturalIdCache naturalIdCacheAnn) {
|
||||
if ( naturalIdCacheAnn != null ) {
|
||||
if ( BinderHelper.isEmptyAnnotationValue( naturalIdCacheAnn.region() ) ) {
|
||||
if (cacheRegion != null) {
|
||||
naturalIdCacheRegion = cacheRegion + NATURAL_ID_CACHE_SUFFIX;
|
||||
}
|
||||
else {
|
||||
naturalIdCacheRegion = persistentClass.getEntityName() + NATURAL_ID_CACHE_SUFFIX;
|
||||
naturalIdCacheRegion = clazzToProcess.getName() + NATURAL_ID_CACHE_SUFFIX;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -25,11 +25,18 @@
|
|||
package org.hibernate.criterion;
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.engine.spi.TypedValue;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
* @deprecated Use {@link Session#byNaturalId(Class)}
|
||||
* @see Session#byNaturalId(Class)
|
||||
* @see Session#byNaturalId(String)
|
||||
* @see Session#bySimpleNaturalId(Class)
|
||||
* @see Session#bySimpleNaturalId(String)
|
||||
*/
|
||||
@Deprecated
|
||||
public class NaturalIdentifier implements Criterion {
|
||||
|
||||
private Junction conjunction = new Conjunction();
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Collection;
|
|||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
|
@ -383,6 +384,14 @@ public class Restrictions {
|
|||
return new SizeExpression(propertyName, size, ">=");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link Session#byNaturalId(Class)}
|
||||
* @see Session#byNaturalId(Class)
|
||||
* @see Session#byNaturalId(String)
|
||||
* @see Session#bySimpleNaturalId(Class)
|
||||
* @see Session#bySimpleNaturalId(String)
|
||||
*/
|
||||
@Deprecated
|
||||
public static NaturalIdentifier naturalId() {
|
||||
return new NaturalIdentifier();
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import java.io.ObjectInputStream;
|
|||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
|
@ -62,6 +63,7 @@ import org.hibernate.engine.spi.EntityEntry;
|
|||
import org.hibernate.engine.spi.EntityKey;
|
||||
import org.hibernate.engine.spi.EntityUniqueKey;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.spi.Status;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
|
@ -74,7 +76,6 @@ import org.hibernate.pretty.MessageHelper;
|
|||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
import org.hibernate.tuple.ElementWrapper;
|
||||
import org.hibernate.type.Type;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
|
@ -1703,6 +1704,20 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
// NATURAL ID RESOLUTION HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@Override
|
||||
public void loadedStateInsertedNotification(EntityEntry entityEntry) {
|
||||
final EntityPersister persister = entityEntry.getPersister();
|
||||
if ( !persister.hasNaturalIdentifier() ) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
final Object[] naturalIdValues = getNaturalIdValues( entityEntry, persister );
|
||||
|
||||
// cache
|
||||
cacheNaturalIdResolution( persister, entityEntry.getId(), naturalIdValues, CachedNaturalIdValueSource.INSERT );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadedStateUpdatedNotification(EntityEntry entityEntry) {
|
||||
final EntityPersister persister = entityEntry.getPersister();
|
||||
|
@ -1731,20 +1746,6 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
evictNaturalIdResolution( persister, entityEntry.getId(), naturalIdValues );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadedStateInsertedNotification(EntityEntry entityEntry) {
|
||||
final EntityPersister persister = entityEntry.getPersister();
|
||||
if ( ! persister.hasNaturalIdentifier() ) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
final Object[] naturalIdValues = getNaturalIdValues( entityEntry, persister );
|
||||
|
||||
// cache
|
||||
cacheNaturalIdResolution( persister, entityEntry.getId(), naturalIdValues, CachedNaturalIdValueSource.INSERT );
|
||||
}
|
||||
|
||||
private Object[] getNaturalIdValues(EntityEntry entityEntry, EntityPersister persister) {
|
||||
final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties();
|
||||
final Object[] naturalIdValues = new Object[naturalIdPropertyIndexes.length];
|
||||
|
@ -1757,24 +1758,53 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
return naturalIdValues;
|
||||
}
|
||||
|
||||
private NaturalIdCacheKey getNaturalIdCacheKey(Object[] naturalIdValues, EntityPersister persister) {
|
||||
final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties();
|
||||
private static class LocalNaturalIdCacheKey {
|
||||
private final EntityPersister persister;
|
||||
private final Object[] values;
|
||||
private int hashCode;
|
||||
|
||||
final Serializable[] naturalIdKeyValues = new Serializable[naturalIdPropertyIndexes.length];
|
||||
final Type[] naturalIdTypes = new Type[naturalIdPropertyIndexes.length];
|
||||
public LocalNaturalIdCacheKey(EntityPersister persister, Object[] values) {
|
||||
this.persister = persister;
|
||||
this.values = values;
|
||||
|
||||
final Type[] propertyTypes = persister.getPropertyTypes();
|
||||
|
||||
for ( int i = 0; i < naturalIdPropertyIndexes.length; i++ ) {
|
||||
naturalIdTypes[i] = propertyTypes[naturalIdPropertyIndexes[i]];
|
||||
naturalIdKeyValues[i] = naturalIdTypes[i].disassemble( naturalIdValues[i], session, null );
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ( ( persister == null ) ? 0 : persister.hashCode() );
|
||||
result = prime * result + Arrays.hashCode( values );
|
||||
this.hashCode = result;
|
||||
}
|
||||
|
||||
return new NaturalIdCacheKey( naturalIdKeyValues, naturalIdTypes, persister.getEntityName(),
|
||||
session.getTenantIdentifier(), session.getFactory() );
|
||||
public Object[] getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
private class NaturalIdResolutionCache implements Serializable {
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if ( this == obj )
|
||||
return true;
|
||||
if ( obj == null )
|
||||
return false;
|
||||
if ( getClass() != obj.getClass() )
|
||||
return false;
|
||||
LocalNaturalIdCacheKey other = (LocalNaturalIdCacheKey) obj;
|
||||
if ( persister == null ) {
|
||||
if ( other.persister != null )
|
||||
return false;
|
||||
}
|
||||
else if ( !persister.equals( other.persister ) )
|
||||
return false;
|
||||
if ( !Arrays.equals( values, other.values ) )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class NaturalIdResolutionCache implements Serializable {
|
||||
private final EntityPersister persister;
|
||||
|
||||
private NaturalIdResolutionCache(EntityPersister persister) {
|
||||
|
@ -1785,13 +1815,20 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
return persister;
|
||||
}
|
||||
|
||||
private Map<Serializable,NaturalIdCacheKey> pkToNaturalIdMap = new ConcurrentHashMap<Serializable, NaturalIdCacheKey>();
|
||||
private Map<NaturalIdCacheKey,Serializable> naturalIdToPkMap = new ConcurrentHashMap<NaturalIdCacheKey,Serializable>();
|
||||
private Map<Serializable, LocalNaturalIdCacheKey> pkToNaturalIdMap = new ConcurrentHashMap<Serializable, LocalNaturalIdCacheKey>();
|
||||
private Map<LocalNaturalIdCacheKey, Serializable> naturalIdToPkMap = new ConcurrentHashMap<LocalNaturalIdCacheKey, Serializable>();
|
||||
}
|
||||
|
||||
private Map<EntityPersister,NaturalIdResolutionCache> naturalIdResolutionCacheMap
|
||||
= new ConcurrentHashMap<EntityPersister, NaturalIdResolutionCache>();
|
||||
private void validateNaturalId(EntityPersister persister, Object[] naturalIdValues) {
|
||||
if ( !persister.hasNaturalIdentifier() ) {
|
||||
throw new IllegalArgumentException( "Entity did not define a natrual-id" );
|
||||
}
|
||||
if ( persister.getNaturalIdentifierProperties().length != naturalIdValues.length ) {
|
||||
throw new IllegalArgumentException( "Mismatch between expected number of natural-id values and found." );
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<EntityPersister, NaturalIdResolutionCache> naturalIdResolutionCacheMap = new ConcurrentHashMap<EntityPersister, NaturalIdResolutionCache>();
|
||||
|
||||
@Override
|
||||
public Object[] findCachedNaturalId(EntityPersister persister, Serializable pk) {
|
||||
|
@ -1799,52 +1836,80 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
if ( entityNaturalIdResolutionCache == null ) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
final NaturalIdCacheKey naturalIdCacheKey = entityNaturalIdResolutionCache.pkToNaturalIdMap.get( pk );
|
||||
if (naturalIdCacheKey == null) {
|
||||
|
||||
final LocalNaturalIdCacheKey localNaturalIdCacheKey = entityNaturalIdResolutionCache.pkToNaturalIdMap.get( pk );
|
||||
if ( localNaturalIdCacheKey == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Serializable[] naturalIdKeyValues = naturalIdCacheKey.getNaturalId();
|
||||
final Type[] types = naturalIdCacheKey.getTypes();
|
||||
Object[] naturalIdValues = new Object[naturalIdKeyValues.length];
|
||||
for (int i = 0; i < naturalIdKeyValues.length; i++) {
|
||||
naturalIdValues[i] = types[i].assemble( naturalIdKeyValues[i], session, null );
|
||||
}
|
||||
|
||||
return naturalIdValues;
|
||||
}
|
||||
return localNaturalIdCacheKey.getValues();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable findCachedNaturalIdResolution(EntityPersister persister, Object[] naturalIdValues) {
|
||||
if ( ! persister.hasNaturalIdentifier() ) {
|
||||
throw new IllegalArgumentException( "Entity did not define a natrual-id" );
|
||||
validateNaturalId( persister, naturalIdValues );
|
||||
|
||||
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
||||
|
||||
Serializable pk;
|
||||
final LocalNaturalIdCacheKey localNaturalIdCacheKey = new LocalNaturalIdCacheKey( persister, naturalIdValues );
|
||||
if ( entityNaturalIdResolutionCache != null ) {
|
||||
pk = entityNaturalIdResolutionCache.naturalIdToPkMap.get( localNaturalIdCacheKey );
|
||||
|
||||
// Found in session cache
|
||||
if ( pk != null ) {
|
||||
if ( LOG.isTraceEnabled() )
|
||||
LOG.tracev(
|
||||
"Resolved primary key in session cache: {0}",
|
||||
MessageHelper.infoString( persister, Arrays.toString( naturalIdValues ),
|
||||
session.getFactory() ) );
|
||||
|
||||
return pk;
|
||||
}
|
||||
if ( persister.getNaturalIdentifierProperties().length != naturalIdValues.length ) {
|
||||
throw new IllegalArgumentException( "Mismatch between expected number of natural-id values and found." );
|
||||
}
|
||||
|
||||
final NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
||||
final NaturalIdCacheKey naturalIdCacheKey = getNaturalIdCacheKey( naturalIdValues, persister );
|
||||
// Session cache miss, see if second-level caching is enabled
|
||||
if ( !persister.hasNaturalIdCache() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Try resolution from second-level cache
|
||||
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( naturalIdValues, persister, session );
|
||||
|
||||
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
|
||||
pk = (Serializable) naturalIdCacheAccessStrategy.get( naturalIdCacheKey, session.getTimestamp() );
|
||||
|
||||
// Found in second-level cache, store in session cache
|
||||
final SessionFactoryImplementor factory = getSession().getFactory();
|
||||
if ( pk != null ) {
|
||||
if ( factory.getStatistics().isStatisticsEnabled() ) {
|
||||
factory.getStatisticsImplementor().naturalIdCacheHit(
|
||||
naturalIdCacheAccessStrategy.getRegion().getName() );
|
||||
}
|
||||
|
||||
if ( LOG.isTraceEnabled() )
|
||||
LOG.tracev( "Resolved primary key in second-level cache: {0}",
|
||||
MessageHelper.infoString( persister, naturalIdCacheKey.getNaturalId(), session.getFactory() ) );
|
||||
|
||||
if ( entityNaturalIdResolutionCache == null ) {
|
||||
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister
|
||||
.getNaturalIdCacheAccessStrategy();
|
||||
return (Serializable) naturalIdCacheAccessStrategy.get( naturalIdCacheKey, session.getTimestamp() );
|
||||
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
|
||||
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
|
||||
}
|
||||
else {
|
||||
return entityNaturalIdResolutionCache.naturalIdToPkMap.get( naturalIdCacheKey );
|
||||
|
||||
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, localNaturalIdCacheKey );
|
||||
entityNaturalIdResolutionCache.naturalIdToPkMap.put( localNaturalIdCacheKey, pk );
|
||||
}
|
||||
else if ( factory.getStatistics().isStatisticsEnabled() ) {
|
||||
factory.getStatisticsImplementor().naturalIdCacheMiss( naturalIdCacheAccessStrategy.getRegion().getName() );
|
||||
}
|
||||
|
||||
return pk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cacheNaturalIdResolution(EntityPersister persister, final Serializable pk, Object[] naturalIdValues, CachedNaturalIdValueSource valueSource ) {
|
||||
if ( ! persister.hasNaturalIdentifier() ) {
|
||||
throw new IllegalArgumentException( "Entity did not define a natural-id" );
|
||||
}
|
||||
if ( persister.getNaturalIdentifierProperties().length != naturalIdValues.length ) {
|
||||
throw new IllegalArgumentException( "Mismatch between expected number of natural-id values and found." );
|
||||
}
|
||||
public void cacheNaturalIdResolution(EntityPersister persister, final Serializable pk, Object[] naturalIdValues,
|
||||
CachedNaturalIdValueSource valueSource) {
|
||||
validateNaturalId( persister, naturalIdValues );
|
||||
|
||||
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
||||
if ( entityNaturalIdResolutionCache == null ) {
|
||||
|
@ -1852,24 +1917,41 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
|
||||
}
|
||||
|
||||
final NaturalIdCacheKey naturalIdCacheKey = getNaturalIdCacheKey( naturalIdValues, persister );
|
||||
|
||||
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, naturalIdCacheKey );
|
||||
entityNaturalIdResolutionCache.naturalIdToPkMap.put( naturalIdCacheKey, pk );
|
||||
final LocalNaturalIdCacheKey localNaturalIdCacheKey = new LocalNaturalIdCacheKey( persister, naturalIdValues );
|
||||
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, localNaturalIdCacheKey );
|
||||
entityNaturalIdResolutionCache.naturalIdToPkMap.put( localNaturalIdCacheKey, pk );
|
||||
|
||||
//If second-level caching is enabled cache the resolution there as well
|
||||
if ( persister.hasNaturalIdCache() ) {
|
||||
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
|
||||
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( naturalIdValues, persister, session );
|
||||
|
||||
final SessionFactoryImplementor factory = getSession().getFactory();
|
||||
|
||||
switch ( valueSource ) {
|
||||
case LOAD: {
|
||||
naturalIdCacheAccessStrategy.putFromLoad( naturalIdCacheKey, pk, session.getTimestamp(), null );
|
||||
final boolean put = naturalIdCacheAccessStrategy.putFromLoad( naturalIdCacheKey, pk, session.getTimestamp(), null );
|
||||
|
||||
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
||||
factory.getStatisticsImplementor().naturalIdCachePut(
|
||||
naturalIdCacheAccessStrategy.getRegion().getName() );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case INSERT: {
|
||||
naturalIdCacheAccessStrategy.insert( naturalIdCacheKey, pk );
|
||||
|
||||
( (EventSource) this.session ).getActionQueue().registerProcess( new AfterTransactionCompletionProcess() {
|
||||
( (EventSource) this.session ).getActionQueue().registerProcess(
|
||||
new AfterTransactionCompletionProcess() {
|
||||
@Override
|
||||
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
|
||||
naturalIdCacheAccessStrategy.afterInsert( naturalIdCacheKey, pk );
|
||||
final boolean put = naturalIdCacheAccessStrategy.afterInsert( naturalIdCacheKey, pk );
|
||||
|
||||
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
||||
factory.getStatisticsImplementor().naturalIdCachePut(
|
||||
naturalIdCacheAccessStrategy.getRegion().getName() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
|
@ -1880,10 +1962,16 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
naturalIdCacheAccessStrategy.update( naturalIdCacheKey, pk );
|
||||
|
||||
( (EventSource) this.session ).getActionQueue().registerProcess( new AfterTransactionCompletionProcess() {
|
||||
( (EventSource) this.session ).getActionQueue().registerProcess(
|
||||
new AfterTransactionCompletionProcess() {
|
||||
@Override
|
||||
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
|
||||
naturalIdCacheAccessStrategy.afterUpdate( naturalIdCacheKey, pk, lock );
|
||||
final boolean put = naturalIdCacheAccessStrategy.afterUpdate( naturalIdCacheKey, pk, lock );
|
||||
|
||||
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
||||
factory.getStatisticsImplementor().naturalIdCachePut(
|
||||
naturalIdCacheAccessStrategy.getRegion().getName() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
|
@ -1891,6 +1979,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictNaturalIdResolution(EntityPersister persister, final Serializable pk, Object[] naturalIdValues) {
|
||||
|
@ -1902,16 +1991,17 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
}
|
||||
|
||||
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
||||
if ( entityNaturalIdResolutionCache == null ) {
|
||||
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
|
||||
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
|
||||
if ( entityNaturalIdResolutionCache != null ) {
|
||||
final LocalNaturalIdCacheKey localNaturalIdCacheKey = new LocalNaturalIdCacheKey( persister,
|
||||
naturalIdValues );
|
||||
entityNaturalIdResolutionCache.pkToNaturalIdMap.remove( pk );
|
||||
entityNaturalIdResolutionCache.naturalIdToPkMap.remove( localNaturalIdCacheKey );
|
||||
}
|
||||
|
||||
final NaturalIdCacheKey naturalIdCacheKey = getNaturalIdCacheKey( naturalIdValues, persister );
|
||||
entityNaturalIdResolutionCache.pkToNaturalIdMap.remove( pk ); //Should I compare the value returned here with the naturalIdCacheKey parameter?
|
||||
entityNaturalIdResolutionCache.naturalIdToPkMap.remove( naturalIdCacheKey );
|
||||
|
||||
if ( persister.hasNaturalIdCache() ) {
|
||||
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
|
||||
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( naturalIdValues, persister, session );
|
||||
naturalIdCacheAccessStrategy.evict( naturalIdCacheKey );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,36 +72,22 @@ public class DefaultResolveNaturalIdEventListener
|
|||
protected Serializable resolveNaturalId(final ResolveNaturalIdEvent event) {
|
||||
final EntityPersister persister = event.getEntityPersister();
|
||||
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.trace(
|
||||
"Attempting to resolve: " +
|
||||
MessageHelper.infoString(
|
||||
persister, event.getNaturalIdValues(), event.getSession().getFactory()
|
||||
)
|
||||
);
|
||||
}
|
||||
final boolean traceEnabled = LOG.isTraceEnabled();
|
||||
if ( traceEnabled )
|
||||
LOG.tracev( "Attempting to resolve: {0}",
|
||||
MessageHelper.infoString( persister, event.getNaturalIdValues(), event.getSession().getFactory() ) );
|
||||
|
||||
Serializable entityId = resolveFromSessionCache( event );
|
||||
Serializable entityId = resolveFromCache( event );
|
||||
if ( entityId != null ) {
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.trace(
|
||||
"Resolved object in session cache: " +
|
||||
MessageHelper.infoString(
|
||||
persister, event.getNaturalIdValues(), event.getSession().getFactory()
|
||||
)
|
||||
);
|
||||
}
|
||||
if ( traceEnabled )
|
||||
LOG.tracev( "Resolved object in cache: {0}",
|
||||
MessageHelper.infoString( persister, event.getNaturalIdValues(), event.getSession().getFactory() ) );
|
||||
return entityId;
|
||||
}
|
||||
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.trace(
|
||||
"Object not resolved in any cache: " +
|
||||
MessageHelper.infoString(
|
||||
persister, event.getNaturalIdValues(), event.getSession().getFactory()
|
||||
)
|
||||
);
|
||||
}
|
||||
if ( traceEnabled )
|
||||
LOG.tracev( "Object not resolved in any cache: {0}",
|
||||
MessageHelper.infoString( persister, event.getNaturalIdValues(), event.getSession().getFactory() ) );
|
||||
|
||||
return loadFromDatasource( event );
|
||||
}
|
||||
|
@ -111,9 +97,9 @@ public class DefaultResolveNaturalIdEventListener
|
|||
*
|
||||
* @param event The load event
|
||||
*
|
||||
* @return The entity from the session-level cache, or null.
|
||||
* @return The entity from the cache, or null.
|
||||
*/
|
||||
protected Serializable resolveFromSessionCache(final ResolveNaturalIdEvent event) {
|
||||
protected Serializable resolveFromCache(final ResolveNaturalIdEvent event) {
|
||||
return event.getSession().getPersistenceContext().findCachedNaturalIdResolution(
|
||||
event.getEntityPersister(),
|
||||
event.getOrderedNaturalIdValues()
|
||||
|
|
|
@ -1487,6 +1487,26 @@ public final class SessionFactoryImpl
|
|||
}
|
||||
}
|
||||
|
||||
public void evictNaturalIdRegion(Class entityClass) {
|
||||
evictNaturalIdRegion( entityClass.getName() );
|
||||
}
|
||||
|
||||
public void evictNaturalIdRegion(String entityName) {
|
||||
EntityPersister p = getEntityPersister( entityName );
|
||||
if ( p.hasNaturalIdCache() ) {
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
LOG.debugf( "Evicting second-level cache: %s", p.getEntityName() );
|
||||
}
|
||||
p.getNaturalIdCacheAccessStrategy().evictAll();
|
||||
}
|
||||
}
|
||||
|
||||
public void evictNaturalIdRegions() {
|
||||
for ( String s : entityPersisters.keySet() ) {
|
||||
evictNaturalIdRegion( s );
|
||||
}
|
||||
}
|
||||
|
||||
public boolean containsCollection(String role, Serializable ownerIdentifier) {
|
||||
CollectionPersister p = getCollectionPersister( role );
|
||||
return p.hasCache() &&
|
||||
|
|
|
@ -3902,6 +3902,10 @@ public abstract class AbstractEntityPersister
|
|||
return cacheEntryStructure;
|
||||
}
|
||||
|
||||
public boolean hasNaturalIdCache() {
|
||||
return naturalIdRegionAccessStrategy != null;
|
||||
}
|
||||
|
||||
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
|
||||
return naturalIdRegionAccessStrategy;
|
||||
}
|
||||
|
|
|
@ -475,6 +475,11 @@ public interface EntityPersister extends OptimisticCacheSource {
|
|||
*/
|
||||
public CacheEntryStructure getCacheEntryStructure();
|
||||
|
||||
/**
|
||||
* Does this class have a natural id cache
|
||||
*/
|
||||
public boolean hasNaturalIdCache();
|
||||
|
||||
/**
|
||||
* Get the NaturalId cache (optional operation)
|
||||
*/
|
||||
|
|
|
@ -63,6 +63,7 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
|
|||
public static final Class[] ENTITY_PERSISTER_CONSTRUCTOR_ARGS = new Class[] {
|
||||
PersistentClass.class,
|
||||
EntityRegionAccessStrategy.class,
|
||||
NaturalIdRegionAccessStrategy.class,
|
||||
SessionFactoryImplementor.class,
|
||||
Mapping.class
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@ package org.hibernate.test.annotations.naturalid;
|
|||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
|
@ -35,12 +36,14 @@ import javax.persistence.Version;
|
|||
import org.hibernate.annotations.Cache;
|
||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.annotations.NaturalIdCache;
|
||||
|
||||
/**
|
||||
* @author Guenther Demetz
|
||||
*/
|
||||
@Entity
|
||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||
@NaturalIdCache
|
||||
public class A {
|
||||
|
||||
@Id
|
||||
|
|
|
@ -6,11 +6,13 @@ import javax.persistence.Id;
|
|||
import javax.persistence.ManyToOne;
|
||||
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.annotations.NaturalIdCache;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
@Entity
|
||||
@NaturalIdCache
|
||||
public class Citizen {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
|
|
|
@ -114,7 +114,7 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalIdLoaderCached() {
|
||||
public void testNaturalIdLoaderNotCached() {
|
||||
saveSomeCitizens();
|
||||
|
||||
Session s = openSession();
|
||||
|
@ -123,6 +123,8 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
|
|||
final NaturalIdLoadAccess naturalIdLoader = s.byNaturalId( Citizen.class );
|
||||
naturalIdLoader.using( "ssn", "1234" ).using( "state", france );
|
||||
|
||||
//NaturalId cache gets populated during entity loading, need to clear it out
|
||||
sessionFactory().getCache().evictNaturalIdRegions();
|
||||
Statistics stats = sessionFactory().getStatistics();
|
||||
stats.setStatisticsEnabled( true );
|
||||
stats.clear();
|
||||
|
@ -147,13 +149,58 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
|
|||
.getNaturalIdCachePutCount()
|
||||
);
|
||||
|
||||
// query a second time - result should be cached
|
||||
citizen = (Citizen)naturalIdLoader.load();
|
||||
// cleanup
|
||||
tx.rollback();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNaturalIdLoaderCached() {
|
||||
saveSomeCitizens();
|
||||
|
||||
Statistics stats = sessionFactory().getStatistics();
|
||||
assertEquals(
|
||||
"Cache hits should be empty", 0, stats
|
||||
.getNaturalIdCacheHitCount()
|
||||
);
|
||||
assertEquals(
|
||||
"First load should be a miss", 1, stats
|
||||
.getNaturalIdCacheMissCount()
|
||||
);
|
||||
assertEquals(
|
||||
"Query result should be added to cache", 3, stats
|
||||
.getNaturalIdCachePutCount()
|
||||
);
|
||||
|
||||
Session s = openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
State france = ( State ) s.load( State.class, 2 );
|
||||
final NaturalIdLoadAccess naturalIdLoader = s.byNaturalId( Citizen.class );
|
||||
naturalIdLoader.using( "ssn", "1234" ).using( "state", france );
|
||||
|
||||
//Not clearing naturalId caches, should be warm from entity loading
|
||||
stats.setStatisticsEnabled( true );
|
||||
stats.clear();
|
||||
assertEquals(
|
||||
"Cache hits should be empty", 0, stats
|
||||
.getQueryCacheHitCount()
|
||||
);
|
||||
|
||||
// first query
|
||||
Citizen citizen = (Citizen)naturalIdLoader.load();
|
||||
assertNotNull( citizen );
|
||||
assertEquals(
|
||||
"Cache hits should be empty", 1, stats
|
||||
.getNaturalIdCacheHitCount()
|
||||
);
|
||||
assertEquals(
|
||||
"First load should be a miss", 0, stats
|
||||
.getNaturalIdCacheMissCount()
|
||||
);
|
||||
assertEquals(
|
||||
"Query result should be added to cache", 0, stats
|
||||
.getNaturalIdCachePutCount()
|
||||
);
|
||||
|
||||
// cleanup
|
||||
tx.rollback();
|
||||
|
|
|
@ -87,6 +87,7 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
|
||||
public NoopEntityPersister(org.hibernate.mapping.PersistentClass persistentClass,
|
||||
org.hibernate.cache.spi.access.EntityRegionAccessStrategy strategy,
|
||||
NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
|
||||
SessionFactoryImplementor sf,
|
||||
Mapping mapping) {
|
||||
throw new GoofyException(NoopEntityPersister.class);
|
||||
|
@ -383,6 +384,11 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNaturalIdCache() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
|
||||
return null;
|
||||
|
|
|
@ -47,6 +47,7 @@ public class CustomPersister implements EntityPersister {
|
|||
public CustomPersister(
|
||||
PersistentClass model,
|
||||
EntityRegionAccessStrategy cacheAccessStrategy,
|
||||
NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
|
||||
SessionFactoryImplementor factory,
|
||||
Mapping mapping) {
|
||||
this.factory = factory;
|
||||
|
@ -443,6 +444,10 @@ public class CustomPersister implements EntityPersister {
|
|||
return null;
|
||||
}
|
||||
|
||||
public boolean hasNaturalIdCache() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -54,6 +54,50 @@ public class NonstopAwareNaturalIdRegionAccessStrategy implements NaturalIdRegio
|
|||
this.hibernateNonstopExceptionHandler = hibernateNonstopExceptionHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insert(Object key, Object value) throws CacheException {
|
||||
try {
|
||||
return actualStrategy.insert( key, value );
|
||||
}
|
||||
catch ( NonStopCacheException nonStopCacheException ) {
|
||||
hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean afterInsert(Object key, Object value) throws CacheException {
|
||||
try {
|
||||
return actualStrategy.afterInsert( key, value );
|
||||
}
|
||||
catch ( NonStopCacheException nonStopCacheException ) {
|
||||
hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(Object key, Object value) throws CacheException {
|
||||
try {
|
||||
return actualStrategy.update( key, value );
|
||||
}
|
||||
catch ( NonStopCacheException nonStopCacheException ) {
|
||||
hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws CacheException {
|
||||
try {
|
||||
return actualStrategy.afterUpdate( key, value, lock );
|
||||
}
|
||||
catch ( NonStopCacheException nonStopCacheException ) {
|
||||
hibernateNonstopExceptionHandler.handleNonstopCacheException( nonStopCacheException );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
|
|
|
@ -89,6 +89,36 @@ public class NonStrictReadWriteEhcacheNaturalIdRegionAccessStrategy
|
|||
region.remove( key );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>false</code> since this is an asynchronous cache access strategy.
|
||||
*/
|
||||
public boolean insert(Object key, Object value ) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>false</code> since this is a non-strict read/write cache access strategy
|
||||
*/
|
||||
public boolean afterInsert(Object key, Object value ) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the entry since this is a non-strict read/write cache strategy.
|
||||
*/
|
||||
public boolean update(Object key, Object value ) throws CacheException {
|
||||
remove( key );
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws CacheException {
|
||||
unlockItem( key, lock );
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
|
|
@ -90,4 +90,37 @@ public class ReadOnlyEhcacheNaturalIdRegionAccessStrategy
|
|||
public void unlockItem(Object key, SoftLock lock) throws CacheException {
|
||||
//throw new UnsupportedOperationException("Can't write to a readonly object");
|
||||
}
|
||||
|
||||
/**
|
||||
* This cache is asynchronous hence a no-op
|
||||
*/
|
||||
public boolean insert(Object key, Object value ) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean afterInsert(Object key, Object value ) throws CacheException {
|
||||
region.put( key, value );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws UnsupportedOperationException since this cache is read-only
|
||||
*
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public boolean update(Object key, Object value ) throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException( "Can't write to a readonly object" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws UnsupportedOperationException since this cache is read-only
|
||||
*
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws UnsupportedOperationException {
|
||||
throw new UnsupportedOperationException( "Can't write to a readonly object" );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,11 @@
|
|||
*/
|
||||
package org.hibernate.cache.ehcache.internal.strategy;
|
||||
|
||||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.cache.ehcache.internal.regions.EhcacheNaturalIdRegion;
|
||||
import org.hibernate.cache.spi.NaturalIdRegion;
|
||||
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
||||
import org.hibernate.cache.spi.access.SoftLock;
|
||||
import org.hibernate.cfg.Settings;
|
||||
|
||||
/**
|
||||
|
@ -51,4 +53,75 @@ public class ReadWriteEhcacheNaturalIdRegionAccessStrategy
|
|||
public NaturalIdRegion getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* A no-op since this is an asynchronous cache access strategy.
|
||||
*/
|
||||
public boolean insert(Object key, Object value ) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p/>
|
||||
* Inserts will only succeed if there is no existing value mapped to this key.
|
||||
*/
|
||||
public boolean afterInsert(Object key, Object value ) throws CacheException {
|
||||
region.writeLock( key );
|
||||
try {
|
||||
Lockable item = (Lockable) region.get( key );
|
||||
if ( item == null ) {
|
||||
region.put( key, new Item( value, null, region.nextTimestamp() ) );
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
region.writeUnlock( key );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A no-op since this is an asynchronous cache access strategy.
|
||||
*/
|
||||
public boolean update(Object key, Object value )
|
||||
throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p/>
|
||||
* Updates will only succeed if this entry was locked by this transaction and exclusively this transaction for the
|
||||
* duration of this transaction. It is important to also note that updates will fail if the soft-lock expired during
|
||||
* the course of this transaction.
|
||||
*/
|
||||
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws CacheException {
|
||||
//what should we do with previousVersion here?
|
||||
region.writeLock( key );
|
||||
try {
|
||||
Lockable item = (Lockable) region.get( key );
|
||||
|
||||
if ( item != null && item.isUnlockable( lock ) ) {
|
||||
Lock lockItem = (Lock) item;
|
||||
if ( lockItem.wasLockedConcurrently() ) {
|
||||
decrementLock( key, lockItem );
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
region.put( key, new Item( value, null, region.nextTimestamp() ) );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
handleLockExpiry( key, item );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
region.writeUnlock( key );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -58,6 +58,20 @@ public class TransactionalEhcacheNaturalIdRegionAccessStrategy
|
|||
this.ehcache = ehcache;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean afterInsert(Object key, Object value ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean afterUpdate(Object key, Object value, SoftLock lock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
|
@ -79,6 +93,20 @@ public class TransactionalEhcacheNaturalIdRegionAccessStrategy
|
|||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean insert(Object key, Object value ) throws CacheException {
|
||||
//OptimisticCache? versioning?
|
||||
try {
|
||||
ehcache.put( new Element( key, value ) );
|
||||
return true;
|
||||
}
|
||||
catch ( net.sf.ehcache.CacheException e ) {
|
||||
throw new CacheException( e );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -124,4 +152,17 @@ public class TransactionalEhcacheNaturalIdRegionAccessStrategy
|
|||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean update(Object key, Object value ) throws CacheException {
|
||||
try {
|
||||
ehcache.put( new Element( key, value ) );
|
||||
return true;
|
||||
}
|
||||
catch ( net.sf.ehcache.CacheException e ) {
|
||||
throw new CacheException( e );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import javax.persistence.EntityManagerFactory;
|
|||
import javax.persistence.PersistenceException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
@ -36,6 +37,7 @@ import org.hibernate.LockOptions;
|
|||
import org.hibernate.MappingException;
|
||||
import org.hibernate.bytecode.spi.EntityInstrumentationMetadata;
|
||||
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
|
||||
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
||||
import org.hibernate.cache.spi.entry.CacheEntryStructure;
|
||||
import org.hibernate.ejb.Ejb3Configuration;
|
||||
import org.hibernate.engine.spi.CascadeStyle;
|
||||
|
@ -108,6 +110,7 @@ public class PersisterClassProviderTest {
|
|||
public GoofyProvider(
|
||||
org.hibernate.mapping.PersistentClass persistentClass,
|
||||
org.hibernate.cache.spi.access.EntityRegionAccessStrategy strategy,
|
||||
NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
|
||||
SessionFactoryImplementor sf,
|
||||
Mapping mapping) {
|
||||
throw new GoofyException();
|
||||
|
@ -274,6 +277,16 @@ public class PersisterClassProviderTest {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNaturalIdCache() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifierGenerator getIdentifierGenerator() {
|
||||
return null;
|
||||
|
|
|
@ -23,8 +23,10 @@
|
|||
*/
|
||||
package org.hibernate.testing.cache;
|
||||
|
||||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.cache.spi.NaturalIdRegion;
|
||||
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
||||
import org.hibernate.cache.spi.access.SoftLock;
|
||||
|
||||
/**
|
||||
* @author Eric Dalquist
|
||||
|
@ -47,6 +49,26 @@ class BaseNaturalIdRegionAccessStrategy extends BaseRegionAccessStrategy impleme
|
|||
return region;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insert(Object key, Object value ) throws CacheException {
|
||||
return putFromLoad( key, value, 0 , null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean afterInsert(Object key, Object value ) throws CacheException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(Object key, Object value ) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
BaseNaturalIdRegionAccessStrategy(NaturalIdRegionImpl region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
|
|
@ -25,8 +25,10 @@ package org.hibernate.testing.cache;
|
|||
|
||||
import java.util.Comparator;
|
||||
|
||||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.cache.spi.NaturalIdRegion;
|
||||
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
||||
import org.hibernate.cache.spi.access.SoftLock;
|
||||
|
||||
/**
|
||||
* @author Eric Dalquist
|
||||
|
@ -40,6 +42,63 @@ class ReadWriteNaturalIdRegionAccessStrategy extends AbstractReadWriteAccessStra
|
|||
this.region = region;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean insert(Object key, Object value ) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update(Object key, Object value ) throws CacheException {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean afterInsert(Object key, Object value ) throws CacheException {
|
||||
|
||||
try {
|
||||
writeLock.lock();
|
||||
Lockable item = (Lockable) region.get( key );
|
||||
if ( item == null ) {
|
||||
region.put( key, new Item( value, null, region.nextTimestamp() ) );
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws CacheException {
|
||||
try {
|
||||
writeLock.lock();
|
||||
Lockable item = (Lockable) region.get( key );
|
||||
|
||||
if ( item != null && item.isUnlockable( lock ) ) {
|
||||
Lock lockItem = (Lock) item;
|
||||
if ( lockItem.wasLockedConcurrently() ) {
|
||||
decrementLock( key, lockItem );
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
region.put( key, new Item( value, null, region.nextTimestamp() ) );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
handleLockExpiry( key, item );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Comparator getVersionComparator() {
|
||||
return region.getCacheDataDescription().getVersionComparator();
|
||||
|
|
Loading…
Reference in New Issue