HHH-6974 Adding hooks into NaturalIdRegionAccessStrategy

Created NaturalIdCacheKey to encapsulate the key for caching a naturalid
Adding hooks into NaturalIdRegionAccessStrategy from StatefulPersistenceContext
for load/insert/update/delete events on entities
This commit is contained in:
Eric Dalquist 2012-02-02 12:21:51 -06:00 committed by Steve Ebersole
parent c473520585
commit ef22e31068
18 changed files with 454 additions and 142 deletions

View File

@ -0,0 +1,147 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.cache.spi;
import java.io.Serializable;
import java.util.Arrays;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.type.Type;
/**
* Allows multiple entity classes / collection roles to be
* stored in the same cache region. Also allows for composite
* keys which do not properly implement equals()/hashCode().
*
* @author Gavin King
*/
public class NaturalIdCacheKey implements Serializable {
private final Serializable[] naturalId;
private final Type[] types;
private final String entityName;
private final String tenantId;
private final int hashCode;
private final String toString;
/**
* Construct a new key for a collection or entity instance.
* Note that an entity name should always be the root entity
* name, not a subclass entity name.
*
* @param 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
*/
public NaturalIdCacheKey(
final Serializable[] naturalId,
final Type[] types,
final String entityOrRoleName,
final String tenantId,
final SessionFactoryImplementor factory) {
this.naturalId = naturalId;
this.types = types;
this.entityName = entityOrRoleName;
this.tenantId = tenantId;
this.hashCode = this.generateHashCode( this.naturalId, this.types, factory );
this.toString = entityOrRoleName + "##NaturalId" + Arrays.toString( this.naturalId );
}
private int generateHashCode(final Serializable[] naturalId, final Type[] types,
final SessionFactoryImplementor factory) {
final int prime = 31;
int result = 1;
result = prime * result + ( ( entityName == null ) ? 0 : entityName.hashCode() );
result = prime * result + ( ( tenantId == null ) ? 0 : tenantId.hashCode() );
for ( int i = 0; i < naturalId.length; i++ ) {
result = prime * result + types[i].getHashCode( naturalId[i], factory );
}
return result;
}
@Override
public String toString() {
// Mainly for OSCache
return this.toString;
}
public Serializable[] getNaturalId() {
return naturalId;
}
public Type[] getTypes() {
return types;
}
@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;
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] ) ) {
return false;
}
}
return true;
}
public String getEntityOrRoleName() {
return entityName;
}
}

View File

