diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java
index ba4a43e284..e97d659b2e 100644
--- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java
+++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java
@@ -108,16 +108,12 @@ public void execute() throws HibernateException {
final SessionFactoryImplementor factory = getSession().getFactory();
if ( isCachePutEnabled( persister, session ) ) {
-
- CacheEntry ce = new CacheEntry(
+ CacheEntry ce = persister.buildCacheEntry(
+ instance,
getState(),
- persister,
- persister.hasUninitializedLazyProperties( instance ),
version,
- session,
- instance
- );
-
+ session
+ );
cacheEntry = persister.getCacheEntryStructure().structure(ce);
final CacheKey ck = session.generateCacheKey( id, persister.getIdentifierType(), persister.getRootEntityName() );
boolean put = persister.getCacheAccessStrategy().insert( ck, cacheEntry, version );
diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java
index db48dd1d38..ec4ad74f46 100644
--- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java
+++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java
@@ -185,14 +185,7 @@ public void execute() throws HibernateException {
}
else {
//TODO: inefficient if that cache is just going to ignore the updated state!
- CacheEntry ce = new CacheEntry(
- state,
- persister,
- persister.hasUninitializedLazyProperties( instance ),
- nextVersion,
- getSession(),
- instance
- );
+ CacheEntry ce = persister.buildCacheEntry( instance,state, nextVersion, getSession() );
cacheEntry = persister.getCacheEntryStructure().structure( ce );
boolean put = persister.getCacheAccessStrategy().update( ck, cacheEntry, nextVersion, previousVersion );
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/SharedCacheDelegate.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/SharedCacheDelegate.java
new file mode 100644
index 0000000000..4ef4792a3f
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/SharedCacheDelegate.java
@@ -0,0 +1,43 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2012, 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 org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Provides central access to putting and getting data into and out of the shared cache.
+ *
+ * Scope-wise, this delegate is per-session
+ *
+ * @author Steve Ebersole
+ */
+public interface SharedCacheDelegate {
+
+ public void storeEntity(Object entity, EntityPersister persister, Serializable id);
+ public Object retrieveEntity(EntityPersister persister, Serializable id, Object optionalEntityInstance);
+
+
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntry.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntry.java
index 69e4dc6c19..63d847563a 100644
--- a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntry.java
+++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/CacheEntry.java
@@ -25,137 +25,41 @@
import java.io.Serializable;
-import org.hibernate.AssertionFailure;
-import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
-import org.hibernate.engine.spi.SessionImplementor;
-import org.hibernate.event.service.spi.EventListenerGroup;
-import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.EventSource;
-import org.hibernate.event.spi.EventType;
-import org.hibernate.event.spi.PreLoadEvent;
-import org.hibernate.event.spi.PreLoadEventListener;
-import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.persister.entity.EntityPersister;
-import org.hibernate.type.TypeHelper;
/**
* A cached instance of a persistent class
*
* @author Gavin King
+ * @author Steve Ebersole
*/
-public final class CacheEntry implements Serializable {
+public interface CacheEntry extends Serializable {
+ public boolean isReferenceEntry();
- private final Serializable[] disassembledState;
- private final String subclass;
- private final boolean lazyPropertiesAreUnfetched;
- private final Object version;
-
- public String getSubclass() {
- return subclass;
- }
-
- public boolean areLazyPropertiesUnfetched() {
- return lazyPropertiesAreUnfetched;
- }
-
- public CacheEntry(
- final Object[] state,
- final EntityPersister persister,
- final boolean unfetched,
- final Object version,
- final SessionImplementor session,
- final Object owner)
- throws HibernateException {
- //disassembled state gets put in a new array (we write to cache by value!)
- this.disassembledState = TypeHelper.disassemble(
- state,
- persister.getPropertyTypes(),
- persister.isLazyPropertiesCacheable() ?
- null : persister.getPropertyLaziness(),
- session,
- owner
- );
- subclass = persister.getEntityName();
- lazyPropertiesAreUnfetched = unfetched || !persister.isLazyPropertiesCacheable();
- this.version = version;
- }
-
- public Object getVersion() {
- return version;
- }
+ /**
+ * Hibernate stores all entries pertaining to a given entity hierarchy in a single region. This attribute
+ * tells us the specific entity type represented by the cached data.
+ *
+ * @return The entry's exact entity type.
+ */
+ public String getSubclass();
- CacheEntry(Serializable[] state, String subclass, boolean unfetched, Object version) {
- this.disassembledState = state;
- this.subclass = subclass;
- this.lazyPropertiesAreUnfetched = unfetched;
- this.version = version;
- }
+ /**
+ * Retrieves the version (optimistic locking) associated with this cache entry.
+ *
+ * @return The version of the entity represented by this entry
+ */
+ public Object getVersion();
- public Object[] assemble(
- final Object instance,
- final Serializable id,
- final EntityPersister persister,
- final Interceptor interceptor,
- final EventSource session)
- throws HibernateException {
+ public boolean areLazyPropertiesUnfetched();
- if ( !persister.getEntityName().equals(subclass) ) {
- throw new AssertionFailure("Tried to assemble a different subclass instance");
- }
- return assemble(disassembledState, instance, id, persister, interceptor, session);
-
- }
-
- private static Object[] assemble(
- final Serializable[] values,
- final Object result,
- final Serializable id,
- final EntityPersister persister,
- final Interceptor interceptor,
- final EventSource session) throws HibernateException {
-
- //assembled state gets put in a new array (we read from cache by value!)
- Object[] assembledProps = TypeHelper.assemble(
- values,
- persister.getPropertyTypes(),
- session, result
- );
-
- //persister.setIdentifier(result, id); //before calling interceptor, for consistency with normal load
-
- //TODO: reuse the PreLoadEvent
- final PreLoadEvent preLoadEvent = new PreLoadEvent( session )
- .setEntity( result )
- .setState( assembledProps )
- .setId( id )
- .setPersister( persister );
-
- final EventListenerGroup listenerGroup = session
- .getFactory()
- .getServiceRegistry()
- .getService( EventListenerRegistry.class )
- .getEventListenerGroup( EventType.PRE_LOAD );
- for ( PreLoadEventListener listener : listenerGroup.listeners() ) {
- listener.onPreLoad( preLoadEvent );
- }
-
- persister.setPropertyValues( result, assembledProps );
-
- return assembledProps;
- }
-
- public Serializable[] getDisassembledState() {
- // todo: this was added to support initializing an entity's EntityEntry snapshot during reattach;
- // this should be refactored to instead expose a method to assemble a EntityEntry based on this
- // state for return.
- return disassembledState;
- }
-
- public String toString() {
- return "CacheEntry(" + subclass + ')' + ArrayHelper.toString(disassembledState);
- }
+ // todo: this was added to support initializing an entity's EntityEntry snapshot during reattach;
+ // this should be refactored to instead expose a method to assemble a EntityEntry based on this
+ // state for return.
+ public Serializable[] getDisassembledState();
}
diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/ReferenceCacheEntryImpl.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/ReferenceCacheEntryImpl.java
new file mode 100644
index 0000000000..95d00ed8db
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/ReferenceCacheEntryImpl.java
@@ -0,0 +1,75 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2012, 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.entry;
+
+import java.io.Serializable;
+
+import org.hibernate.Interceptor;
+import org.hibernate.event.spi.EventSource;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * @author Steve Ebersole
+ */
+public class ReferenceCacheEntryImpl implements CacheEntry {
+ private final Object reference;
+ private final String subclass;
+
+ public ReferenceCacheEntryImpl(Object reference, String subclass) {
+ this.reference = reference;
+ this.subclass = subclass;
+ }
+
+ @Override
+ public boolean isReferenceEntry() {
+ return true;
+ }
+
+ @Override
+ public String getSubclass() {
+ return subclass;
+ }
+
+ @Override
+ public Object getVersion() {
+ // reference data cannot be versioned
+ return null;
+ }
+
+ @Override
+ public boolean areLazyPropertiesUnfetched() {
+ // reference data cannot define lazy attributes
+ return false;
+ }
+
+ @Override
+ public Serializable[] getDisassembledState() {
+ // reference data is not disassembled into the cache
+ return null;
+ }
+
+ public Object getReference() {
+ return reference;
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StandardCacheEntryImpl.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StandardCacheEntryImpl.java
new file mode 100644
index 0000000000..51f9fceacd
--- /dev/null
+++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StandardCacheEntryImpl.java
@@ -0,0 +1,170 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2012, 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.entry;
+
+import java.io.Serializable;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.Interceptor;
+import org.hibernate.engine.spi.SessionImplementor;
+import org.hibernate.event.service.spi.EventListenerGroup;
+import org.hibernate.event.service.spi.EventListenerRegistry;
+import org.hibernate.event.spi.EventSource;
+import org.hibernate.event.spi.EventType;
+import org.hibernate.event.spi.PreLoadEvent;
+import org.hibernate.event.spi.PreLoadEventListener;
+import org.hibernate.internal.util.collections.ArrayHelper;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.type.TypeHelper;
+
+/**
+ * @author Steve Ebersole
+ */
+public class StandardCacheEntryImpl implements CacheEntry {
+ private final Serializable[] disassembledState;
+ private final String subclass;
+ private final boolean lazyPropertiesAreUnfetched;
+ private final Object version;
+
+ @Override
+ public boolean isReferenceEntry() {
+ return false;
+ }
+
+ @Override
+ public Serializable[] getDisassembledState() {
+ // todo: this was added to support initializing an entity's EntityEntry snapshot during reattach;
+ // this should be refactored to instead expose a method to assemble a EntityEntry based on this
+ // state for return.
+ return disassembledState;
+ }
+
+ @Override
+ public String getSubclass() {
+ return subclass;
+ }
+
+ @Override
+ public boolean areLazyPropertiesUnfetched() {
+ return lazyPropertiesAreUnfetched;
+ }
+
+ @Override
+ public Object getVersion() {
+ return version;
+ }
+
+ public StandardCacheEntryImpl(
+ final Object[] state,
+ final EntityPersister persister,
+ final boolean unfetched,
+ final Object version,
+ final SessionImplementor session,
+ final Object owner)
+ throws HibernateException {
+ //disassembled state gets put in a new array (we write to cache by value!)
+ this.disassembledState = TypeHelper.disassemble(
+ state,
+ persister.getPropertyTypes(),
+ persister.isLazyPropertiesCacheable() ?
+ null : persister.getPropertyLaziness(),
+ session,
+ owner
+ );
+ subclass = persister.getEntityName();
+ lazyPropertiesAreUnfetched = unfetched || !persister.isLazyPropertiesCacheable();
+ this.version = version;
+ }
+
+ StandardCacheEntryImpl(Serializable[] state, String subclass, boolean unfetched, Object version) {
+ this.disassembledState = state;
+ this.subclass = subclass;
+ this.lazyPropertiesAreUnfetched = unfetched;
+ this.version = version;
+ }
+
+ public boolean isDeepCopyNeeded() {
+ // for now always return true.
+ // todo : See discussion on HHH-7872
+ return true;
+ }
+
+ public Object[] assemble(
+ final Object instance,
+ final Serializable id,
+ final EntityPersister persister,
+ final Interceptor interceptor,
+ final EventSource session)
+ throws HibernateException {
+
+ if ( !persister.getEntityName().equals(subclass) ) {
+ throw new AssertionFailure("Tried to assemble a different subclass instance");
+ }
+
+ return assemble(disassembledState, instance, id, persister, interceptor, session);
+ }
+
+ private static Object[] assemble(
+ final Serializable[] values,
+ final Object result,
+ final Serializable id,
+ final EntityPersister persister,
+ final Interceptor interceptor,
+ final EventSource session) throws HibernateException {
+
+ //assembled state gets put in a new array (we read from cache by value!)
+ Object[] assembledProps = TypeHelper.assemble(
+ values,
+ persister.getPropertyTypes(),
+ session, result
+ );
+
+ //persister.setIdentifier(result, id); //before calling interceptor, for consistency with normal load
+
+ //TODO: reuse the PreLoadEvent
+ final PreLoadEvent preLoadEvent = new PreLoadEvent( session )
+ .setEntity( result )
+ .setState( assembledProps )
+ .setId( id )
+ .setPersister( persister );
+
+ final EventListenerGroup listenerGroup = session
+ .getFactory()
+ .getServiceRegistry()
+ .getService( EventListenerRegistry.class )
+ .getEventListenerGroup( EventType.PRE_LOAD );
+ for ( PreLoadEventListener listener : listenerGroup.listeners() ) {
+ listener.onPreLoad( preLoadEvent );
+ }
+
+ persister.setPropertyValues( result, assembledProps );
+
+ return assembledProps;
+ }
+
+ public String toString() {
+ return "CacheEntry(" + subclass + ')' + ArrayHelper.toString( disassembledState );
+ }
+}
diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StructuredCacheEntry.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StructuredCacheEntry.java
index 095f91606c..90e1f86952 100755
--- a/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StructuredCacheEntry.java
+++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/entry/StructuredCacheEntry.java
@@ -31,7 +31,11 @@
import org.hibernate.persister.entity.EntityPersister;
/**
+ * Structured CacheEntry format for entities. Used to store the entry into the second-level cache
+ * as a Map so that users can more easily see the cached state.
+ *
* @author Gavin King
+ * @author Steve Ebersole
*/
public class StructuredCacheEntry implements CacheEntryStructure {
@@ -52,7 +56,7 @@ public Object destructure(Object item, SessionFactoryImplementor factory) {
for ( int i=0; i classMeta = new HashMap();
@@ -380,7 +382,7 @@ public void sessionFactoryClosed(SessionFactory factory) {
}
}
- EntityPersister cp = serviceRegistry.getService( PersisterFactory.class ).createEntityPersister(
+ EntityPersister cp = persisterFactory.createEntityPersister(
model,
accessStrategy,
naturalIdAccessStrategy,
@@ -409,7 +411,7 @@ public void sessionFactoryClosed(SessionFactory factory) {
entityAccessStrategies.put( cacheRegionName, accessStrategy );
cacheAccess.addCacheRegion( cacheRegionName, collectionRegion );
}
- CollectionPersister persister = serviceRegistry.getService( PersisterFactory.class ).createCollectionPersister(
+ CollectionPersister persister = persisterFactory.createCollectionPersister(
cfg,
model,
accessStrategy,
diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
index dbf1f76376..c56a4e924e 100644
--- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
+++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java
@@ -56,6 +56,8 @@
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.ReferenceCacheEntryImpl;
+import org.hibernate.cache.spi.entry.StandardCacheEntryImpl;
import org.hibernate.cache.spi.entry.StructuredCacheEntry;
import org.hibernate.cache.spi.entry.UnstructuredCacheEntry;
import org.hibernate.dialect.lock.LockingStrategy;
@@ -149,7 +151,7 @@ public abstract class AbstractEntityPersister
private final EntityRegionAccessStrategy cacheAccessStrategy;
private final NaturalIdRegionAccessStrategy naturalIdRegionAccessStrategy;
private final boolean isLazyPropertiesCacheable;
- private final CacheEntryStructure cacheEntryStructure;
+ private final CacheEntryHelper cacheEntryHelper;
private final EntityMetamodel entityMetamodel;
private final EntityTuplizer entityTuplizer;
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -501,9 +503,6 @@ public AbstractEntityPersister(
this.cacheAccessStrategy = cacheAccessStrategy;
this.naturalIdRegionAccessStrategy = naturalIdRegionAccessStrategy;
isLazyPropertiesCacheable = persistentClass.isLazyPropertiesCacheable();
- this.cacheEntryStructure = factory.getSettings().isStructuredCacheEntriesEnabled() ?
- (CacheEntryStructure) new StructuredCacheEntry(this) :
- (CacheEntryStructure) new UnstructuredCacheEntry();
this.entityMetamodel = new EntityMetamodel( persistentClass, factory );
this.entityTuplizer = this.entityMetamodel.getTuplizer();
@@ -778,6 +777,48 @@ public AbstractEntityPersister(
temporaryIdTableName = persistentClass.getTemporaryIdTableName();
temporaryIdTableDDL = persistentClass.getTemporaryIdTableDDL();
+
+ this.cacheEntryHelper = buildCacheEntryHelper();
+ }
+
+ protected CacheEntryHelper buildCacheEntryHelper() {
+ if ( cacheAccessStrategy == null ) {
+ // the entity defined no caching...
+ return NoopCacheEntryHelper.INSTANCE;
+ }
+
+ if ( canUseReferenceCacheEntries() ) {
+ entityMetamodel.setLazy( false );
+ // todo : do we also need to unset proxy factory?
+ return new ReferenceCacheEntryHelper( this );
+ }
+
+ return factory.getSettings().isStructuredCacheEntriesEnabled()
+ ? new StructuredCacheEntryHelper( this )
+ : new StandardCacheEntryHelper( this );
+ }
+
+ protected boolean canUseReferenceCacheEntries() {
+ // todo : should really validate that the cache access type is read-only
+
+ if ( ! factory.getSettings().isDirectReferenceCacheEntriesEnabled() ) {
+ return false;
+ }
+
+ // for now, limit this to just entities that:
+ // 1) are immutable
+ if ( entityMetamodel.isMutable() ) {
+ return false;
+ }
+
+ // 2) have no associations. Eventually we want to be a little more lenient with associations.
+ for ( Type type : getSubclassPropertyTypeClosure() ) {
+ if ( type.isAssociationType() ) {
+ return false;
+ }
+ }
+
+ return true;
}
@@ -793,10 +834,6 @@ public AbstractEntityPersister(
entityBinding.getHierarchyDetails().getCaching() == null ?
false :
entityBinding.getHierarchyDetails().getCaching().isCacheLazyProperties();
- this.cacheEntryStructure =
- factory.getSettings().isStructuredCacheEntriesEnabled() ?
- new StructuredCacheEntry(this) :
- new UnstructuredCacheEntry();
this.entityMetamodel = new EntityMetamodel( entityBinding, factory );
this.entityTuplizer = this.entityMetamodel.getTuplizer();
int batch = entityBinding.getBatchSize();
@@ -1104,6 +1141,8 @@ public AbstractEntityPersister(
temporaryIdTableName = null;
temporaryIdTableDDL = null;
+
+ this.cacheEntryHelper = buildCacheEntryHelper();
}
protected static String getTemplateFromString(String string, SessionFactoryImplementor factory) {
@@ -4066,10 +4105,16 @@ public EntityRegionAccessStrategy getCacheAccessStrategy() {
return cacheAccessStrategy;
}
+ @Override
public CacheEntryStructure getCacheEntryStructure() {
- return cacheEntryStructure;
+ return cacheEntryHelper.getCacheEntryStructure();
}
-
+
+ @Override
+ public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) {
+ return cacheEntryHelper.buildCacheEntry( entity, state, version, session );
+ }
+
public boolean hasNaturalIdCache() {
return naturalIdRegionAccessStrategy != null;
}
@@ -4925,5 +4970,97 @@ public String getTableAliasForColumn(String columnName, String rootAlias) {
public int determineTableNumberForColumn(String columnName) {
return 0;
}
-
+
+ /**
+ * Consolidated these onto a single helper because the 2 pieces work in tandem.
+ */
+ public static interface CacheEntryHelper {
+ public CacheEntryStructure getCacheEntryStructure();
+
+ public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session);
+ }
+
+ private static class StandardCacheEntryHelper implements CacheEntryHelper {
+ private final EntityPersister persister;
+
+ private StandardCacheEntryHelper(EntityPersister persister) {
+ this.persister = persister;
+ }
+
+ @Override
+ public CacheEntryStructure getCacheEntryStructure() {
+ return UnstructuredCacheEntry.INSTANCE;
+ }
+
+ @Override
+ public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) {
+ return new StandardCacheEntryImpl(
+ state,
+ persister,
+ persister.hasUninitializedLazyProperties( entity ),
+ version,
+ session,
+ entity
+ );
+ }
+ }
+
+ private static class ReferenceCacheEntryHelper implements CacheEntryHelper {
+ private final EntityPersister persister;
+
+ private ReferenceCacheEntryHelper(EntityPersister persister) {
+ this.persister = persister;
+ }
+
+ @Override
+ public CacheEntryStructure getCacheEntryStructure() {
+ return UnstructuredCacheEntry.INSTANCE;
+ }
+
+ @Override
+ public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) {
+ return new ReferenceCacheEntryImpl( entity, persister.getEntityName() );
+ }
+ }
+
+ private static class StructuredCacheEntryHelper implements CacheEntryHelper {
+ private final EntityPersister persister;
+ private final StructuredCacheEntry structure;
+
+ private StructuredCacheEntryHelper(EntityPersister persister) {
+ this.persister = persister;
+ this.structure = new StructuredCacheEntry( persister );
+ }
+
+ @Override
+ public CacheEntryStructure getCacheEntryStructure() {
+ return structure;
+ }
+
+ @Override
+ public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) {
+ return new StandardCacheEntryImpl(
+ state,
+ persister,
+ persister.hasUninitializedLazyProperties( entity ),
+ version,
+ session,
+ entity
+ );
+ }
+ }
+
+ private static class NoopCacheEntryHelper implements CacheEntryHelper {
+ public static final NoopCacheEntryHelper INSTANCE = new NoopCacheEntryHelper();
+
+ @Override
+ public CacheEntryStructure getCacheEntryStructure() {
+ return UnstructuredCacheEntry.INSTANCE;
+ }
+
+ @Override
+ public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session) {
+ throw new HibernateException( "Illegal attempt to build cache entry for non-cached entity" );
+ }
+ }
}
diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java
index 29b4faa7ac..f95ea71970 100644
--- a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java
+++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java
@@ -35,6 +35,7 @@
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.CacheEntry;
import org.hibernate.cache.spi.entry.CacheEntryStructure;
import org.hibernate.engine.spi.CascadeStyle;
import org.hibernate.engine.spi.SessionFactoryImplementor;
@@ -475,7 +476,9 @@ public void update(
* Get the cache structure
*/
public CacheEntryStructure getCacheEntryStructure();
-
+
+ public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SessionImplementor session);
+
/**
* Does this class have a natural id cache
*/
diff --git a/hibernate-core/src/test/java/org/hibernate/test/cache/ReferenceCacheTest.java b/hibernate-core/src/test/java/org/hibernate/test/cache/ReferenceCacheTest.java
new file mode 100644
index 0000000000..0d65243ea2
--- /dev/null
+++ b/hibernate-core/src/test/java/org/hibernate/test/cache/ReferenceCacheTest.java
@@ -0,0 +1,140 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2012, 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.test.cache;
+
+import javax.persistence.Cacheable;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+import org.hibernate.Session;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.hibernate.annotations.Immutable;
+import org.hibernate.cfg.AvailableSettings;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.persister.entity.EntityPersister;
+
+import org.junit.Test;
+
+import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author Steve Ebersole
+ */
+public class ReferenceCacheTest extends BaseCoreFunctionalTestCase {
+ @Override
+ protected void configure(Configuration configuration) {
+ super.configure( configuration );
+ configuration.setProperty( AvailableSettings.USE_DIRECT_REFERENCE_CACHE_ENTRIES, "true" );
+ }
+
+ @Override
+ protected Class>[] getAnnotatedClasses() {
+ return new Class[] { MyReferenceData.class };
+ }
+
+ @Test
+ public void testUseOfDirectReferencesInCache() throws Exception {
+ EntityPersister persister = (EntityPersister) sessionFactory().getClassMetadata( MyReferenceData.class );
+ assertFalse( persister.isMutable() );
+ assertTrue( persister.buildCacheEntry( null, null, null, null ).isReferenceEntry() );
+ assertFalse( persister.hasProxy() );
+
+ final MyReferenceData myReferenceData = new MyReferenceData( 1, "first item", "abc" );
+
+ // save a reference in one session
+ Session s = openSession();
+ s.beginTransaction();
+ s.save( myReferenceData );
+ s.getTransaction().commit();
+ s.close();
+
+ // now load it in another
+ s = openSession();
+ s.beginTransaction();
+// MyReferenceData loaded = (MyReferenceData) s.get( MyReferenceData.class, 1 );
+ MyReferenceData loaded = (MyReferenceData) s.load( MyReferenceData.class, 1 );
+ s.getTransaction().commit();
+ s.close();
+
+ // the 2 instances should be the same (==)
+ assertTrue( "The two instances were different references", myReferenceData == loaded );
+
+ // cleanup
+ s = openSession();
+ s.beginTransaction();
+ s.delete( myReferenceData );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ @Entity( name="MyReferenceData" )
+ @Immutable
+ @Cacheable
+ @Cache( usage = CacheConcurrencyStrategy.READ_ONLY )
+// @Proxy( lazy = false )
+ @SuppressWarnings("UnusedDeclaration")
+ public static class MyReferenceData {
+ @Id
+ private Integer id;
+ private String name;
+ private String theValue;
+
+ public MyReferenceData(Integer id, String name, String theValue) {
+ this.id = id;
+ this.name = name;
+ this.theValue = theValue;
+ }
+
+ protected MyReferenceData() {
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getTheValue() {
+ return theValue;
+ }
+
+ public void setTheValue(String theValue) {
+ this.theValue = theValue;
+ }
+ }
+}
diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java
index ee64cfb2d7..3e32129981 100644
--- a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java
+++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java
@@ -35,6 +35,7 @@
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.CacheEntry;
import org.hibernate.cache.spi.entry.CacheEntryStructure;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.engine.spi.CascadeStyle;
@@ -400,6 +401,12 @@ public CacheEntryStructure getCacheEntryStructure() {
return null;
}
+ @Override
+ public CacheEntry buildCacheEntry(
+ Object entity, Object[] state, Object version, SessionImplementor session) {
+ return null;
+ }
+
@Override
public ClassMetadata getClassMetadata() {
return null;
diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java
index 3289c7a75d..62258c377b 100644
--- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java
+++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java
@@ -13,7 +13,9 @@
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.CacheEntry;
import org.hibernate.cache.spi.entry.CacheEntryStructure;
+import org.hibernate.cache.spi.entry.StandardCacheEntryImpl;
import org.hibernate.cache.spi.entry.UnstructuredCacheEntry;
import org.hibernate.engine.internal.TwoPhaseLoad;
import org.hibernate.engine.spi.CascadeStyle;
@@ -566,6 +568,19 @@ public CacheEntryStructure getCacheEntryStructure() {
return new UnstructuredCacheEntry();
}
+ @Override
+ public CacheEntry buildCacheEntry(
+ Object entity, Object[] state, Object version, SessionImplementor session) {
+ return new StandardCacheEntryImpl(
+ state,
+ this,
+ this.hasUninitializedLazyProperties( entity ),
+ version,
+ session,
+ entity
+ );
+ }
+
@Override
public boolean hasSubselectLoadableCollections() {
return false;