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();
|
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.
|
* Determine whether the cache contains data for the given collection.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
|
|
@ -24,9 +24,10 @@
|
||||||
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.persister.entity.EntityPersister;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,7 +39,7 @@ import org.hibernate.type.Type;
|
||||||
*/
|
*/
|
||||||
public class NaturalIdCacheKey implements Serializable {
|
public class NaturalIdCacheKey implements Serializable {
|
||||||
private final Serializable[] naturalId;
|
private final Serializable[] naturalId;
|
||||||
private final Type[] types;
|
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;
|
||||||
|
@ -50,52 +51,68 @@ public class NaturalIdCacheKey implements Serializable {
|
||||||
* name, not a subclass entity name.
|
* name, not a subclass entity name.
|
||||||
*
|
*
|
||||||
* @param naturalId The naturalId associated with the cached data
|
* @param naturalId The naturalId associated with the cached data
|
||||||
* @param types The Hibernate type mappings
|
* @param persister The persister for the entity
|
||||||
* @param entityOrRoleName The entity name.
|
* @param session The session for which we are caching
|
||||||
* @param tenantId The tenant identifier associated this data.
|
|
||||||
* @param factory The session factory for which we are caching
|
|
||||||
*/
|
*/
|
||||||
public NaturalIdCacheKey(
|
public NaturalIdCacheKey(
|
||||||
final Serializable[] naturalId,
|
final Object[] naturalId,
|
||||||
final Type[] types,
|
final EntityPersister persister,
|
||||||
final String entityOrRoleName,
|
final SessionImplementor session) {
|
||||||
final String tenantId,
|
|
||||||
final SessionFactoryImplementor factory) {
|
|
||||||
|
|
||||||
this.naturalId = naturalId;
|
this.entityName = persister.getEntityName();
|
||||||
this.types = types;
|
this.tenantId = session.getTenantIdentifier();
|
||||||
this.entityName = entityOrRoleName;
|
|
||||||
this.tenantId = tenantId;
|
|
||||||
|
|
||||||
this.hashCode = this.generateHashCode( this.naturalId, this.types, factory );
|
final Serializable[] disassembledNaturalId = new Serializable[naturalId.length];
|
||||||
this.toString = entityOrRoleName + "##NaturalId" + Arrays.toString( this.naturalId );
|
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;
|
final int prime = 31;
|
||||||
int result = 1;
|
int result = 1;
|
||||||
result = prime * result + ( ( entityName == null ) ? 0 : entityName.hashCode() );
|
result = prime * result + ( ( this.entityName == null ) ? 0 : this.entityName.hashCode() );
|
||||||
result = prime * result + ( ( tenantId == null ) ? 0 : 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 < 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 getEntityName() {
|
||||||
public String toString() {
|
return entityName;
|
||||||
// Mainly for OSCache
|
}
|
||||||
return this.toString;
|
|
||||||
|
public String getTenantId() {
|
||||||
|
return tenantId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Serializable[] getNaturalId() {
|
public Serializable[] getNaturalId() {
|
||||||
return naturalId;
|
return naturalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type[] getTypes() {
|
@Override
|
||||||
return types;
|
public String toString() {
|
||||||
|
return this.toString;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -112,36 +129,30 @@ public class NaturalIdCacheKey implements Serializable {
|
||||||
if ( getClass() != obj.getClass() )
|
if ( getClass() != obj.getClass() )
|
||||||
return false;
|
return false;
|
||||||
NaturalIdCacheKey other = (NaturalIdCacheKey) obj;
|
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 ( entityName == null ) {
|
||||||
if ( other.entityName != null )
|
if ( other.entityName != null )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if ( !entityName.equals( other.entityName ) )
|
else if ( !entityName.equals( other.entityName ) )
|
||||||
return false;
|
return false;
|
||||||
if ( !Arrays.equals( naturalId, other.naturalId ) )
|
|
||||||
return false;
|
|
||||||
if ( tenantId == null ) {
|
if ( tenantId == null ) {
|
||||||
if ( other.tenantId != null )
|
if ( other.tenantId != null )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if ( !tenantId.equals( other.tenantId ) )
|
else if ( !tenantId.equals( other.tenantId ) )
|
||||||
return false;
|
return false;
|
||||||
|
if ( naturalId == other.naturalId )
|
||||||
for ( int i = 0; i < naturalId.length; i++ ) {
|
return true;
|
||||||
if ( !types[i].isEqual( naturalId[i], other.naturalId[i] ) ) {
|
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 false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEntityOrRoleName() {
|
|
||||||
return entityName;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -575,7 +575,7 @@ public final class AnnotationBinder {
|
||||||
entityBinder.setBatchSize( clazzToProcess.getAnnotation( BatchSize.class ) );
|
entityBinder.setBatchSize( clazzToProcess.getAnnotation( BatchSize.class ) );
|
||||||
entityBinder.setWhere( clazzToProcess.getAnnotation( Where.class ) );
|
entityBinder.setWhere( clazzToProcess.getAnnotation( Where.class ) );
|
||||||
entityBinder.setCache( determineCacheSettings( clazzToProcess, mappings ) );
|
entityBinder.setCache( determineCacheSettings( clazzToProcess, mappings ) );
|
||||||
entityBinder.setNaturalIdCache( clazzToProcess.getAnnotation( NaturalIdCache.class ) );
|
entityBinder.setNaturalIdCache( clazzToProcess, clazzToProcess.getAnnotation( NaturalIdCache.class ) );
|
||||||
|
|
||||||
//Filters are not allowed on subclasses
|
//Filters are not allowed on subclasses
|
||||||
if ( !inheritanceState.hasParents() ) {
|
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 ( naturalIdCacheAnn != null ) {
|
||||||
if ( BinderHelper.isEmptyAnnotationValue( naturalIdCacheAnn.region() ) ) {
|
if ( BinderHelper.isEmptyAnnotationValue( naturalIdCacheAnn.region() ) ) {
|
||||||
if (cacheRegion != null) {
|
if (cacheRegion != null) {
|
||||||
naturalIdCacheRegion = cacheRegion + NATURAL_ID_CACHE_SUFFIX;
|
naturalIdCacheRegion = cacheRegion + NATURAL_ID_CACHE_SUFFIX;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
naturalIdCacheRegion = persistentClass.getEntityName() + NATURAL_ID_CACHE_SUFFIX;
|
naturalIdCacheRegion = clazzToProcess.getName() + NATURAL_ID_CACHE_SUFFIX;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -25,11 +25,18 @@
|
||||||
package org.hibernate.criterion;
|
package org.hibernate.criterion;
|
||||||
import org.hibernate.Criteria;
|
import org.hibernate.Criteria;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
|
import org.hibernate.Session;
|
||||||
import org.hibernate.engine.spi.TypedValue;
|
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(String)
|
||||||
|
* @see Session#bySimpleNaturalId(Class)
|
||||||
|
* @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();
|
||||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hibernate.Session;
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
|
@ -383,6 +384,14 @@ public class Restrictions {
|
||||||
return new SizeExpression(propertyName, size, ">=");
|
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() {
|
public static NaturalIdentifier naturalId() {
|
||||||
return new NaturalIdentifier();
|
return new NaturalIdentifier();
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.IdentityHashMap;
|
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.EntityKey;
|
||||||
import org.hibernate.engine.spi.EntityUniqueKey;
|
import org.hibernate.engine.spi.EntityUniqueKey;
|
||||||
import org.hibernate.engine.spi.PersistenceContext;
|
import org.hibernate.engine.spi.PersistenceContext;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SessionImplementor;
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
import org.hibernate.engine.spi.Status;
|
import org.hibernate.engine.spi.Status;
|
||||||
import org.hibernate.event.spi.EventSource;
|
import org.hibernate.event.spi.EventSource;
|
||||||
|
@ -74,7 +76,6 @@ import org.hibernate.pretty.MessageHelper;
|
||||||
import org.hibernate.proxy.HibernateProxy;
|
import org.hibernate.proxy.HibernateProxy;
|
||||||
import org.hibernate.proxy.LazyInitializer;
|
import org.hibernate.proxy.LazyInitializer;
|
||||||
import org.hibernate.tuple.ElementWrapper;
|
import org.hibernate.tuple.ElementWrapper;
|
||||||
import org.hibernate.type.Type;
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1703,6 +1704,20 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
|
|
||||||
// NATURAL ID RESOLUTION HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// 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
|
@Override
|
||||||
public void loadedStateUpdatedNotification(EntityEntry entityEntry) {
|
public void loadedStateUpdatedNotification(EntityEntry entityEntry) {
|
||||||
final EntityPersister persister = entityEntry.getPersister();
|
final EntityPersister persister = entityEntry.getPersister();
|
||||||
|
@ -1731,20 +1746,6 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
evictNaturalIdResolution( persister, entityEntry.getId(), naturalIdValues );
|
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) {
|
private Object[] getNaturalIdValues(EntityEntry entityEntry, EntityPersister persister) {
|
||||||
final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties();
|
final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties();
|
||||||
final Object[] naturalIdValues = new Object[naturalIdPropertyIndexes.length];
|
final Object[] naturalIdValues = new Object[naturalIdPropertyIndexes.length];
|
||||||
|
@ -1757,24 +1758,53 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
return naturalIdValues;
|
return naturalIdValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
private NaturalIdCacheKey getNaturalIdCacheKey(Object[] naturalIdValues, EntityPersister persister) {
|
private static class LocalNaturalIdCacheKey {
|
||||||
final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties();
|
private final EntityPersister persister;
|
||||||
|
private final Object[] values;
|
||||||
|
private int hashCode;
|
||||||
|
|
||||||
final Serializable[] naturalIdKeyValues = new Serializable[naturalIdPropertyIndexes.length];
|
public LocalNaturalIdCacheKey(EntityPersister persister, Object[] values) {
|
||||||
final Type[] naturalIdTypes = new Type[naturalIdPropertyIndexes.length];
|
this.persister = persister;
|
||||||
|
this.values = values;
|
||||||
|
|
||||||
final Type[] propertyTypes = persister.getPropertyTypes();
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
for ( int i = 0; i < naturalIdPropertyIndexes.length; i++ ) {
|
result = prime * result + ( ( persister == null ) ? 0 : persister.hashCode() );
|
||||||
naturalIdTypes[i] = propertyTypes[naturalIdPropertyIndexes[i]];
|
result = prime * result + Arrays.hashCode( values );
|
||||||
naturalIdKeyValues[i] = naturalIdTypes[i].disassemble( naturalIdValues[i], session, null );
|
this.hashCode = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new NaturalIdCacheKey( naturalIdKeyValues, naturalIdTypes, persister.getEntityName(),
|
public Object[] getValues() {
|
||||||
session.getTenantIdentifier(), session.getFactory() );
|
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 final EntityPersister persister;
|
||||||
|
|
||||||
private NaturalIdResolutionCache(EntityPersister persister) {
|
private NaturalIdResolutionCache(EntityPersister persister) {
|
||||||
|
@ -1785,13 +1815,20 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
return persister;
|
return persister;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Serializable,NaturalIdCacheKey> pkToNaturalIdMap = new ConcurrentHashMap<Serializable, NaturalIdCacheKey>();
|
private Map<Serializable, LocalNaturalIdCacheKey> pkToNaturalIdMap = new ConcurrentHashMap<Serializable, LocalNaturalIdCacheKey>();
|
||||||
private Map<NaturalIdCacheKey,Serializable> naturalIdToPkMap = new ConcurrentHashMap<NaturalIdCacheKey,Serializable>();
|
private Map<LocalNaturalIdCacheKey, Serializable> naturalIdToPkMap = new ConcurrentHashMap<LocalNaturalIdCacheKey, Serializable>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<EntityPersister,NaturalIdResolutionCache> naturalIdResolutionCacheMap
|
private void validateNaturalId(EntityPersister persister, Object[] naturalIdValues) {
|
||||||
= new ConcurrentHashMap<EntityPersister, NaturalIdResolutionCache>();
|
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
|
@Override
|
||||||
public Object[] findCachedNaturalId(EntityPersister persister, Serializable pk) {
|
public Object[] findCachedNaturalId(EntityPersister persister, Serializable pk) {
|
||||||
|
@ -1799,52 +1836,80 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
if ( entityNaturalIdResolutionCache == null ) {
|
if ( entityNaturalIdResolutionCache == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
final NaturalIdCacheKey naturalIdCacheKey = entityNaturalIdResolutionCache.pkToNaturalIdMap.get( pk );
|
final LocalNaturalIdCacheKey localNaturalIdCacheKey = entityNaturalIdResolutionCache.pkToNaturalIdMap.get( pk );
|
||||||
if (naturalIdCacheKey == null) {
|
if ( localNaturalIdCacheKey == null ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Serializable[] naturalIdKeyValues = naturalIdCacheKey.getNaturalId();
|
return localNaturalIdCacheKey.getValues();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Serializable findCachedNaturalIdResolution(EntityPersister persister, Object[] naturalIdValues) {
|
public Serializable findCachedNaturalIdResolution(EntityPersister persister, Object[] naturalIdValues) {
|
||||||
if ( ! persister.hasNaturalIdentifier() ) {
|
validateNaturalId( persister, naturalIdValues );
|
||||||
throw new IllegalArgumentException( "Entity did not define a natrual-id" );
|
|
||||||
|
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 );
|
// Session cache miss, see if second-level caching is enabled
|
||||||
final NaturalIdCacheKey naturalIdCacheKey = getNaturalIdCacheKey( naturalIdValues, persister );
|
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 ) {
|
if ( entityNaturalIdResolutionCache == null ) {
|
||||||
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister
|
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
|
||||||
.getNaturalIdCacheAccessStrategy();
|
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
|
||||||
return (Serializable) naturalIdCacheAccessStrategy.get( naturalIdCacheKey, session.getTimestamp() );
|
|
||||||
}
|
}
|
||||||
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
|
@Override
|
||||||
public void cacheNaturalIdResolution(EntityPersister persister, final Serializable pk, Object[] naturalIdValues, CachedNaturalIdValueSource valueSource ) {
|
public void cacheNaturalIdResolution(EntityPersister persister, final Serializable pk, Object[] naturalIdValues,
|
||||||
if ( ! persister.hasNaturalIdentifier() ) {
|
CachedNaturalIdValueSource valueSource) {
|
||||||
throw new IllegalArgumentException( "Entity did not define a natural-id" );
|
validateNaturalId( persister, naturalIdValues );
|
||||||
}
|
|
||||||
if ( persister.getNaturalIdentifierProperties().length != naturalIdValues.length ) {
|
|
||||||
throw new IllegalArgumentException( "Mismatch between expected number of natural-id values and found." );
|
|
||||||
}
|
|
||||||
|
|
||||||
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
||||||
if ( entityNaturalIdResolutionCache == null ) {
|
if ( entityNaturalIdResolutionCache == null ) {
|
||||||
|
@ -1852,24 +1917,41 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
|
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
|
||||||
}
|
}
|
||||||
|
|
||||||
final NaturalIdCacheKey naturalIdCacheKey = getNaturalIdCacheKey( naturalIdValues, persister );
|
final LocalNaturalIdCacheKey localNaturalIdCacheKey = new LocalNaturalIdCacheKey( persister, naturalIdValues );
|
||||||
|
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, localNaturalIdCacheKey );
|
||||||
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, naturalIdCacheKey );
|
entityNaturalIdResolutionCache.naturalIdToPkMap.put( localNaturalIdCacheKey, pk );
|
||||||
entityNaturalIdResolutionCache.naturalIdToPkMap.put( naturalIdCacheKey, pk );
|
|
||||||
|
|
||||||
|
//If second-level caching is enabled cache the resolution there as well
|
||||||
|
if ( persister.hasNaturalIdCache() ) {
|
||||||
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
|
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
|
||||||
|
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( naturalIdValues, persister, session );
|
||||||
|
|
||||||
|
final SessionFactoryImplementor factory = getSession().getFactory();
|
||||||
|
|
||||||
switch ( valueSource ) {
|
switch ( valueSource ) {
|
||||||
case LOAD: {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
case INSERT: {
|
case INSERT: {
|
||||||
naturalIdCacheAccessStrategy.insert( naturalIdCacheKey, pk );
|
naturalIdCacheAccessStrategy.insert( naturalIdCacheKey, pk );
|
||||||
|
|
||||||
( (EventSource) this.session ).getActionQueue().registerProcess( new AfterTransactionCompletionProcess() {
|
( (EventSource) this.session ).getActionQueue().registerProcess(
|
||||||
|
new AfterTransactionCompletionProcess() {
|
||||||
@Override
|
@Override
|
||||||
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
|
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 );
|
naturalIdCacheAccessStrategy.update( naturalIdCacheKey, pk );
|
||||||
|
|
||||||
( (EventSource) this.session ).getActionQueue().registerProcess( new AfterTransactionCompletionProcess() {
|
( (EventSource) this.session ).getActionQueue().registerProcess(
|
||||||
|
new AfterTransactionCompletionProcess() {
|
||||||
@Override
|
@Override
|
||||||
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
|
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
|
@Override
|
||||||
public void evictNaturalIdResolution(EntityPersister persister, final Serializable pk, Object[] naturalIdValues) {
|
public void evictNaturalIdResolution(EntityPersister persister, final Serializable pk, Object[] naturalIdValues) {
|
||||||
|
@ -1902,16 +1991,17 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
||||||
if ( entityNaturalIdResolutionCache == null ) {
|
if ( entityNaturalIdResolutionCache != null ) {
|
||||||
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
|
final LocalNaturalIdCacheKey localNaturalIdCacheKey = new LocalNaturalIdCacheKey( persister,
|
||||||
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
|
naturalIdValues );
|
||||||
|
entityNaturalIdResolutionCache.pkToNaturalIdMap.remove( pk );
|
||||||
|
entityNaturalIdResolutionCache.naturalIdToPkMap.remove( localNaturalIdCacheKey );
|
||||||
}
|
}
|
||||||
|
|
||||||
final NaturalIdCacheKey naturalIdCacheKey = getNaturalIdCacheKey( naturalIdValues, persister );
|
if ( persister.hasNaturalIdCache() ) {
|
||||||
entityNaturalIdResolutionCache.pkToNaturalIdMap.remove( pk ); //Should I compare the value returned here with the naturalIdCacheKey parameter?
|
|
||||||
entityNaturalIdResolutionCache.naturalIdToPkMap.remove( naturalIdCacheKey );
|
|
||||||
|
|
||||||
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
|
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
|
||||||
|
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( naturalIdValues, persister, session );
|
||||||
naturalIdCacheAccessStrategy.evict( naturalIdCacheKey );
|
naturalIdCacheAccessStrategy.evict( naturalIdCacheKey );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -72,36 +72,22 @@ public class DefaultResolveNaturalIdEventListener
|
||||||
protected Serializable resolveNaturalId(final ResolveNaturalIdEvent event) {
|
protected Serializable resolveNaturalId(final ResolveNaturalIdEvent event) {
|
||||||
final EntityPersister persister = event.getEntityPersister();
|
final EntityPersister persister = event.getEntityPersister();
|
||||||
|
|
||||||
if ( LOG.isTraceEnabled() ) {
|
final boolean traceEnabled = LOG.isTraceEnabled();
|
||||||
LOG.trace(
|
if ( traceEnabled )
|
||||||
"Attempting to resolve: " +
|
LOG.tracev( "Attempting to resolve: {0}",
|
||||||
MessageHelper.infoString(
|
MessageHelper.infoString( persister, event.getNaturalIdValues(), event.getSession().getFactory() ) );
|
||||||
persister, event.getNaturalIdValues(), event.getSession().getFactory()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Serializable entityId = resolveFromSessionCache( event );
|
Serializable entityId = resolveFromCache( event );
|
||||||
if ( entityId != null ) {
|
if ( entityId != null ) {
|
||||||
if ( LOG.isTraceEnabled() ) {
|
if ( traceEnabled )
|
||||||
LOG.trace(
|
LOG.tracev( "Resolved object in cache: {0}",
|
||||||
"Resolved object in session cache: " +
|
MessageHelper.infoString( persister, event.getNaturalIdValues(), event.getSession().getFactory() ) );
|
||||||
MessageHelper.infoString(
|
|
||||||
persister, event.getNaturalIdValues(), event.getSession().getFactory()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return entityId;
|
return entityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( LOG.isTraceEnabled() ) {
|
if ( traceEnabled )
|
||||||
LOG.trace(
|
LOG.tracev( "Object not resolved in any cache: {0}",
|
||||||
"Object not resolved in any cache: " +
|
MessageHelper.infoString( persister, event.getNaturalIdValues(), event.getSession().getFactory() ) );
|
||||||
MessageHelper.infoString(
|
|
||||||
persister, event.getNaturalIdValues(), event.getSession().getFactory()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return loadFromDatasource( event );
|
return loadFromDatasource( event );
|
||||||
}
|
}
|
||||||
|
@ -111,9 +97,9 @@ public class DefaultResolveNaturalIdEventListener
|
||||||
*
|
*
|
||||||
* @param event The load event
|
* @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(
|
return event.getSession().getPersistenceContext().findCachedNaturalIdResolution(
|
||||||
event.getEntityPersister(),
|
event.getEntityPersister(),
|
||||||
event.getOrderedNaturalIdValues()
|
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) {
|
public boolean containsCollection(String role, Serializable ownerIdentifier) {
|
||||||
CollectionPersister p = getCollectionPersister( role );
|
CollectionPersister p = getCollectionPersister( role );
|
||||||
return p.hasCache() &&
|
return p.hasCache() &&
|
||||||
|
|
|
@ -3902,6 +3902,10 @@ public abstract class AbstractEntityPersister
|
||||||
return cacheEntryStructure;
|
return cacheEntryStructure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasNaturalIdCache() {
|
||||||
|
return naturalIdRegionAccessStrategy != null;
|
||||||
|
}
|
||||||
|
|
||||||
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
|
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
|
||||||
return naturalIdRegionAccessStrategy;
|
return naturalIdRegionAccessStrategy;
|
||||||
}
|
}
|
||||||
|
|
|
@ -475,6 +475,11 @@ public interface EntityPersister extends OptimisticCacheSource {
|
||||||
*/
|
*/
|
||||||
public CacheEntryStructure getCacheEntryStructure();
|
public CacheEntryStructure getCacheEntryStructure();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this class have a natural id cache
|
||||||
|
*/
|
||||||
|
public boolean hasNaturalIdCache();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the NaturalId cache (optional operation)
|
* 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[] {
|
public static final Class[] ENTITY_PERSISTER_CONSTRUCTOR_ARGS = new Class[] {
|
||||||
PersistentClass.class,
|
PersistentClass.class,
|
||||||
EntityRegionAccessStrategy.class,
|
EntityRegionAccessStrategy.class,
|
||||||
|
NaturalIdRegionAccessStrategy.class,
|
||||||
SessionFactoryImplementor.class,
|
SessionFactoryImplementor.class,
|
||||||
Mapping.class
|
Mapping.class
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,6 +25,7 @@ package org.hibernate.test.annotations.naturalid;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
|
@ -35,12 +36,14 @@ import javax.persistence.Version;
|
||||||
import org.hibernate.annotations.Cache;
|
import org.hibernate.annotations.Cache;
|
||||||
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
import org.hibernate.annotations.CacheConcurrencyStrategy;
|
||||||
import org.hibernate.annotations.NaturalId;
|
import org.hibernate.annotations.NaturalId;
|
||||||
|
import org.hibernate.annotations.NaturalIdCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Guenther Demetz
|
* @author Guenther Demetz
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
|
||||||
|
@NaturalIdCache
|
||||||
public class A {
|
public class A {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
|
|
@ -6,11 +6,13 @@ import javax.persistence.Id;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
|
|
||||||
import org.hibernate.annotations.NaturalId;
|
import org.hibernate.annotations.NaturalId;
|
||||||
|
import org.hibernate.annotations.NaturalIdCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Emmanuel Bernard
|
* @author Emmanuel Bernard
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
|
@NaturalIdCache
|
||||||
public class Citizen {
|
public class Citizen {
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue
|
@GeneratedValue
|
||||||
|
|
|
@ -114,7 +114,7 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNaturalIdLoaderCached() {
|
public void testNaturalIdLoaderNotCached() {
|
||||||
saveSomeCitizens();
|
saveSomeCitizens();
|
||||||
|
|
||||||
Session s = openSession();
|
Session s = openSession();
|
||||||
|
@ -123,6 +123,8 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
|
||||||
final NaturalIdLoadAccess naturalIdLoader = s.byNaturalId( Citizen.class );
|
final NaturalIdLoadAccess naturalIdLoader = s.byNaturalId( Citizen.class );
|
||||||
naturalIdLoader.using( "ssn", "1234" ).using( "state", france );
|
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();
|
Statistics stats = sessionFactory().getStatistics();
|
||||||
stats.setStatisticsEnabled( true );
|
stats.setStatisticsEnabled( true );
|
||||||
stats.clear();
|
stats.clear();
|
||||||
|
@ -147,13 +149,58 @@ public class NaturalIdTest extends BaseCoreFunctionalTestCase {
|
||||||
.getNaturalIdCachePutCount()
|
.getNaturalIdCachePutCount()
|
||||||
);
|
);
|
||||||
|
|
||||||
// query a second time - result should be cached
|
// cleanup
|
||||||
citizen = (Citizen)naturalIdLoader.load();
|
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 );
|
assertNotNull( citizen );
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"Cache hits should be empty", 1, stats
|
"Cache hits should be empty", 1, stats
|
||||||
.getNaturalIdCacheHitCount()
|
.getNaturalIdCacheHitCount()
|
||||||
);
|
);
|
||||||
|
assertEquals(
|
||||||
|
"First load should be a miss", 0, stats
|
||||||
|
.getNaturalIdCacheMissCount()
|
||||||
|
);
|
||||||
|
assertEquals(
|
||||||
|
"Query result should be added to cache", 0, stats
|
||||||
|
.getNaturalIdCachePutCount()
|
||||||
|
);
|
||||||
|
|
||||||
// cleanup
|
// cleanup
|
||||||
tx.rollback();
|
tx.rollback();
|
||||||
|
|
|
@ -87,6 +87,7 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
||||||
|
|
||||||
public NoopEntityPersister(org.hibernate.mapping.PersistentClass persistentClass,
|
public NoopEntityPersister(org.hibernate.mapping.PersistentClass persistentClass,
|
||||||
org.hibernate.cache.spi.access.EntityRegionAccessStrategy strategy,
|
org.hibernate.cache.spi.access.EntityRegionAccessStrategy strategy,
|
||||||
|
NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
|
||||||
SessionFactoryImplementor sf,
|
SessionFactoryImplementor sf,
|
||||||
Mapping mapping) {
|
Mapping mapping) {
|
||||||
throw new GoofyException(NoopEntityPersister.class);
|
throw new GoofyException(NoopEntityPersister.class);
|
||||||
|
@ -383,6 +384,11 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNaturalIdCache() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
|
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -47,6 +47,7 @@ public class CustomPersister implements EntityPersister {
|
||||||
public CustomPersister(
|
public CustomPersister(
|
||||||
PersistentClass model,
|
PersistentClass model,
|
||||||
EntityRegionAccessStrategy cacheAccessStrategy,
|
EntityRegionAccessStrategy cacheAccessStrategy,
|
||||||
|
NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
|
||||||
SessionFactoryImplementor factory,
|
SessionFactoryImplementor factory,
|
||||||
Mapping mapping) {
|
Mapping mapping) {
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
|
@ -443,6 +444,10 @@ public class CustomPersister implements EntityPersister {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasNaturalIdCache() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
|
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,50 @@ public class NonstopAwareNaturalIdRegionAccessStrategy implements NaturalIdRegio
|
||||||
this.hibernateNonstopExceptionHandler = hibernateNonstopExceptionHandler;
|
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}
|
* {@inheritDoc}
|
||||||
*
|
*
|
||||||
|
|
|
@ -89,6 +89,36 @@ public class NonStrictReadWriteEhcacheNaturalIdRegionAccessStrategy
|
||||||
region.remove( key );
|
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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -90,4 +90,37 @@ public class ReadOnlyEhcacheNaturalIdRegionAccessStrategy
|
||||||
public void unlockItem(Object key, SoftLock lock) throws CacheException {
|
public void unlockItem(Object key, SoftLock lock) throws CacheException {
|
||||||
//throw new UnsupportedOperationException("Can't write to a readonly object");
|
//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;
|
package org.hibernate.cache.ehcache.internal.strategy;
|
||||||
|
|
||||||
|
import org.hibernate.cache.CacheException;
|
||||||
import org.hibernate.cache.ehcache.internal.regions.EhcacheNaturalIdRegion;
|
import org.hibernate.cache.ehcache.internal.regions.EhcacheNaturalIdRegion;
|
||||||
import org.hibernate.cache.spi.NaturalIdRegion;
|
import org.hibernate.cache.spi.NaturalIdRegion;
|
||||||
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
||||||
|
import org.hibernate.cache.spi.access.SoftLock;
|
||||||
import org.hibernate.cfg.Settings;
|
import org.hibernate.cfg.Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,4 +53,75 @@ public class ReadWriteEhcacheNaturalIdRegionAccessStrategy
|
||||||
public NaturalIdRegion getRegion() {
|
public NaturalIdRegion getRegion() {
|
||||||
return region;
|
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;
|
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}
|
* {@inheritDoc}
|
||||||
|
@ -79,6 +93,20 @@ public class TransactionalEhcacheNaturalIdRegionAccessStrategy
|
||||||
return region;
|
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}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
@ -124,4 +152,17 @@ public class TransactionalEhcacheNaturalIdRegionAccessStrategy
|
||||||
// no-op
|
// 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 javax.persistence.PersistenceException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
@ -36,6 +37,7 @@ import org.hibernate.LockOptions;
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.bytecode.spi.EntityInstrumentationMetadata;
|
import org.hibernate.bytecode.spi.EntityInstrumentationMetadata;
|
||||||
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
|
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
|
||||||
|
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
||||||
import org.hibernate.cache.spi.entry.CacheEntryStructure;
|
import org.hibernate.cache.spi.entry.CacheEntryStructure;
|
||||||
import org.hibernate.ejb.Ejb3Configuration;
|
import org.hibernate.ejb.Ejb3Configuration;
|
||||||
import org.hibernate.engine.spi.CascadeStyle;
|
import org.hibernate.engine.spi.CascadeStyle;
|
||||||
|
@ -108,6 +110,7 @@ public class PersisterClassProviderTest {
|
||||||
public GoofyProvider(
|
public GoofyProvider(
|
||||||
org.hibernate.mapping.PersistentClass persistentClass,
|
org.hibernate.mapping.PersistentClass persistentClass,
|
||||||
org.hibernate.cache.spi.access.EntityRegionAccessStrategy strategy,
|
org.hibernate.cache.spi.access.EntityRegionAccessStrategy strategy,
|
||||||
|
NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
|
||||||
SessionFactoryImplementor sf,
|
SessionFactoryImplementor sf,
|
||||||
Mapping mapping) {
|
Mapping mapping) {
|
||||||
throw new GoofyException();
|
throw new GoofyException();
|
||||||
|
@ -274,6 +277,16 @@ public class PersisterClassProviderTest {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNaturalIdCache() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdentifierGenerator getIdentifierGenerator() {
|
public IdentifierGenerator getIdentifierGenerator() {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -23,8 +23,10 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.testing.cache;
|
package org.hibernate.testing.cache;
|
||||||
|
|
||||||
|
import org.hibernate.cache.CacheException;
|
||||||
import org.hibernate.cache.spi.NaturalIdRegion;
|
import org.hibernate.cache.spi.NaturalIdRegion;
|
||||||
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
||||||
|
import org.hibernate.cache.spi.access.SoftLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eric Dalquist
|
* @author Eric Dalquist
|
||||||
|
@ -47,6 +49,26 @@ class BaseNaturalIdRegionAccessStrategy extends BaseRegionAccessStrategy impleme
|
||||||
return region;
|
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) {
|
BaseNaturalIdRegionAccessStrategy(NaturalIdRegionImpl region) {
|
||||||
this.region = region;
|
this.region = region;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,10 @@ package org.hibernate.testing.cache;
|
||||||
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
import org.hibernate.cache.CacheException;
|
||||||
import org.hibernate.cache.spi.NaturalIdRegion;
|
import org.hibernate.cache.spi.NaturalIdRegion;
|
||||||
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
||||||
|
import org.hibernate.cache.spi.access.SoftLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eric Dalquist
|
* @author Eric Dalquist
|
||||||
|
@ -40,6 +42,63 @@ class ReadWriteNaturalIdRegionAccessStrategy extends AbstractReadWriteAccessStra
|
||||||
this.region = region;
|
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
|
@Override
|
||||||
Comparator getVersionComparator() {
|
Comparator getVersionComparator() {
|
||||||
return region.getCacheDataDescription().getVersionComparator();
|
return region.getCacheDataDescription().getVersionComparator();
|
||||||
|
|
Loading…
Reference in New Issue