@ -23,6 +23,7 @@
*/
package org.hibernate.cache.spi.access;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.spi.NaturalIdRegion;
/**
@ -34,12 +35,16 @@ import org.hibernate.cache.spi.NaturalIdRegion;
* There is another usage pattern that is used to invalidate entries
* after performing "bulk" HQL/SQL operations:
* {@link #lockRegion} -> {@link #removeAll} -> {@link #unlockRegion}
* <p/>
* NaturalIds are not versioned so null will always be passed to the version parameter for
* {@link #putFromLoad(Object, Object, long, Object)}, {@link #putFromLoad(Object, Object, long, Object, boolean)},
* and {@link #lockItem(Object, Object)}
*
* @author Gavin King
* @author Steve Ebersole
* @author Eric Dalquist
*/
public interface NaturalIdRegionAccessStrategy extends RegionAccessStrategy{
public interface NaturalIdRegionAccessStrategy extends RegionAccessStrategy {
/**
* Get the wrapped naturalId cache region
@ -47,4 +52,53 @@ public interface NaturalIdRegionAccessStrategy extends RegionAccessStrategy{
* @return The underlying region
*/
public NaturalIdRegion getRegion();
/**
* Called after an item has been inserted (before the transaction completes),
* instead of calling evict().
* This method is used by "synchronous" concurrency strategies.
*
* @param key The item key
* @param value The item
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
*/
public boolean insert(Object key, Object value) throws CacheException;
/**
* Called after an item has been inserted (after the transaction completes),
* instead of calling release().
* This method is used by "asynchronous" concurrency strategies.
*
* @param key The item key
* @param value The item
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
*/
public boolean afterInsert(Object key, Object value) throws CacheException;
/**
* Called after an item has been updated (before the transaction completes),
* instead of calling evict(). This method is used by "synchronous" concurrency
* strategies.
*
* @param key The item key
* @param value The item
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
*/
public boolean update(Object key, Object value) throws CacheException;
/**
* Called after an item has been updated (after the transaction completes),
* instead of calling release(). This method is used by "asynchronous"
* concurrency strategies.
*
* @param key The item key
* @param value The item
* @param lock The lock previously obtained from {@link #lockItem}
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
*/
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws CacheException;
}

View File

@ -29,7 +29,6 @@ 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;
@ -41,8 +40,6 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.collections.map.AbstractReferenceMap;
import org.apache.commons.collections.map.ReferenceMap;
import org.jboss.logging.Logger;
import org.hibernate.AssertionFailure;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
@ -51,6 +48,10 @@ import org.hibernate.MappingException;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.PersistentObjectException;
import org.hibernate.TransientObjectException;
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
import org.hibernate.cache.spi.NaturalIdCacheKey;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.access.SoftLock;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.loading.internal.LoadContexts;
import org.hibernate.engine.spi.AssociationKey;
@ -63,6 +64,7 @@ import org.hibernate.engine.spi.EntityUniqueKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.event.spi.EventSource;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.MarkerObject;
import org.hibernate.internal.util.collections.IdentityMap;
@ -72,6 +74,8 @@ 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;
/**
* A <strong>stateful</strong> implementation of the {@link PersistenceContext} contract meaning that we maintain this
@ -327,7 +331,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
if ( persister.getEntityMetamodel().hasImmutableNaturalId() ) {
// an immutable natural-id is not retrieved during a normal database-snapshot operation...
final Object[] dbValue = persister.getNaturalIdentifierSnapshot( id, session );
cacheNaturalIdResolution( persister, id, dbValue );
cacheNaturalIdResolution( persister, id, dbValue, CachedNaturalIdValueSource.LOAD );
return dbValue;
}
else {
@ -343,7 +347,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
for ( int i = 0; i < props.length; i++ ) {
naturalIdSnapshotSubSet[i] = entitySnapshot[ props[i] ];
}
cacheNaturalIdResolution( persister, id, naturalIdSnapshotSubSet );
cacheNaturalIdResolution( persister, id, naturalIdSnapshotSubSet, CachedNaturalIdValueSource.LOAD );
return naturalIdSnapshotSubSet;
}
}
@ -1707,56 +1711,67 @@ public class StatefulPersistenceContext implements PersistenceContext {
return;
}
// extract the natural id values
final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties();
final Object[] naturalIdValues = new Object[ naturalIdPropertyIndexes.length ];
int i = 0;
for ( int naturalIdPropertyIndex : naturalIdPropertyIndexes ) {
naturalIdValues[i++] = entityEntry.getLoadedState()[ naturalIdPropertyIndex ];
}
final Object[] naturalIdValues = getNaturalIdValues( entityEntry, persister );
// re-cache
cacheNaturalIdResolution( persister, entityEntry.getId(), naturalIdValues );
cacheNaturalIdResolution( persister, entityEntry.getId(), naturalIdValues, CachedNaturalIdValueSource.UPDATE );
}
@Override
public void loadedStateDeletedNotification(EntityEntry entityEntry) {
final EntityPersister persister = entityEntry.getPersister();
if ( ! persister.hasNaturalIdentifier() ) {
// nothing to do
return;
}
final Object[] naturalIdValues = getNaturalIdValues( entityEntry, persister );
// evict from cache
evictNaturalIdResolution( persister, entityEntry.getId(), naturalIdValues );
}
private class NaturalId {
private final EntityPersister persister;
private final Object[] values;
private NaturalId(EntityPersister persister, Object[] values) {
this.persister = persister;
this.values = values;
@Override
public void loadedStateInsertedNotification(EntityEntry entityEntry) {
final EntityPersister persister = entityEntry.getPersister();
if ( ! persister.hasNaturalIdentifier() ) {
// nothing to do
return;
}
public EntityPersister getPersister() {
return persister;
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];
final Object[] loadedState = entityEntry.getLoadedState();
for ( int i = 0; i < naturalIdPropertyIndexes.length; i++ ) {
naturalIdValues[i] = loadedState[naturalIdPropertyIndexes[i]];
}
public Object[] getValues() {
return values;
return naturalIdValues;
}
private NaturalIdCacheKey getNaturalIdCacheKey(Object[] naturalIdValues, EntityPersister persister) {
final int[] naturalIdPropertyIndexes = persister.getNaturalIdentifierProperties();
final Serializable[] naturalIdKeyValues = new Serializable[naturalIdPropertyIndexes.length];
final Type[] naturalIdTypes = new Type[naturalIdPropertyIndexes.length];
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 );
}
@Override
public boolean equals(Object other) {
if ( this == other ) {
return true;
}
if ( other == null || getClass() != other.getClass() ) {
return false;
}
final NaturalId that = (NaturalId) other;
return persister.equals( that.persister )
&& Arrays.equals( values, that.values );
}
@Override
public int hashCode() {
int result = persister.hashCode();
result = 31 * result + Arrays.hashCode( values );
return result;
}
return new NaturalIdCacheKey( naturalIdKeyValues, naturalIdTypes, persister.getEntityName(),
session.getTenantIdentifier(), session.getFactory() );
}
private class NaturalIdResolutionCache implements Serializable {
@ -1770,8 +1785,8 @@ public class StatefulPersistenceContext implements PersistenceContext {
return persister;
}
private Map<Serializable,NaturalId> pkToNaturalIdMap = new ConcurrentHashMap<Serializable, NaturalId>();
private Map<NaturalId,Serializable> naturalIdToPkMap = new ConcurrentHashMap<NaturalId,Serializable>();
private Map<Serializable,NaturalIdCacheKey> pkToNaturalIdMap = new ConcurrentHashMap<Serializable, NaturalIdCacheKey>();
private Map<NaturalIdCacheKey,Serializable> naturalIdToPkMap = new ConcurrentHashMap<NaturalIdCacheKey,Serializable>();
}
private Map<EntityPersister,NaturalIdResolutionCache> naturalIdResolutionCacheMap
@ -1785,8 +1800,19 @@ public class StatefulPersistenceContext implements PersistenceContext {
return null;
}
else {
final NaturalId naturalId = entityNaturalIdResolutionCache.pkToNaturalIdMap.get( pk );
return naturalId == null ? null : naturalId.getValues();
final NaturalIdCacheKey naturalIdCacheKey = entityNaturalIdResolutionCache.pkToNaturalIdMap.get( pk );
if (naturalIdCacheKey == 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;
}
}
@ -1800,17 +1826,19 @@ public class StatefulPersistenceContext implements PersistenceContext {
}
final NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
final NaturalIdCacheKey naturalIdCacheKey = getNaturalIdCacheKey( naturalIdValues, persister );
if ( entityNaturalIdResolutionCache == null ) {
return null;
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister
.getNaturalIdCacheAccessStrategy();
return (Serializable) naturalIdCacheAccessStrategy.get( naturalIdCacheKey, session.getTimestamp() );
}
else {
final NaturalId naturalId = new NaturalId( persister, naturalIdValues );
return entityNaturalIdResolutionCache.naturalIdToPkMap.get( naturalId );
return entityNaturalIdResolutionCache.naturalIdToPkMap.get( naturalIdCacheKey );
}
}
@Override
public void cacheNaturalIdResolution(EntityPersister persister, Serializable pk, Object[] naturalIdValues) {
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" );
}
@ -1824,8 +1852,66 @@ public class StatefulPersistenceContext implements PersistenceContext {
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
}
final NaturalId naturalId = new NaturalId( persister, naturalIdValues );
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, naturalId );
entityNaturalIdResolutionCache.naturalIdToPkMap.put( naturalId, pk );
final NaturalIdCacheKey naturalIdCacheKey = getNaturalIdCacheKey( naturalIdValues, persister );
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, naturalIdCacheKey );
entityNaturalIdResolutionCache.naturalIdToPkMap.put( naturalIdCacheKey, pk );
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
switch ( valueSource ) {
case LOAD: {
naturalIdCacheAccessStrategy.putFromLoad( naturalIdCacheKey, pk, session.getTimestamp(), null );
break;
}
case INSERT: {
naturalIdCacheAccessStrategy.insert( naturalIdCacheKey, pk );
( (EventSource) this.session ).getActionQueue().registerProcess( new AfterTransactionCompletionProcess() {
@Override
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
naturalIdCacheAccessStrategy.afterInsert( naturalIdCacheKey, pk );
}
} );
break;
}
case UPDATE: {
final SoftLock lock = naturalIdCacheAccessStrategy.lockItem( naturalIdCacheKey, null );
naturalIdCacheAccessStrategy.update( naturalIdCacheKey, pk );
( (EventSource) this.session ).getActionQueue().registerProcess( new AfterTransactionCompletionProcess() {
@Override
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
naturalIdCacheAccessStrategy.afterUpdate( naturalIdCacheKey, pk, lock );
}
} );
break;
}
}
}
@Override
public void evictNaturalIdResolution(EntityPersister persister, final Serializable pk, Object[] naturalIdValues ) {
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." );
}
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
if ( entityNaturalIdResolutionCache == null ) {
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
}
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 );
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
naturalIdCacheAccessStrategy.evict( naturalIdCacheKey );
}
}

View File

@ -252,6 +252,8 @@ public final class EntityEntry implements Serializable {
previousStatus = status;
status = Status.GONE;
existsInDatabase = false;
notifyLoadedStateDeleted();
}
/**
@ -260,6 +262,8 @@ public final class EntityEntry implements Serializable {
*/
public void postInsert() {
existsInDatabase = true;
notifyLoadedStateInserted();
}
public boolean isNullifiable(boolean earlyInsert, SessionImplementor session) {
@ -377,6 +381,22 @@ public final class EntityEntry implements Serializable {
persistenceContext.loadedStateUpdatedNotification( this );
}
private void notifyLoadedStateInserted() {
if ( persistenceContext == null ) {
throw new HibernateException( "PersistenceContext was null on attempt to insert loaded state" );
}
persistenceContext.loadedStateInsertedNotification( this );
}
private void notifyLoadedStateDeleted() {
if ( persistenceContext == null ) {
throw new HibernateException( "PersistenceContext was null on attempt to delete loaded state" );
}
persistenceContext.loadedStateDeletedNotification( this );
}
/**
* Custom serialization routine used during serialization of a

View File

@ -684,10 +684,25 @@ public interface PersistenceContext {
public boolean wasInsertedDuringTransaction(EntityPersister persister, Serializable id);
public void loadedStateUpdatedNotification(EntityEntry entityEntry);
public void loadedStateInsertedNotification(EntityEntry entityEntry);
public void loadedStateDeletedNotification(EntityEntry entityEntry);
public Object[] findCachedNaturalId(EntityPersister persister, Serializable pk);
public Serializable findCachedNaturalIdResolution(EntityPersister persister, Object[] naturalId);
public void cacheNaturalIdResolution(EntityPersister persister, Serializable pk, Object[] naturalId);
/**
* The type of action the cache call is originating from
*/
public enum CachedNaturalIdValueSource {
LOAD,
INSERT,
UPDATE
}
public void cacheNaturalIdResolution(EntityPersister persister, Serializable pk, Object[] naturalId, CachedNaturalIdValueSource valueSource);
public void evictNaturalIdResolution(EntityPersister persister, final Serializable pk, Object[] naturalIdValues );
}

View File

@ -26,10 +26,7 @@ package org.hibernate.event.internal;
import java.io.Serializable;
import org.hibernate.HibernateException;
import org.hibernate.cache.spi.CacheKey;
import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.PersistenceContext.CachedNaturalIdValueSource;
import org.hibernate.event.spi.ResolveNaturalIdEvent;
import org.hibernate.event.spi.ResolveNaturalIdEventListener;
import org.hibernate.internal.CoreMessageLogger;
@ -97,19 +94,6 @@ public class DefaultResolveNaturalIdEventListener
return entityId;
}
entityId = loadFromSecondLevelCache( event );
if ( entityId != null ) {
if ( LOG.isTraceEnabled() ) {
LOG.trace(
"Resolved object in second-level cache: " +
MessageHelper.infoString(
persister, event.getNaturalIdValues(), event.getSession().getFactory()
)
);
}
return entityId;
}
if ( LOG.isTraceEnabled() ) {
LOG.trace(
"Object not resolved in any cache: " +
@ -136,48 +120,6 @@ public class DefaultResolveNaturalIdEventListener
);
}
/**
* Attempts to load the entity from the second-level cache.
*
* @param event The event
*
* @return The entity from the second-level cache, or null.
*/
protected Serializable loadFromSecondLevelCache(final ResolveNaturalIdEvent event) {
// final SessionImplementor source = event.getSession();
//
// EntityPersister persister = event.getEntityPersister();
//
// final boolean useCache = persister.hasCache() && source.getCacheMode().isGetEnabled();
//
// if ( useCache ) {
//
// final SessionFactoryImplementor factory = source.getFactory();
//
// final CacheKey ck = source.generateCacheKey( event.getNaturalIdValues(), persister.getIdentifierType(),
// persister.getRootEntityName() );
// Object ce = persister.getCacheAccessStrategy().get( ck, source.getTimestamp() );
// if ( factory.getStatistics().isStatisticsEnabled() ) {
// if ( ce == null ) {
// factory.getStatisticsImplementor().secondLevelCacheMiss(
// persister.getCacheAccessStrategy().getRegion().getName() );
// }
// else {
// factory.getStatisticsImplementor().secondLevelCacheHit(
// persister.getCacheAccessStrategy().getRegion().getName() );
// }
// }
//
// if ( ce != null ) {
// CacheEntry entry = (CacheEntry) persister.getCacheEntryStructure().destructure( ce, factory );
//
// // Entity was found in second-level cache...
// return assembleCacheEntry( entry, event.getEntityId(), persister, event );
// }
// }
return null;
}
/**
* Performs the process of loading an entity from the configured
* underlying datasource.
@ -195,7 +137,8 @@ public class DefaultResolveNaturalIdEventListener
event.getSession().getPersistenceContext().cacheNaturalIdResolution(
event.getEntityPersister(),
pk,
event.getOrderedNaturalIdValues()
event.getOrderedNaturalIdValues(),
CachedNaturalIdValueSource.LOAD
);
return pk;
}

View File

@ -37,6 +37,7 @@ import org.hibernate.ScrollableResults;
import org.hibernate.SessionException;
import org.hibernate.SharedSessionContract;
import org.hibernate.cache.spi.CacheKey;
import org.hibernate.cache.spi.NaturalIdCacheKey;
import org.hibernate.engine.jdbc.LobCreationContext;
import org.hibernate.engine.jdbc.spi.JdbcConnectionAccess;
import org.hibernate.engine.query.spi.HQLQueryPlan;

View File

@ -350,26 +350,29 @@ public final class SessionFactoryImpl
allCacheRegions.put( cacheRegionName, entityRegion );
}
}
EntityPersister cp = serviceRegistry.getService( PersisterFactory.class ).createEntityPersister(
model,
accessStrategy,
this,
mapping
);
entityPersisters.put( model.getEntityName(), cp );
classMeta.put( model.getEntityName(), cp.getClassMetadata() );
if ( cp.hasNaturalIdentifier() && model.getNaturalIdCacheRegionName() != null ) {
NaturalIdRegionAccessStrategy naturalIdAccessStrategy = null;
if ( model.hasNaturalId() && model.getNaturalIdCacheRegionName() != null ) {
final String naturalIdCacheRegionName = cacheRegionPrefix + model.getNaturalIdCacheRegionName();
NaturalIdRegionAccessStrategy naturalIdAccessStrategy = ( NaturalIdRegionAccessStrategy ) entityAccessStrategies.get( naturalIdCacheRegionName );
naturalIdAccessStrategy = ( NaturalIdRegionAccessStrategy ) entityAccessStrategies.get( naturalIdCacheRegionName );
if ( naturalIdAccessStrategy == null && settings.isSecondLevelCacheEnabled() ) {
final NaturalIdRegion naturalIdRegion = settings.getRegionFactory().buildNaturalIdRegion( naturalIdCacheRegionName, properties, CacheDataDescriptionImpl.decode( cp ) );
final NaturalIdRegion naturalIdRegion = settings.getRegionFactory().buildNaturalIdRegion( naturalIdCacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) );
naturalIdAccessStrategy = naturalIdRegion.buildAccessStrategy( settings.getRegionFactory().getDefaultAccessType() );
entityAccessStrategies.put( naturalIdCacheRegionName, naturalIdAccessStrategy );
allCacheRegions.put( naturalIdCacheRegionName, naturalIdRegion );
}
}
EntityPersister cp = serviceRegistry.getService( PersisterFactory.class ).createEntityPersister(
model,
accessStrategy,
naturalIdAccessStrategy,
this,
mapping
);
entityPersisters.put( model.getEntityName(), cp );
classMeta.put( model.getEntityName(), cp.getClassMetadata() );
}
this.classMetadata = Collections.unmodifiableMap(classMeta);

View File

@ -52,6 +52,7 @@ import org.hibernate.bytecode.instrumentation.spi.LazyPropertyInitializer;
import org.hibernate.bytecode.spi.EntityInstrumentationMetadata;
import org.hibernate.cache.spi.CacheKey;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.entry.CacheEntry;
import org.hibernate.cache.spi.entry.CacheEntryStructure;
import org.hibernate.cache.spi.entry.StructuredCacheEntry;
@ -140,6 +141,7 @@ public abstract class AbstractEntityPersister
// moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private final SessionFactoryImplementor factory;
private final EntityRegionAccessStrategy cacheAccessStrategy;
private final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy;
private final boolean isLazyPropertiesCacheable;
private final CacheEntryStructure cacheEntryStructure;
private final EntityMetamodel entityMetamodel;
@ -485,11 +487,13 @@ public abstract class AbstractEntityPersister
public AbstractEntityPersister(
final PersistentClass persistentClass,
final EntityRegionAccessStrategy cacheAccessStrategy,
final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
final SessionFactoryImplementor factory) throws HibernateException {
// moved up from AbstractEntityPersister ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
this.factory = factory;
this.cacheAccessStrategy = cacheAccessStrategy;
this.naturalIdRegionAccessStrategy = naturalIdRegionAccessStrategy;
isLazyPropertiesCacheable = persistentClass.isLazyPropertiesCacheable();
this.cacheEntryStructure = factory.getSettings().isStructuredCacheEntriesEnabled() ?
(CacheEntryStructure) new StructuredCacheEntry(this) :
@ -770,9 +774,11 @@ public abstract class AbstractEntityPersister
public AbstractEntityPersister(
final EntityBinding entityBinding,
final EntityRegionAccessStrategy cacheAccessStrategy,
final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
final SessionFactoryImplementor factory) throws HibernateException {
this.factory = factory;
this.cacheAccessStrategy = cacheAccessStrategy;
this.naturalIdRegionAccessStrategy = naturalIdRegionAccessStrategy;
this.isLazyPropertiesCacheable =
entityBinding.getHierarchyDetails().getCaching() == null ?
false :
@ -3895,6 +3901,10 @@ public abstract class AbstractEntityPersister
public CacheEntryStructure getCacheEntryStructure() {
return cacheEntryStructure;
}
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
return naturalIdRegionAccessStrategy;
}
public Comparator getVersionComparator() {
return isVersioned() ? getVersionType().getComparator() : null;

View File

@ -31,10 +31,10 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.annotations.NaturalId;
import org.hibernate.bytecode.spi.EntityInstrumentationMetadata;
import org.hibernate.cache.spi.OptimisticCacheSource;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.entry.CacheEntryStructure;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -474,6 +474,11 @@ public interface EntityPersister extends OptimisticCacheSource {
* Get the cache structure
*/
public CacheEntryStructure getCacheEntryStructure();
/**
* Get the NaturalId cache (optional operation)
*/
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy();
/**
* Get the user-visible metadata for the class (optional operation)

View File

@ -34,6 +34,7 @@ import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.QueryException;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
import org.hibernate.engine.spi.Mapping;
@ -121,10 +122,11 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
public JoinedSubclassEntityPersister(
final PersistentClass persistentClass,
final EntityRegionAccessStrategy cacheAccessStrategy,
final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
final SessionFactoryImplementor factory,
final Mapping mapping) throws HibernateException {
super( persistentClass, cacheAccessStrategy, factory );
super( persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory );
// DISCRIMINATOR
@ -505,9 +507,10 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
public JoinedSubclassEntityPersister(
final EntityBinding entityBinding,
final EntityRegionAccessStrategy cacheAccessStrategy,
final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
final SessionFactoryImplementor factory,
final Mapping mapping) throws HibernateException {
super( entityBinding, cacheAccessStrategy, factory );
super( entityBinding, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory );
// TODO: implement!!! initializing final fields to null to make compiler happy
tableSpan = -1;
tableNames = null;

View File

@ -33,6 +33,7 @@ import java.util.Map;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -136,10 +137,11 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
public SingleTableEntityPersister(
final PersistentClass persistentClass,
final EntityRegionAccessStrategy cacheAccessStrategy,
final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
final SessionFactoryImplementor factory,
final Mapping mapping) throws HibernateException {
super( persistentClass, cacheAccessStrategy, factory );
super( persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory );
// CLASS + TABLE
@ -451,10 +453,11 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
public SingleTableEntityPersister(
final EntityBinding entityBinding,
final EntityRegionAccessStrategy cacheAccessStrategy,
final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
final SessionFactoryImplementor factory,
final Mapping mapping) throws HibernateException {
super( entityBinding, cacheAccessStrategy, factory );
super( entityBinding, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory );
// CLASS + TABLE

View File

@ -36,6 +36,7 @@ import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.MappingException;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cfg.Settings;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
@ -82,10 +83,11 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
public UnionSubclassEntityPersister(
final PersistentClass persistentClass,
final EntityRegionAccessStrategy cacheAccessStrategy,
final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
final SessionFactoryImplementor factory,
final Mapping mapping) throws HibernateException {
super( persistentClass, cacheAccessStrategy, factory );
super( persistentClass, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory );
if ( getIdentifierGenerator() instanceof IdentityGenerator ) {
throw new MappingException(
@ -241,9 +243,10 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
public UnionSubclassEntityPersister(
final EntityBinding entityBinding,
final EntityRegionAccessStrategy cacheAccessStrategy,
final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
final SessionFactoryImplementor factory,
final Mapping mapping) throws HibernateException {
super(entityBinding, cacheAccessStrategy, factory );
super(entityBinding, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory );
// TODO: implement!!! initializing final fields to null to make compiler happy.
subquery = null;
tableName = null;

View File

@ -30,6 +30,7 @@ import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -77,6 +78,7 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
public static final Class[] ENTITY_PERSISTER_CONSTRUCTOR_ARGS_NEW = new Class[] {
EntityBinding.class,
EntityRegionAccessStrategy.class,
NaturalIdRegionAccessStrategy.class,
SessionFactoryImplementor.class,
Mapping.class
};
@ -122,13 +124,14 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
public EntityPersister createEntityPersister(
PersistentClass metadata,
EntityRegionAccessStrategy cacheAccessStrategy,
NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
SessionFactoryImplementor factory,
Mapping cfg) {
Class<? extends EntityPersister> persisterClass = metadata.getEntityPersisterClass();
if ( persisterClass == null ) {
persisterClass = serviceRegistry.getService( PersisterClassResolver.class ).getEntityPersisterClass( metadata );
}
return create( persisterClass, ENTITY_PERSISTER_CONSTRUCTOR_ARGS, metadata, cacheAccessStrategy, factory, cfg );
return create( persisterClass, ENTITY_PERSISTER_CONSTRUCTOR_ARGS, metadata, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory, cfg );
}
@Override
@ -141,7 +144,7 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
if ( persisterClass == null ) {
persisterClass = serviceRegistry.getService( PersisterClassResolver.class ).getEntityPersisterClass( metadata );
}
return create( persisterClass, ENTITY_PERSISTER_CONSTRUCTOR_ARGS_NEW, metadata, cacheAccessStrategy, factory, cfg );
return create( persisterClass, ENTITY_PERSISTER_CONSTRUCTOR_ARGS_NEW, metadata, cacheAccessStrategy, null, factory, cfg );
}
// TODO: change metadata arg type to EntityBinding when new metadata is integrated
@ -150,12 +153,13 @@ public final class PersisterFactoryImpl implements PersisterFactory, ServiceRegi
Class[] persisterConstructorArgs,
Object metadata,
EntityRegionAccessStrategy cacheAccessStrategy,
NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
SessionFactoryImplementor factory,
Mapping cfg) throws HibernateException {
try {
Constructor<? extends EntityPersister> constructor = persisterClass.getConstructor( persisterConstructorArgs );
try {
return constructor.newInstance( metadata, cacheAccessStrategy, factory, cfg );
return constructor.newInstance( metadata, cacheAccessStrategy, naturalIdRegionAccessStrategy, factory, cfg );
}
catch (MappingException e) {
throw e;

View File

@ -26,6 +26,7 @@ package org.hibernate.persister.spi;
import org.hibernate.HibernateException;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -69,6 +70,7 @@ public interface PersisterFactory extends Service {
public EntityPersister createEntityPersister(
PersistentClass model,
EntityRegionAccessStrategy cacheAccessStrategy,
NaturalIdRegionAccessStrategy naturalIdAccessStrategy,
SessionFactoryImplementor factory,
Mapping cfg) throws HibernateException;

View File

@ -1,6 +1,7 @@
package org.hibernate.test.annotations.persister;
import org.hibernate.HibernateException;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.mapping.PersistentClass;
@ -11,7 +12,8 @@ import org.hibernate.persister.entity.SingleTableEntityPersister;
*/
public class EntityPersister extends SingleTableEntityPersister {
public EntityPersister(PersistentClass persistentClass, EntityRegionAccessStrategy cache,
SessionFactoryImplementor factory, Mapping cfg) throws HibernateException {
super( persistentClass, cache, factory, cfg );
NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy,
SessionFactoryImplementor factory, Mapping cfg) throws HibernateException {
super( persistentClass, cache, naturalIdRegionAccessStrategy, factory, cfg );
}
}

View File

@ -34,6 +34,7 @@ import org.hibernate.MappingException;
import org.hibernate.bytecode.spi.EntityInstrumentationMetadata;
import org.hibernate.cache.spi.access.CollectionRegionAccessStrategy;
import org.hibernate.cache.spi.access.EntityRegionAccessStrategy;
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
import org.hibernate.cache.spi.entry.CacheEntryStructure;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.CascadeStyle;
@ -381,6 +382,11 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
public EntityRegionAccessStrategy getCacheAccessStrategy() {
return null;
}
@Override
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
return null;
}
@Override
public CacheEntryStructure getCacheEntryStructure() {

View File

@ -12,6 +12,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.cache.spi.entry.UnstructuredCacheEntry;
import org.hibernate.engine.internal.TwoPhaseLoad;
@ -441,6 +442,10 @@ public class CustomPersister implements EntityPersister {
public EntityRegionAccessStrategy getCacheAccessStrategy() {
return null;
}
public NaturalIdRegionAccessStrategy getNaturalIdCacheAccessStrategy() {
return null;
}
public String getRootEntityName() {
return "CUSTOMS";