📄 statefulpersistencecontext.java
字号:
// $Id: StatefulPersistenceContext.java 11490 2007-05-09 01:43:11Z steve.ebersole@jboss.com $package org.hibernate.engine;import java.io.IOException;import java.io.InvalidObjectException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.util.ArrayList;import java.util.HashMap;import java.util.HashSet;import java.util.Iterator;import java.util.List;import java.util.Map;import org.apache.commons.collections.map.ReferenceMap;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.hibernate.AssertionFailure;import org.hibernate.Hibernate;import org.hibernate.HibernateException;import org.hibernate.LockMode;import org.hibernate.MappingException;import org.hibernate.NonUniqueObjectException;import org.hibernate.PersistentObjectException;import org.hibernate.TransientObjectException;import org.hibernate.engine.loading.LoadContexts;import org.hibernate.pretty.MessageHelper;import org.hibernate.collection.PersistentCollection;import org.hibernate.persister.collection.CollectionPersister;import org.hibernate.persister.entity.EntityPersister;import org.hibernate.proxy.HibernateProxy;import org.hibernate.proxy.LazyInitializer;import org.hibernate.tuple.ElementWrapper;import org.hibernate.util.IdentityMap;import org.hibernate.util.MarkerObject;/** * A <tt>PersistenceContext</tt> represents the state of persistent "stuff" which * Hibernate is tracking. This includes persistent entities, collections, * as well as proxies generated. * </p> * There is meant to be a one-to-one correspondence between a SessionImpl and * a PersistentContext. The SessionImpl uses the PersistentContext to track * the current state of its context. Event-listeners then use the * PersistentContext to drive their processing. * * @author Steve Ebersole */public class StatefulPersistenceContext implements PersistenceContext { public static final Object NO_ROW = new MarkerObject( "NO_ROW" ); private static final Logger log = LoggerFactory.getLogger( StatefulPersistenceContext.class ); private static final Logger PROXY_WARN_LOG = LoggerFactory.getLogger( StatefulPersistenceContext.class.getName() + ".ProxyWarnLog" ); private static final int INIT_COLL_SIZE = 8; private SessionImplementor session; // Loaded entity instances, by EntityKey private Map entitiesByKey; // Loaded entity instances, by EntityUniqueKey private Map entitiesByUniqueKey; // Identity map of EntityEntry instances, by the entity instance private Map entityEntries; // Entity proxies, by EntityKey private Map proxiesByKey; // Snapshots of current database state for entities // that have *not* been loaded private Map entitySnapshotsByKey; // Identity map of array holder ArrayHolder instances, by the array instance private Map arrayHolders; // Identity map of CollectionEntry instances, by the collection wrapper private Map collectionEntries; // Collection wrappers, by the CollectionKey private Map collectionsByKey; //key=CollectionKey, value=PersistentCollection // Set of EntityKeys of deleted objects private HashSet nullifiableEntityKeys; // properties that we have tried to load, and not found in the database private HashSet nullAssociations; // A list of collection wrappers that were instantiating during result set // processing, that we will need to initialize at the end of the query private List nonlazyCollections; // A container for collections we load up when the owning entity is not // yet loaded ... for now, this is purely transient! private Map unownedCollections; private int cascading = 0; private int loadCounter = 0; private boolean flushing = false; private boolean hasNonReadOnlyEntities = false; private LoadContexts loadContexts; private BatchFetchQueue batchFetchQueue; /** * Constructs a PersistentContext, bound to the given session. * * @param session The session "owning" this context. */ public StatefulPersistenceContext(SessionImplementor session) { this.session = session; entitiesByKey = new HashMap( INIT_COLL_SIZE ); entitiesByUniqueKey = new HashMap( INIT_COLL_SIZE ); proxiesByKey = new ReferenceMap( ReferenceMap.HARD, ReferenceMap.WEAK ); entitySnapshotsByKey = new HashMap( INIT_COLL_SIZE ); entityEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE ); collectionEntries = IdentityMap.instantiateSequenced( INIT_COLL_SIZE ); collectionsByKey = new HashMap( INIT_COLL_SIZE ); arrayHolders = IdentityMap.instantiate( INIT_COLL_SIZE ); nullifiableEntityKeys = new HashSet(); initTransientState(); } private void initTransientState() { nullAssociations = new HashSet( INIT_COLL_SIZE ); nonlazyCollections = new ArrayList( INIT_COLL_SIZE ); } public boolean isStateless() { return false; } public SessionImplementor getSession() { return session; } public LoadContexts getLoadContexts() { if ( loadContexts == null ) { loadContexts = new LoadContexts( this ); } return loadContexts; } public void addUnownedCollection(CollectionKey key, PersistentCollection collection) { if (unownedCollections==null) { unownedCollections = new HashMap(8); } unownedCollections.put(key, collection); } public PersistentCollection useUnownedCollection(CollectionKey key) { if (unownedCollections==null) { return null; } else { return (PersistentCollection) unownedCollections.remove(key); } } /** * Get the <tt>BatchFetchQueue</tt>, instantiating one if * necessary. */ public BatchFetchQueue getBatchFetchQueue() { if (batchFetchQueue==null) { batchFetchQueue = new BatchFetchQueue(this); } return batchFetchQueue; } public void clear() { Iterator itr = proxiesByKey.values().iterator(); while ( itr.hasNext() ) { final LazyInitializer li = ( ( HibernateProxy ) itr.next() ).getHibernateLazyInitializer(); li.setSession( null ); } Map.Entry[] collectionEntryArray = IdentityMap.concurrentEntries( collectionEntries ); for ( int i = 0; i < collectionEntryArray.length; i++ ) { ( ( PersistentCollection ) collectionEntryArray[i].getKey() ).unsetSession( getSession() ); } arrayHolders.clear(); entitiesByKey.clear(); entitiesByUniqueKey.clear(); entityEntries.clear(); entitySnapshotsByKey.clear(); collectionsByKey.clear(); collectionEntries.clear(); if ( unownedCollections != null ) { unownedCollections.clear(); } proxiesByKey.clear(); nullifiableEntityKeys.clear(); if ( batchFetchQueue != null ) { batchFetchQueue.clear(); } hasNonReadOnlyEntities = false; if ( loadContexts != null ) { loadContexts.cleanup(); } } public boolean hasNonReadOnlyEntities() { return hasNonReadOnlyEntities; } public void setEntryStatus(EntityEntry entry, Status status) { entry.setStatus(status); setHasNonReadOnlyEnties(status); } private void setHasNonReadOnlyEnties(Status status) { if ( status==Status.DELETED || status==Status.MANAGED || status==Status.SAVING ) { hasNonReadOnlyEntities = true; } } public void afterTransactionCompletion() { // Downgrade locks Iterator iter = entityEntries.values().iterator(); while ( iter.hasNext() ) { ( (EntityEntry) iter.next() ).setLockMode(LockMode.NONE); } } /** * Get the current state of the entity as known to the underlying * database, or null if there is no corresponding row */ public Object[] getDatabaseSnapshot(Serializable id, EntityPersister persister) throws HibernateException { EntityKey key = new EntityKey( id, persister, session.getEntityMode() ); Object cached = entitySnapshotsByKey.get(key); if (cached!=null) { return cached==NO_ROW ? null : (Object[]) cached; } else { Object[] snapshot = persister.getDatabaseSnapshot( id, session ); entitySnapshotsByKey.put( key, snapshot==null ? NO_ROW : snapshot ); return snapshot; } } public Object[] getNaturalIdSnapshot(Serializable id, EntityPersister persister) throws HibernateException { if ( !persister.hasNaturalIdentifier() ) { return null; } // if the natural-id is marked as non-mutable, it is not retrieved during a // normal database-snapshot operation... int[] props = persister.getNaturalIdentifierProperties(); boolean[] updateable = persister.getPropertyUpdateability(); boolean allNatualIdPropsAreUpdateable = true; for ( int i = 0; i < props.length; i++ ) { if ( !updateable[ props[i] ] ) { allNatualIdPropsAreUpdateable = false; break; } } if ( allNatualIdPropsAreUpdateable ) { // do this when all the properties are updateable since there is // a certain likelihood that the information will already be // snapshot-cached. Object[] entitySnapshot = getDatabaseSnapshot( id, persister ); if ( entitySnapshot == NO_ROW ) { return null; } Object[] naturalIdSnapshot = new Object[ props.length ]; for ( int i = 0; i < props.length; i++ ) { naturalIdSnapshot[i] = entitySnapshot[ props[i] ]; } return naturalIdSnapshot; } else { return persister.getNaturalIdentifierSnapshot( id, session ); } } /** * Retrieve the cached database snapshot for the requested entity key. * <p/> * This differs from {@link #getDatabaseSnapshot} is two important respects:<ol> * <li>no snapshot is obtained from the database if not already cached</li> * <li>an entry of {@link #NO_ROW} here is interpretet as an exception</li> * </ol> * @param key The entity key for which to retrieve the cached snapshot * @return The cached snapshot * @throws IllegalStateException if the cached snapshot was == {@link #NO_ROW}. */ public Object[] getCachedDatabaseSnapshot(EntityKey key) { Object snapshot = entitySnapshotsByKey.get( key ); if ( snapshot == NO_ROW ) { throw new IllegalStateException( "persistence context reported no row snapshot for " + MessageHelper.infoString( key.getEntityName(), key.getIdentifier() ) ); } return ( Object[] ) snapshot; } /*public void removeDatabaseSnapshot(EntityKey key) { entitySnapshotsByKey.remove(key); }*/ public void addEntity(EntityKey key, Object entity) { entitiesByKey.put(key, entity); getBatchFetchQueue().removeBatchLoadableEntityKey(key); } /** * Get the entity instance associated with the given * <tt>EntityKey</tt> */ public Object getEntity(EntityKey key) { return entitiesByKey.get(key); } public boolean containsEntity(EntityKey key) { return entitiesByKey.containsKey(key); } /** * Remove an entity from the session cache, also clear * up other state associated with the entity, all except * for the <tt>EntityEntry</tt> */ public Object removeEntity(EntityKey key) { Object entity = entitiesByKey.remove(key); Iterator iter = entitiesByUniqueKey.values().iterator(); while ( iter.hasNext() ) { if ( iter.next()==entity ) iter.remove(); } entitySnapshotsByKey.remove(key); nullifiableEntityKeys.remove(key); getBatchFetchQueue().removeBatchLoadableEntityKey(key); getBatchFetchQueue().removeSubselect(key); return entity; } /** * Get an entity cached by unique key */ public Object getEntity(EntityUniqueKey euk) { return entitiesByUniqueKey.get(euk); } /** * Add an entity to the cache by unique key */ public void addEntity(EntityUniqueKey euk, Object entity) { entitiesByUniqueKey.put(euk, entity); } /** * Retreive the EntityEntry representation of the given entity. * * @param entity The entity for which to locate the EntityEntry. * @return The EntityEntry for the given entity. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -