HHH-7085 - Entities marked as @Immutable that have a @NaturalId fail to be inserted with NPE
This commit is contained in:
parent
f1a54aeadf
commit
9f4fd48603
|
@ -0,0 +1,325 @@
|
||||||
|
/*
|
||||||
|
* 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.engine.internal;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
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.engine.spi.CachedNaturalIdValueSource;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.engine.spi.SessionImplementor;
|
||||||
|
import org.hibernate.event.spi.EventSource;
|
||||||
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maintains a {@link org.hibernate.engine.spi.PersistenceContext}-level 2-way cross-reference (xref) between the
|
||||||
|
* identifiers and natural ids of entities associated with the PersistenceContext. Additionally coordinates
|
||||||
|
* actions related to the shared caching of the entity's natural id.
|
||||||
|
*
|
||||||
|
* @author Steve Ebersole
|
||||||
|
*/
|
||||||
|
public class NaturalIdXrefDelegate {
|
||||||
|
private static final Logger LOG = Logger.getLogger( NaturalIdXrefDelegate.class );
|
||||||
|
|
||||||
|
private final StatefulPersistenceContext persistenceContext;
|
||||||
|
private final Map<EntityPersister, NaturalIdResolutionCache> naturalIdResolutionCacheMap = new ConcurrentHashMap<EntityPersister, NaturalIdResolutionCache>();
|
||||||
|
|
||||||
|
public NaturalIdXrefDelegate(StatefulPersistenceContext persistenceContext) {
|
||||||
|
this.persistenceContext = persistenceContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SessionImplementor session() {
|
||||||
|
return persistenceContext.getSession();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cacheNaturalIdResolution(
|
||||||
|
EntityPersister persister,
|
||||||
|
final Serializable pk,
|
||||||
|
Object[] naturalIdValues,
|
||||||
|
CachedNaturalIdValueSource valueSource) {
|
||||||
|
validateNaturalId( persister, naturalIdValues );
|
||||||
|
|
||||||
|
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
||||||
|
if ( entityNaturalIdResolutionCache == null ) {
|
||||||
|
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
|
||||||
|
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
|
||||||
|
}
|
||||||
|
|
||||||
|
final CachedNaturalId cachedNaturalId = new CachedNaturalId( persister, naturalIdValues );
|
||||||
|
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, cachedNaturalId );
|
||||||
|
entityNaturalIdResolutionCache.naturalIdToPkMap.put( cachedNaturalId, pk );
|
||||||
|
|
||||||
|
//If second-level caching is enabled cache the resolution there as well
|
||||||
|
if ( persister.hasNaturalIdCache() ) {
|
||||||
|
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
|
||||||
|
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( naturalIdValues, persister, session() );
|
||||||
|
|
||||||
|
final SessionFactoryImplementor factory = session().getFactory();
|
||||||
|
|
||||||
|
switch ( valueSource ) {
|
||||||
|
case LOAD: {
|
||||||
|
final boolean put = naturalIdCacheAccessStrategy.putFromLoad(
|
||||||
|
naturalIdCacheKey,
|
||||||
|
pk,
|
||||||
|
session().getTimestamp(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
||||||
|
factory.getStatisticsImplementor()
|
||||||
|
.naturalIdCachePut( naturalIdCacheAccessStrategy.getRegion().getName() );
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case INSERT: {
|
||||||
|
naturalIdCacheAccessStrategy.insert( naturalIdCacheKey, pk );
|
||||||
|
|
||||||
|
( (EventSource) session() ).getActionQueue().registerProcess(
|
||||||
|
new AfterTransactionCompletionProcess() {
|
||||||
|
@Override
|
||||||
|
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
|
||||||
|
final boolean put = naturalIdCacheAccessStrategy.afterInsert( naturalIdCacheKey, pk );
|
||||||
|
|
||||||
|
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
||||||
|
factory.getStatisticsImplementor().naturalIdCachePut(
|
||||||
|
naturalIdCacheAccessStrategy.getRegion().getName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UPDATE: {
|
||||||
|
final SoftLock lock = naturalIdCacheAccessStrategy.lockItem( naturalIdCacheKey, null );
|
||||||
|
naturalIdCacheAccessStrategy.update( naturalIdCacheKey, pk );
|
||||||
|
|
||||||
|
( (EventSource) session() ).getActionQueue().registerProcess(
|
||||||
|
new AfterTransactionCompletionProcess() {
|
||||||
|
@Override
|
||||||
|
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
|
||||||
|
final boolean put = naturalIdCacheAccessStrategy.afterUpdate( naturalIdCacheKey, pk, lock );
|
||||||
|
|
||||||
|
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
||||||
|
factory.getStatisticsImplementor().naturalIdCachePut(
|
||||||
|
naturalIdCacheAccessStrategy.getRegion().getName() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void validateNaturalId(EntityPersister persister, Object[] naturalIdValues) {
|
||||||
|
if ( !persister.hasNaturalIdentifier() ) {
|
||||||
|
throw new IllegalArgumentException( "Entity did not define a natrual-id" );
|
||||||
|
}
|
||||||
|
if ( persister.getNaturalIdentifierProperties().length != naturalIdValues.length ) {
|
||||||
|
throw new IllegalArgumentException( "Mismatch between expected number of natural-id values and found." );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void evictNaturalIdResolution(EntityPersister persister, final Serializable pk, Object[] deletedNaturalIdValues) {
|
||||||
|
validateNaturalId( persister, deletedNaturalIdValues );
|
||||||
|
|
||||||
|
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
||||||
|
Object[] sessionCachedNaturalIdValues = null;
|
||||||
|
if ( entityNaturalIdResolutionCache != null ) {
|
||||||
|
final CachedNaturalId cachedNaturalId = entityNaturalIdResolutionCache.pkToNaturalIdMap
|
||||||
|
.remove( pk );
|
||||||
|
if ( cachedNaturalId != null ) {
|
||||||
|
entityNaturalIdResolutionCache.naturalIdToPkMap.remove( cachedNaturalId );
|
||||||
|
sessionCachedNaturalIdValues = cachedNaturalId.getValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( persister.hasNaturalIdCache() ) {
|
||||||
|
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister
|
||||||
|
.getNaturalIdCacheAccessStrategy();
|
||||||
|
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( deletedNaturalIdValues, persister, session() );
|
||||||
|
naturalIdCacheAccessStrategy.evict( naturalIdCacheKey );
|
||||||
|
|
||||||
|
if ( sessionCachedNaturalIdValues != null
|
||||||
|
&& !Arrays.equals( sessionCachedNaturalIdValues, deletedNaturalIdValues ) ) {
|
||||||
|
final NaturalIdCacheKey sessionNaturalIdCacheKey = new NaturalIdCacheKey( sessionCachedNaturalIdValues, persister, session() );
|
||||||
|
naturalIdCacheAccessStrategy.evict( sessionNaturalIdCacheKey );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] findCachedNaturalId(EntityPersister persister, Serializable pk) {
|
||||||
|
final NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
||||||
|
if ( entityNaturalIdResolutionCache == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CachedNaturalId cachedNaturalId = entityNaturalIdResolutionCache.pkToNaturalIdMap.get( pk );
|
||||||
|
if ( cachedNaturalId == null ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cachedNaturalId.getValues();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Serializable findCachedNaturalIdResolution(EntityPersister persister, Object[] naturalIdValues) {
|
||||||
|
validateNaturalId( persister, naturalIdValues );
|
||||||
|
|
||||||
|
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
||||||
|
|
||||||
|
Serializable pk;
|
||||||
|
final CachedNaturalId cachedNaturalId = new CachedNaturalId( persister, naturalIdValues );
|
||||||
|
if ( entityNaturalIdResolutionCache != null ) {
|
||||||
|
pk = entityNaturalIdResolutionCache.naturalIdToPkMap.get( cachedNaturalId );
|
||||||
|
|
||||||
|
// Found in session cache
|
||||||
|
if ( pk != null ) {
|
||||||
|
if ( LOG.isTraceEnabled() ) {
|
||||||
|
LOG.trace(
|
||||||
|
"Resolved natural key -> primary key resolution in session cache: " +
|
||||||
|
persister.getRootEntityName() + "#[" +
|
||||||
|
Arrays.toString( naturalIdValues ) + "]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session cache miss, see if second-level caching is enabled
|
||||||
|
if ( !persister.hasNaturalIdCache() ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try resolution from second-level cache
|
||||||
|
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( naturalIdValues, persister, session() );
|
||||||
|
|
||||||
|
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
|
||||||
|
pk = (Serializable) naturalIdCacheAccessStrategy.get( naturalIdCacheKey, session().getTimestamp() );
|
||||||
|
|
||||||
|
// Found in second-level cache, store in session cache
|
||||||
|
final SessionFactoryImplementor factory = session().getFactory();
|
||||||
|
if ( pk != null ) {
|
||||||
|
if ( factory.getStatistics().isStatisticsEnabled() ) {
|
||||||
|
factory.getStatisticsImplementor().naturalIdCacheHit(
|
||||||
|
naturalIdCacheAccessStrategy.getRegion().getName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( LOG.isTraceEnabled() ) {
|
||||||
|
// protected to avoid Arrays.toString call unless needed
|
||||||
|
LOG.tracef(
|
||||||
|
"Found natural key [%s] -> primary key [%s] xref in second-level cache for %s",
|
||||||
|
Arrays.toString( naturalIdValues ),
|
||||||
|
pk,
|
||||||
|
persister.getRootEntityName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( entityNaturalIdResolutionCache == null ) {
|
||||||
|
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
|
||||||
|
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
|
||||||
|
}
|
||||||
|
|
||||||
|
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, cachedNaturalId );
|
||||||
|
entityNaturalIdResolutionCache.naturalIdToPkMap.put( cachedNaturalId, pk );
|
||||||
|
}
|
||||||
|
else if ( factory.getStatistics().isStatisticsEnabled() ) {
|
||||||
|
factory.getStatisticsImplementor().naturalIdCacheMiss( naturalIdCacheAccessStrategy.getRegion().getName() );
|
||||||
|
}
|
||||||
|
|
||||||
|
return pk;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class CachedNaturalId {
|
||||||
|
private final EntityPersister persister;
|
||||||
|
private final Object[] values;
|
||||||
|
private int hashCode;
|
||||||
|
|
||||||
|
public CachedNaturalId(EntityPersister persister, Object[] values) {
|
||||||
|
this.persister = persister;
|
||||||
|
this.values = values;
|
||||||
|
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + ( ( persister == null ) ? 0 : persister.hashCode() );
|
||||||
|
result = prime * result + Arrays.hashCode( values );
|
||||||
|
this.hashCode = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] getValues() {
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CachedNaturalId other = (CachedNaturalId) obj;
|
||||||
|
return persister.equals( other.persister )
|
||||||
|
&& Arrays.equals( values, other.values );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NaturalIdResolutionCache implements Serializable {
|
||||||
|
private final EntityPersister persister;
|
||||||
|
|
||||||
|
private NaturalIdResolutionCache(EntityPersister persister) {
|
||||||
|
this.persister = persister;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityPersister getPersister() {
|
||||||
|
return persister;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<Serializable, CachedNaturalId> pkToNaturalIdMap = new ConcurrentHashMap<Serializable, CachedNaturalId>();
|
||||||
|
private Map<CachedNaturalId, Serializable> naturalIdToPkMap = new ConcurrentHashMap<CachedNaturalId, Serializable>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,7 +29,6 @@ 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;
|
||||||
|
@ -37,10 +36,11 @@ import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
import org.apache.commons.collections.map.AbstractReferenceMap;
|
import org.apache.commons.collections.map.AbstractReferenceMap;
|
||||||
import org.apache.commons.collections.map.ReferenceMap;
|
import org.apache.commons.collections.map.ReferenceMap;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
import org.hibernate.AssertionFailure;
|
import org.hibernate.AssertionFailure;
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
|
@ -49,24 +49,19 @@ import org.hibernate.MappingException;
|
||||||
import org.hibernate.NonUniqueObjectException;
|
import org.hibernate.NonUniqueObjectException;
|
||||||
import org.hibernate.PersistentObjectException;
|
import org.hibernate.PersistentObjectException;
|
||||||
import org.hibernate.TransientObjectException;
|
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.collection.spi.PersistentCollection;
|
||||||
import org.hibernate.engine.loading.internal.LoadContexts;
|
import org.hibernate.engine.loading.internal.LoadContexts;
|
||||||
import org.hibernate.engine.spi.AssociationKey;
|
import org.hibernate.engine.spi.AssociationKey;
|
||||||
import org.hibernate.engine.spi.BatchFetchQueue;
|
import org.hibernate.engine.spi.BatchFetchQueue;
|
||||||
|
import org.hibernate.engine.spi.CachedNaturalIdValueSource;
|
||||||
import org.hibernate.engine.spi.CollectionEntry;
|
import org.hibernate.engine.spi.CollectionEntry;
|
||||||
import org.hibernate.engine.spi.CollectionKey;
|
import org.hibernate.engine.spi.CollectionKey;
|
||||||
import org.hibernate.engine.spi.EntityEntry;
|
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.internal.CoreMessageLogger;
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.util.MarkerObject;
|
import org.hibernate.internal.util.MarkerObject;
|
||||||
import org.hibernate.internal.util.collections.IdentityMap;
|
import org.hibernate.internal.util.collections.IdentityMap;
|
||||||
|
@ -76,7 +71,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.jboss.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A <strong>stateful</strong> implementation of the {@link PersistenceContext} contract meaning that we maintain this
|
* A <strong>stateful</strong> implementation of the {@link PersistenceContext} contract meaning that we maintain this
|
||||||
|
@ -1704,8 +1698,10 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
|
|
||||||
// NATURAL ID RESOLUTION HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// NATURAL ID RESOLUTION HANDLING ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
private NaturalIdXrefDelegate naturalIdXrefDelegate = new NaturalIdXrefDelegate( this );
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadedStateInsertedNotification(EntityEntry entityEntry, Object[] state) {
|
public void entityStateInsertedNotification(EntityEntry entityEntry, Object[] state) {
|
||||||
final EntityPersister persister = entityEntry.getPersister();
|
final EntityPersister persister = entityEntry.getPersister();
|
||||||
if ( !persister.hasNaturalIdentifier() ) {
|
if ( !persister.hasNaturalIdentifier() ) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
@ -1715,11 +1711,16 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
final Object[] naturalIdValues = getNaturalIdValues( state, persister );
|
final Object[] naturalIdValues = getNaturalIdValues( state, persister );
|
||||||
|
|
||||||
// cache
|
// cache
|
||||||
cacheNaturalIdResolution( persister, entityEntry.getId(), naturalIdValues, CachedNaturalIdValueSource.INSERT );
|
naturalIdXrefDelegate.cacheNaturalIdResolution(
|
||||||
|
persister,
|
||||||
|
entityEntry.getId(),
|
||||||
|
naturalIdValues,
|
||||||
|
CachedNaturalIdValueSource.INSERT
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadedStateUpdatedNotification(EntityEntry entityEntry, Object[] state) {
|
public void entityStateUpdatedNotification(EntityEntry entityEntry, Object[] state) {
|
||||||
final EntityPersister persister = entityEntry.getPersister();
|
final EntityPersister persister = entityEntry.getPersister();
|
||||||
if ( !persister.hasNaturalIdentifier() ) {
|
if ( !persister.hasNaturalIdentifier() ) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
@ -1729,11 +1730,16 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
final Object[] naturalIdValues = getNaturalIdValues( state, persister );
|
final Object[] naturalIdValues = getNaturalIdValues( state, persister );
|
||||||
|
|
||||||
// re-cache
|
// re-cache
|
||||||
cacheNaturalIdResolution( persister, entityEntry.getId(), naturalIdValues, CachedNaturalIdValueSource.UPDATE );
|
naturalIdXrefDelegate.cacheNaturalIdResolution(
|
||||||
|
persister,
|
||||||
|
entityEntry.getId(),
|
||||||
|
naturalIdValues,
|
||||||
|
CachedNaturalIdValueSource.UPDATE
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadedStateDeletedNotification(EntityEntry entityEntry, Object[] deletedState) {
|
public void entityStateDeletedNotification(EntityEntry entityEntry, Object[] deletedState) {
|
||||||
final EntityPersister persister = entityEntry.getPersister();
|
final EntityPersister persister = entityEntry.getPersister();
|
||||||
if ( !persister.hasNaturalIdentifier() ) {
|
if ( !persister.hasNaturalIdentifier() ) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
@ -1743,7 +1749,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
final Object[] naturalIdValues = getNaturalIdValues( deletedState, persister );
|
final Object[] naturalIdValues = getNaturalIdValues( deletedState, persister );
|
||||||
|
|
||||||
// evict from cache
|
// evict from cache
|
||||||
evictNaturalIdResolution( persister, entityEntry.getId(), naturalIdValues );
|
naturalIdXrefDelegate.evictNaturalIdResolution( persister, entityEntry.getId(), naturalIdValues );
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object[] getNaturalIdValues(Object[] state, EntityPersister persister) {
|
private Object[] getNaturalIdValues(Object[] state, EntityPersister persister) {
|
||||||
|
@ -1757,270 +1763,23 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
||||||
return naturalIdValues;
|
return naturalIdValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class LocalNaturalIdCacheKey {
|
|
||||||
private final EntityPersister persister;
|
|
||||||
private final Object[] values;
|
|
||||||
private int hashCode;
|
|
||||||
|
|
||||||
public LocalNaturalIdCacheKey(EntityPersister persister, Object[] values) {
|
|
||||||
this.persister = persister;
|
|
||||||
this.values = values;
|
|
||||||
|
|
||||||
final int prime = 31;
|
|
||||||
int result = 1;
|
|
||||||
result = prime * result + ( ( persister == null ) ? 0 : persister.hashCode() );
|
|
||||||
result = prime * result + Arrays.hashCode( values );
|
|
||||||
this.hashCode = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object[] getValues() {
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
final LocalNaturalIdCacheKey other = (LocalNaturalIdCacheKey) obj;
|
|
||||||
return persister.equals( other.persister )
|
|
||||||
&& Arrays.equals( values, other.values );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class NaturalIdResolutionCache implements Serializable {
|
|
||||||
private final EntityPersister persister;
|
|
||||||
|
|
||||||
private NaturalIdResolutionCache(EntityPersister persister) {
|
|
||||||
this.persister = persister;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EntityPersister getPersister() {
|
|
||||||
return persister;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<Serializable, LocalNaturalIdCacheKey> pkToNaturalIdMap = new ConcurrentHashMap<Serializable, LocalNaturalIdCacheKey>();
|
|
||||||
private Map<LocalNaturalIdCacheKey, Serializable> naturalIdToPkMap = new ConcurrentHashMap<LocalNaturalIdCacheKey, Serializable>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateNaturalId(EntityPersister persister, Object[] naturalIdValues) {
|
|
||||||
if ( !persister.hasNaturalIdentifier() ) {
|
|
||||||
throw new IllegalArgumentException( "Entity did not define a natrual-id" );
|
|
||||||
}
|
|
||||||
if ( persister.getNaturalIdentifierProperties().length != naturalIdValues.length ) {
|
|
||||||
throw new IllegalArgumentException( "Mismatch between expected number of natural-id values and found." );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Map<EntityPersister, NaturalIdResolutionCache> naturalIdResolutionCacheMap = new ConcurrentHashMap<EntityPersister, NaturalIdResolutionCache>();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object[] findCachedNaturalId(EntityPersister persister, Serializable pk) {
|
public Object[] findCachedNaturalId(EntityPersister persister, Serializable pk) {
|
||||||
final NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
return naturalIdXrefDelegate.findCachedNaturalId( persister, pk );
|
||||||
if ( entityNaturalIdResolutionCache == null ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final LocalNaturalIdCacheKey localNaturalIdCacheKey = entityNaturalIdResolutionCache.pkToNaturalIdMap.get( pk );
|
|
||||||
if ( localNaturalIdCacheKey == null ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return localNaturalIdCacheKey.getValues();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Serializable findCachedNaturalIdResolution(EntityPersister persister, Object[] naturalIdValues) {
|
public Serializable findCachedNaturalIdResolution(EntityPersister persister, Object[] naturalIdValues) {
|
||||||
validateNaturalId( persister, naturalIdValues );
|
return naturalIdXrefDelegate.findCachedNaturalIdResolution( persister, naturalIdValues );
|
||||||
|
|
||||||
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
|
||||||
|
|
||||||
Serializable pk;
|
|
||||||
final LocalNaturalIdCacheKey localNaturalIdCacheKey = new LocalNaturalIdCacheKey( persister, naturalIdValues );
|
|
||||||
if ( entityNaturalIdResolutionCache != null ) {
|
|
||||||
pk = entityNaturalIdResolutionCache.naturalIdToPkMap.get( localNaturalIdCacheKey );
|
|
||||||
|
|
||||||
// Found in session cache
|
|
||||||
if ( pk != null ) {
|
|
||||||
if ( LOG.isTraceEnabled() ) {
|
|
||||||
LOG.trace(
|
|
||||||
"Resolved natural key -> primary key resolution in session cache: " +
|
|
||||||
persister.getRootEntityName() + "#[" +
|
|
||||||
Arrays.toString( naturalIdValues ) + "]"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Session cache miss, see if second-level caching is enabled
|
|
||||||
if ( !persister.hasNaturalIdCache() ) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try resolution from second-level cache
|
|
||||||
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( naturalIdValues, persister, session );
|
|
||||||
|
|
||||||
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
|
|
||||||
pk = (Serializable) naturalIdCacheAccessStrategy.get( naturalIdCacheKey, session.getTimestamp() );
|
|
||||||
|
|
||||||
// Found in second-level cache, store in session cache
|
|
||||||
final SessionFactoryImplementor factory = getSession().getFactory();
|
|
||||||
if ( pk != null ) {
|
|
||||||
if ( factory.getStatistics().isStatisticsEnabled() ) {
|
|
||||||
factory.getStatisticsImplementor().naturalIdCacheHit(
|
|
||||||
naturalIdCacheAccessStrategy.getRegion().getName()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( LOG.isTraceEnabled() )
|
|
||||||
LOG.trace(
|
|
||||||
"Resolved natural key -> primary key resolution in second-level cache: " +
|
|
||||||
persister.getRootEntityName() + "#[" +
|
|
||||||
Arrays.toString( naturalIdValues ) + "]"
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( entityNaturalIdResolutionCache == null ) {
|
|
||||||
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
|
|
||||||
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
public void cacheNaturalIdResolution(
|
||||||
|
EntityPersister persister,
|
||||||
|
final Serializable pk,
|
||||||
|
Object[] naturalIdValues,
|
||||||
CachedNaturalIdValueSource valueSource) {
|
CachedNaturalIdValueSource valueSource) {
|
||||||
validateNaturalId( persister, naturalIdValues );
|
naturalIdXrefDelegate.cacheNaturalIdResolution( persister, pk, naturalIdValues, valueSource );
|
||||||
|
|
||||||
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
|
||||||
if ( entityNaturalIdResolutionCache == null ) {
|
|
||||||
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
|
|
||||||
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache );
|
|
||||||
}
|
|
||||||
|
|
||||||
final LocalNaturalIdCacheKey localNaturalIdCacheKey = new LocalNaturalIdCacheKey( persister, naturalIdValues );
|
|
||||||
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, localNaturalIdCacheKey );
|
|
||||||
entityNaturalIdResolutionCache.naturalIdToPkMap.put( localNaturalIdCacheKey, pk );
|
|
||||||
|
|
||||||
//If second-level caching is enabled cache the resolution there as well
|
|
||||||
if ( persister.hasNaturalIdCache() ) {
|
|
||||||
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister.getNaturalIdCacheAccessStrategy();
|
|
||||||
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( naturalIdValues, persister, session );
|
|
||||||
|
|
||||||
final SessionFactoryImplementor factory = getSession().getFactory();
|
|
||||||
|
|
||||||
switch ( valueSource ) {
|
|
||||||
case LOAD: {
|
|
||||||
final boolean put = naturalIdCacheAccessStrategy.putFromLoad(
|
|
||||||
naturalIdCacheKey,
|
|
||||||
pk,
|
|
||||||
session.getTimestamp(),
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
|
||||||
factory.getStatisticsImplementor()
|
|
||||||
.naturalIdCachePut( naturalIdCacheAccessStrategy.getRegion().getName() );
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case INSERT: {
|
|
||||||
naturalIdCacheAccessStrategy.insert( naturalIdCacheKey, pk );
|
|
||||||
|
|
||||||
( (EventSource) this.session ).getActionQueue().registerProcess(
|
|
||||||
new AfterTransactionCompletionProcess() {
|
|
||||||
@Override
|
|
||||||
public void doAfterTransactionCompletion(boolean success, SessionImplementor session) {
|
|
||||||
final boolean put = naturalIdCacheAccessStrategy.afterInsert( naturalIdCacheKey, pk );
|
|
||||||
|
|
||||||
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
|
||||||
factory.getStatisticsImplementor().naturalIdCachePut(
|
|
||||||
naturalIdCacheAccessStrategy.getRegion().getName() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
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) {
|
|
||||||
final boolean put = naturalIdCacheAccessStrategy.afterUpdate( naturalIdCacheKey, pk, lock );
|
|
||||||
|
|
||||||
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
|
||||||
factory.getStatisticsImplementor().naturalIdCachePut(
|
|
||||||
naturalIdCacheAccessStrategy.getRegion().getName() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void evictNaturalIdResolution(EntityPersister persister, final Serializable pk, Object[] deletedNaturalIdValues) {
|
|
||||||
if ( !persister.hasNaturalIdentifier() ) {
|
|
||||||
throw new IllegalArgumentException( "Entity did not define a natrual-id" );
|
|
||||||
}
|
|
||||||
if ( persister.getNaturalIdentifierProperties().length != deletedNaturalIdValues.length ) {
|
|
||||||
throw new IllegalArgumentException( "Mismatch between expected number of natural-id values and found." );
|
|
||||||
}
|
|
||||||
|
|
||||||
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
|
|
||||||
Object[] sessionCachedNaturalIdValues = null;
|
|
||||||
if ( entityNaturalIdResolutionCache != null ) {
|
|
||||||
final LocalNaturalIdCacheKey localNaturalIdCacheKey = entityNaturalIdResolutionCache.pkToNaturalIdMap
|
|
||||||
.remove( pk );
|
|
||||||
if ( localNaturalIdCacheKey != null ) {
|
|
||||||
entityNaturalIdResolutionCache.naturalIdToPkMap.remove( localNaturalIdCacheKey );
|
|
||||||
sessionCachedNaturalIdValues = localNaturalIdCacheKey.getValues();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( persister.hasNaturalIdCache() ) {
|
|
||||||
final NaturalIdRegionAccessStrategy naturalIdCacheAccessStrategy = persister
|
|
||||||
.getNaturalIdCacheAccessStrategy();
|
|
||||||
final NaturalIdCacheKey naturalIdCacheKey = new NaturalIdCacheKey( deletedNaturalIdValues, persister,
|
|
||||||
session );
|
|
||||||
naturalIdCacheAccessStrategy.evict( naturalIdCacheKey );
|
|
||||||
|
|
||||||
if ( sessionCachedNaturalIdValues != null
|
|
||||||
&& !Arrays.equals( sessionCachedNaturalIdValues, deletedNaturalIdValues ) ) {
|
|
||||||
final NaturalIdCacheKey sessionNaturalIdCacheKey = new NaturalIdCacheKey( sessionCachedNaturalIdValues,
|
|
||||||
persister, session );
|
|
||||||
naturalIdCacheAccessStrategy.evict( sessionNaturalIdCacheKey );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* 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.engine.spi;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of action from which the cache call is originating.
|
||||||
|
*/
|
||||||
|
public enum CachedNaturalIdValueSource {
|
||||||
|
LOAD,
|
||||||
|
INSERT,
|
||||||
|
UPDATE
|
||||||
|
}
|
|
@ -379,7 +379,7 @@ public final class EntityEntry implements Serializable {
|
||||||
throw new HibernateException( "PersistenceContext was null on attempt to update loaded state" );
|
throw new HibernateException( "PersistenceContext was null on attempt to update loaded state" );
|
||||||
}
|
}
|
||||||
|
|
||||||
persistenceContext.loadedStateUpdatedNotification( this, state );
|
persistenceContext.entityStateUpdatedNotification( this, state );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyLoadedStateInserted(Object[] state) {
|
private void notifyLoadedStateInserted(Object[] state) {
|
||||||
|
@ -387,7 +387,7 @@ public final class EntityEntry implements Serializable {
|
||||||
throw new HibernateException( "PersistenceContext was null on attempt to insert loaded state" );
|
throw new HibernateException( "PersistenceContext was null on attempt to insert loaded state" );
|
||||||
}
|
}
|
||||||
|
|
||||||
persistenceContext.loadedStateInsertedNotification( this, state );
|
persistenceContext.entityStateInsertedNotification( this, state );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyLoadedStateDeleted(Object[] deletedState) {
|
private void notifyLoadedStateDeleted(Object[] deletedState) {
|
||||||
|
@ -395,7 +395,7 @@ public final class EntityEntry implements Serializable {
|
||||||
throw new HibernateException( "PersistenceContext was null on attempt to delete loaded state" );
|
throw new HibernateException( "PersistenceContext was null on attempt to delete loaded state" );
|
||||||
}
|
}
|
||||||
|
|
||||||
persistenceContext.loadedStateDeletedNotification( this, deletedState );
|
persistenceContext.entityStateDeletedNotification( this, deletedState );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -683,25 +683,34 @@ public interface PersistenceContext {
|
||||||
*/
|
*/
|
||||||
public boolean wasInsertedDuringTransaction(EntityPersister persister, Serializable id);
|
public boolean wasInsertedDuringTransaction(EntityPersister persister, Serializable id);
|
||||||
|
|
||||||
public void loadedStateUpdatedNotification(EntityEntry entityEntry, Object[] state);
|
/**
|
||||||
|
* Callback used to signal that loaded entity state has changed.
|
||||||
public void loadedStateInsertedNotification(EntityEntry entityEntry, Object[] state);
|
*
|
||||||
|
* @param entityEntry The entry of the entity that has changed.
|
||||||
public void loadedStateDeletedNotification(EntityEntry entityEntry, Object[] deletedState);
|
* @param state The new state.
|
||||||
|
*/
|
||||||
|
public void entityStateUpdatedNotification(EntityEntry entityEntry, Object[] state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback used to signal that entity state has been inserted.
|
||||||
|
*
|
||||||
|
* @param entityEntry The entry of the inserted entity
|
||||||
|
* @param state The new state
|
||||||
|
*/
|
||||||
|
public void entityStateInsertedNotification(EntityEntry entityEntry, Object[] state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback used to signal that entity state has been deleted.
|
||||||
|
*
|
||||||
|
* @param entityEntry The entry of the inserted entity
|
||||||
|
* @param deletedState The state of the entity at the time of deletion
|
||||||
|
*/
|
||||||
|
public void entityStateDeletedNotification(EntityEntry entityEntry, Object[] deletedState);
|
||||||
|
|
||||||
public Object[] findCachedNaturalId(EntityPersister persister, Serializable pk);
|
public Object[] findCachedNaturalId(EntityPersister persister, Serializable pk);
|
||||||
|
|
||||||
public Serializable findCachedNaturalIdResolution(EntityPersister persister, Object[] naturalId);
|
public Serializable findCachedNaturalIdResolution(EntityPersister persister, 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 cacheNaturalIdResolution(EntityPersister persister, Serializable pk, Object[] naturalId, CachedNaturalIdValueSource valueSource);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ import java.io.Serializable;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
import org.hibernate.cache.spi.access.NaturalIdRegionAccessStrategy;
|
||||||
import org.hibernate.engine.spi.PersistenceContext.CachedNaturalIdValueSource;
|
import org.hibernate.engine.spi.CachedNaturalIdValueSource;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.event.spi.ResolveNaturalIdEvent;
|
import org.hibernate.event.spi.ResolveNaturalIdEvent;
|
||||||
import org.hibernate.event.spi.ResolveNaturalIdEventListener;
|
import org.hibernate.event.spi.ResolveNaturalIdEventListener;
|
||||||
|
|
|
@ -1,9 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
//$Id$
|
//$Id$
|
||||||
package org.hibernate.test.annotations.naturalid;
|
package org.hibernate.test.naturalid.immutableentity;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.ManyToOne;
|
|
||||||
|
|
||||||
import org.hibernate.annotations.Immutable;
|
import org.hibernate.annotations.Immutable;
|
||||||
import org.hibernate.annotations.NaturalId;
|
import org.hibernate.annotations.NaturalId;
|
||||||
|
@ -11,7 +34,6 @@ import org.hibernate.annotations.NaturalIdCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eric Dalquist
|
* @author Eric Dalquist
|
||||||
* @see https://hibernate.onjira.com/browse/HHH-7085
|
|
||||||
*/
|
*/
|
||||||
@Entity
|
@Entity
|
||||||
@Immutable
|
@Immutable
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Hibernate, Relational Persistence for Idiomatic Java
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
*
|
*
|
||||||
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
|
* Copyright (c) 2012, Red Hat Inc. or third-party contributors as
|
||||||
* indicated by the @author tags or express copyright attribution
|
* indicated by the @author tags or express copyright attribution
|
||||||
* statements applied by the authors. All third-party contributions are
|
* statements applied by the authors. All third-party contributions are
|
||||||
* distributed under license by Red Hat Inc.
|
* distributed under license by Red Hat Inc.
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
* 51 Franklin Street, Fifth Floor
|
* 51 Franklin Street, Fifth Floor
|
||||||
* Boston, MA 02110-1301 USA
|
* Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.hibernate.test.annotations.naturalid;
|
package org.hibernate.test.naturalid.immutableentity;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
@ -35,6 +35,8 @@ import org.hibernate.annotations.Immutable;
|
||||||
import org.hibernate.cfg.Configuration;
|
import org.hibernate.cfg.Configuration;
|
||||||
import org.hibernate.metadata.ClassMetadata;
|
import org.hibernate.metadata.ClassMetadata;
|
||||||
import org.hibernate.stat.Statistics;
|
import org.hibernate.stat.Statistics;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -42,9 +44,9 @@ import org.junit.Test;
|
||||||
* Test case for NaturalId annotation on an {@link Immutable} entity
|
* Test case for NaturalId annotation on an {@link Immutable} entity
|
||||||
*
|
*
|
||||||
* @author Eric Dalquist
|
* @author Eric Dalquist
|
||||||
* @see https://hibernate.onjira.com/browse/HHH-7085
|
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@TestForIssue( jiraKey = "HHH-7085" )
|
||||||
public class ImmutableEntityNaturalIdTest extends BaseCoreFunctionalTestCase {
|
public class ImmutableEntityNaturalIdTest extends BaseCoreFunctionalTestCase {
|
||||||
@Test
|
@Test
|
||||||
public void testMappingProperties() {
|
public void testMappingProperties() {
|
Loading…
Reference in New Issue