/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2008, Red Hat Middleware LLC 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 Middleware LLC. * * 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.impl; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Reader; import java.io.Serializable; import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; import java.sql.Blob; import java.sql.Clob; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.hibernate.CacheMode; import org.hibernate.ConnectionReleaseMode; import org.hibernate.Criteria; import org.hibernate.EntityMode; import org.hibernate.Filter; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.Interceptor; import org.hibernate.LobHelper; import org.hibernate.LockMode; import org.hibernate.MappingException; import org.hibernate.ObjectDeletedException; import org.hibernate.Query; import org.hibernate.QueryException; import org.hibernate.ReplicationMode; import org.hibernate.SQLQuery; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.Session; import org.hibernate.SessionException; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.TransientObjectException; import org.hibernate.TypeHelper; import org.hibernate.UnresolvableObjectException; import org.hibernate.UnknownProfileException; import org.hibernate.EntityNameResolver; import org.hibernate.LockOptions; import org.hibernate.collection.PersistentCollection; import org.hibernate.engine.ActionQueue; import org.hibernate.engine.CollectionEntry; import org.hibernate.engine.EntityEntry; import org.hibernate.engine.EntityKey; import org.hibernate.engine.NonFlushedChanges; import org.hibernate.engine.PersistenceContext; import org.hibernate.engine.QueryParameters; import org.hibernate.engine.StatefulPersistenceContext; import org.hibernate.engine.Status; import org.hibernate.engine.LoadQueryInfluencers; import org.hibernate.engine.jdbc.LobCreationContext; import org.hibernate.engine.jdbc.LobCreator; import org.hibernate.engine.query.FilterQueryPlan; import org.hibernate.engine.query.HQLQueryPlan; import org.hibernate.engine.query.NativeSQLQueryPlan; import org.hibernate.engine.query.sql.NativeSQLQuerySpecification; import org.hibernate.event.AutoFlushEvent; import org.hibernate.event.AutoFlushEventListener; import org.hibernate.event.DeleteEvent; import org.hibernate.event.DeleteEventListener; import org.hibernate.event.DirtyCheckEvent; import org.hibernate.event.DirtyCheckEventListener; import org.hibernate.event.EventListeners; import org.hibernate.event.EventSource; import org.hibernate.event.EvictEvent; import org.hibernate.event.EvictEventListener; import org.hibernate.event.FlushEvent; import org.hibernate.event.FlushEventListener; import org.hibernate.event.InitializeCollectionEvent; import org.hibernate.event.InitializeCollectionEventListener; import org.hibernate.event.LoadEvent; import org.hibernate.event.LoadEventListener; import org.hibernate.event.LoadEventListener.LoadType; import org.hibernate.event.LockEvent; import org.hibernate.event.LockEventListener; import org.hibernate.event.MergeEvent; import org.hibernate.event.MergeEventListener; import org.hibernate.event.PersistEvent; import org.hibernate.event.PersistEventListener; import org.hibernate.event.RefreshEvent; import org.hibernate.event.RefreshEventListener; import org.hibernate.event.ReplicateEvent; import org.hibernate.event.ReplicateEventListener; import org.hibernate.event.SaveOrUpdateEvent; import org.hibernate.event.SaveOrUpdateEventListener; import org.hibernate.exception.JDBCExceptionHelper; import org.hibernate.jdbc.Batcher; import org.hibernate.jdbc.JDBCContext; import org.hibernate.jdbc.Work; import org.hibernate.loader.criteria.CriteriaLoader; import org.hibernate.loader.custom.CustomLoader; import org.hibernate.loader.custom.CustomQuery; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.OuterJoinLoadable; import org.hibernate.pretty.MessageHelper; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.stat.SessionStatistics; import org.hibernate.stat.SessionStatisticsImpl; import org.hibernate.type.Type; import org.hibernate.type.SerializationException; import org.hibernate.util.ArrayHelper; import org.hibernate.util.CollectionHelper; import org.hibernate.util.StringHelper; /** * Concrete implementation of a Session, and also the central, organizing component * of Hibernate's internal implementation. As such, this class exposes two interfaces; * Session itself, to the application, and SessionImplementor, to other components * of Hibernate. This class is not threadsafe. * * @author Gavin King */ public final class SessionImpl extends AbstractSessionImpl implements EventSource, org.hibernate.classic.Session, JDBCContext.Context, LobCreationContext { // todo : need to find a clean way to handle the "event source" role // a seperate classs responsible for generating/dispatching events just duplicates most of the Session methods... // passing around seperate reto interceptor, factory, actionQueue, and persistentContext is not manageable... private static final Logger log = LoggerFactory.getLogger(SessionImpl.class); private transient EntityMode entityMode = EntityMode.POJO; private transient boolean autoClear; //for EJB3 private transient long timestamp; private transient FlushMode flushMode = FlushMode.AUTO; private transient CacheMode cacheMode = CacheMode.NORMAL; private transient Interceptor interceptor; private transient int dontFlushFromFind = 0; private transient ActionQueue actionQueue; private transient StatefulPersistenceContext persistenceContext; private transient JDBCContext jdbcContext; private transient EventListeners listeners; private transient boolean flushBeforeCompletionEnabled; private transient boolean autoCloseSessionEnabled; private transient ConnectionReleaseMode connectionReleaseMode; private transient LoadQueryInfluencers loadQueryInfluencers; private transient Session rootSession; private transient Map childSessionsByEntityMode; private transient EntityNameResolver entityNameResolver = new CoordinatingEntityNameResolver(); /** * Constructor used in building "child sessions". * * @param parent The parent session * @param entityMode */ private SessionImpl(SessionImpl parent, EntityMode entityMode) { super( parent.factory ); this.rootSession = parent; this.timestamp = parent.timestamp; this.jdbcContext = parent.jdbcContext; this.interceptor = parent.interceptor; this.listeners = parent.listeners; this.actionQueue = new ActionQueue( this ); this.entityMode = entityMode; this.persistenceContext = new StatefulPersistenceContext( this ); this.flushBeforeCompletionEnabled = false; this.autoCloseSessionEnabled = false; this.connectionReleaseMode = null; loadQueryInfluencers = new LoadQueryInfluencers( factory ); if ( factory.getStatistics().isStatisticsEnabled() ) { factory.getStatisticsImplementor().openSession(); } log.debug( "opened session [" + entityMode + "]" ); } /** * Constructor used for openSession(...) processing, as well as construction * of sessions for getCurrentSession(). * * @param connection The user-supplied connection to use for this session. * @param factory The factory from which this session was obtained * @param autoclose NOT USED * @param timestamp The timestamp for this session * @param interceptor The interceptor to be applied to this session * @param entityMode The entity-mode for this session * @param flushBeforeCompletionEnabled Should we auto flush before completion of transaction * @param autoCloseSessionEnabled Should we auto close after completion of transaction * @param connectionReleaseMode The mode by which we should release JDBC connections. */ SessionImpl( final Connection connection, final SessionFactoryImpl factory, final boolean autoclose, final long timestamp, final Interceptor interceptor, final EntityMode entityMode, final boolean flushBeforeCompletionEnabled, final boolean autoCloseSessionEnabled, final ConnectionReleaseMode connectionReleaseMode) { super( factory ); this.rootSession = null; this.timestamp = timestamp; this.entityMode = entityMode; this.interceptor = interceptor; this.listeners = factory.getEventListeners(); this.actionQueue = new ActionQueue( this ); this.persistenceContext = new StatefulPersistenceContext( this ); this.flushBeforeCompletionEnabled = flushBeforeCompletionEnabled; this.autoCloseSessionEnabled = autoCloseSessionEnabled; this.connectionReleaseMode = connectionReleaseMode; this.jdbcContext = new JDBCContext( this, connection, interceptor ); loadQueryInfluencers = new LoadQueryInfluencers( factory ); if ( factory.getStatistics().isStatisticsEnabled() ) { factory.getStatisticsImplementor().openSession(); } if ( log.isDebugEnabled() ) { log.debug( "opened session at timestamp: " + timestamp ); } } public Session getSession(EntityMode entityMode) { if ( this.entityMode == entityMode ) { return this; } if ( rootSession != null ) { return rootSession.getSession( entityMode ); } errorIfClosed(); checkTransactionSynchStatus(); SessionImpl rtn = null; if ( childSessionsByEntityMode == null ) { childSessionsByEntityMode = new HashMap(); } else { rtn = (SessionImpl) childSessionsByEntityMode.get( entityMode ); } if ( rtn == null ) { rtn = new SessionImpl( this, entityMode ); childSessionsByEntityMode.put( entityMode, rtn ); } return rtn; } public void clear() { errorIfClosed(); checkTransactionSynchStatus(); persistenceContext.clear(); actionQueue.clear(); } public Batcher getBatcher() { errorIfClosed(); checkTransactionSynchStatus(); // TODO : should remove this exposure // and have all references to the session's batcher use the ConnectionManager. return jdbcContext.getConnectionManager().getBatcher(); } public long getTimestamp() { checkTransactionSynchStatus(); return timestamp; } public Connection close() throws HibernateException { log.trace( "closing session" ); if ( isClosed() ) { throw new SessionException( "Session was already closed" ); } if ( factory.getStatistics().isStatisticsEnabled() ) { factory.getStatisticsImplementor().closeSession(); } try { try { if ( childSessionsByEntityMode != null ) { Iterator childSessions = childSessionsByEntityMode.values().iterator(); while ( childSessions.hasNext() ) { final SessionImpl child = ( SessionImpl ) childSessions.next(); child.close(); } } } catch( Throwable t ) { // just ignore } if ( rootSession == null ) { return jdbcContext.getConnectionManager().close(); } else { return null; } } finally { setClosed(); cleanup(); } } public ConnectionReleaseMode getConnectionReleaseMode() { checkTransactionSynchStatus(); return connectionReleaseMode; } public boolean isAutoCloseSessionEnabled() { return autoCloseSessionEnabled; } public boolean isOpen() { checkTransactionSynchStatus(); return !isClosed(); } public boolean isFlushModeNever() { return FlushMode.isManualFlushMode( getFlushMode() ); } public boolean isFlushBeforeCompletionEnabled() { return flushBeforeCompletionEnabled; } public void managedFlush() { if ( isClosed() ) { log.trace( "skipping auto-flush due to session closed" ); return; } log.trace("automatically flushing session"); flush(); if ( childSessionsByEntityMode != null ) { Iterator iter = childSessionsByEntityMode.values().iterator(); while ( iter.hasNext() ) { ( (Session) iter.next() ).flush(); } } } /** * Return changes to this session and its child sessions that have not been flushed yet. * <p/> * @return The non-flushed changes. */ public NonFlushedChanges getNonFlushedChanges() throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); NonFlushedChanges nonFlushedChanges = new NonFlushedChangesImpl( this ); if ( childSessionsByEntityMode != null ) { Iterator it = childSessionsByEntityMode.values().iterator(); while ( it.hasNext() ) { nonFlushedChanges.extractFromSession( ( EventSource ) it.next() ); } } return nonFlushedChanges; } /** * Apply non-flushed changes from a different session to this session. It is assumed * that this SessionImpl is "clean" (e.g., has no non-flushed changes, no cached entities, * no cached collections, no queued actions). The specified NonFlushedChanges object cannot * be bound to any session. * <p/> * @param nonFlushedChanges the non-flushed changes */ public void applyNonFlushedChanges(NonFlushedChanges nonFlushedChanges) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); replacePersistenceContext( ( ( NonFlushedChangesImpl ) nonFlushedChanges ).getPersistenceContext( entityMode) ); replaceActionQueue( ( ( NonFlushedChangesImpl ) nonFlushedChanges ).getActionQueue( entityMode ) ); if ( childSessionsByEntityMode != null ) { for ( Iterator it = childSessionsByEntityMode.values().iterator(); it.hasNext(); ) { ( ( SessionImpl ) it.next() ).applyNonFlushedChanges( nonFlushedChanges ); } } } private void replacePersistenceContext(StatefulPersistenceContext persistenceContextNew) { if ( persistenceContextNew.getSession() != null ) { throw new IllegalStateException( "new persistence context is already connected to a session " ); } persistenceContext.clear(); ObjectInputStream ois = null; try { ois = new ObjectInputStream( new ByteArrayInputStream( serializePersistenceContext( persistenceContextNew ) ) ); this.persistenceContext = StatefulPersistenceContext.deserialize( ois, this ); } catch (IOException ex) { throw new SerializationException( "could not deserialize the persistence context", ex ); } catch (ClassNotFoundException ex) { throw new SerializationException( "could not deserialize the persistence context", ex ); } finally { try { if (ois != null) ois.close(); } catch (IOException ex) {} } } private static byte[] serializePersistenceContext(StatefulPersistenceContext pc) { ByteArrayOutputStream baos = new ByteArrayOutputStream( 512 ); ObjectOutputStream oos = null; try { oos = new ObjectOutputStream( baos ); ( ( StatefulPersistenceContext ) pc ).serialize( oos ); } catch (IOException ex) { throw new SerializationException( "could not serialize persistence context", ex ); } finally { if ( oos != null ) { try { oos.close(); } catch( IOException ex ) { //ignore } } } return baos.toByteArray(); } private void replaceActionQueue(ActionQueue actionQueueNew) { if ( actionQueue.hasAnyQueuedActions() ) { throw new IllegalStateException( "cannot replace an ActionQueue with queued actions " ); } actionQueue.clear(); ObjectInputStream ois = null; try { ois = new ObjectInputStream( new ByteArrayInputStream( serializeActionQueue( actionQueueNew ) ) ); actionQueue = ActionQueue.deserialize( ois, this ); } catch (IOException ex) { throw new SerializationException( "could not deserialize the action queue", ex ); } catch (ClassNotFoundException ex) { throw new SerializationException( "could not deserialize the action queue", ex ); } finally { try { if (ois != null) ois.close(); } catch (IOException ex) {} } } private static byte[] serializeActionQueue(ActionQueue actionQueue) { ByteArrayOutputStream baos = new ByteArrayOutputStream( 512 ); ObjectOutputStream oos = null; try { oos = new ObjectOutputStream( baos ); actionQueue.serialize( oos ); } catch (IOException ex) { throw new SerializationException( "could not serialize action queue", ex ); } finally { if ( oos != null ) { try { oos.close(); } catch( IOException ex ) { //ignore } } } return baos.toByteArray(); } public boolean shouldAutoClose() { return isAutoCloseSessionEnabled() && !isClosed(); } public void managedClose() { log.trace( "automatically closing session" ); close(); } public Connection connection() throws HibernateException { errorIfClosed(); return jdbcContext.borrowConnection(); } public boolean isConnected() { checkTransactionSynchStatus(); return !isClosed() && jdbcContext.getConnectionManager().isCurrentlyConnected(); } public boolean isTransactionInProgress() { checkTransactionSynchStatus(); return !isClosed() && jdbcContext.isTransactionInProgress(); } public Connection disconnect() throws HibernateException { errorIfClosed(); log.debug( "disconnecting session" ); return jdbcContext.getConnectionManager().manualDisconnect(); } public void reconnect() throws HibernateException { errorIfClosed(); log.debug( "reconnecting session" ); checkTransactionSynchStatus(); jdbcContext.getConnectionManager().manualReconnect(); } public void reconnect(Connection conn) throws HibernateException { errorIfClosed(); log.debug( "reconnecting session" ); checkTransactionSynchStatus(); jdbcContext.getConnectionManager().manualReconnect( conn ); } public void beforeTransactionCompletion(Transaction tx) { log.trace( "before transaction completion" ); actionQueue.beforeTransactionCompletion(); if ( rootSession == null ) { try { interceptor.beforeTransactionCompletion(tx); } catch (Throwable t) { log.error("exception in interceptor beforeTransactionCompletion()", t); } } } public void setAutoClear(boolean enabled) { errorIfClosed(); autoClear = enabled; } /** * Check if there is a Hibernate or JTA transaction in progress and, * if there is not, flush if necessary, make sure the connection has * been committed (if it is not in autocommit mode) and run the after * completion processing */ public void afterOperation(boolean success) { if ( !jdbcContext.isTransactionInProgress() ) { jdbcContext.afterNontransactionalQuery( success ); } } public void afterTransactionCompletion(boolean success, Transaction tx) { log.trace( "after transaction completion" ); persistenceContext.afterTransactionCompletion(); actionQueue.afterTransactionCompletion(success); if ( rootSession == null && tx != null ) { try { interceptor.afterTransactionCompletion(tx); } catch (Throwable t) { log.error("exception in interceptor afterTransactionCompletion()", t); } } if ( autoClear ) { clear(); } } /** * clear all the internal collections, just * to help the garbage collector, does not * clear anything that is needed during the * afterTransactionCompletion() phase */ private void cleanup() { persistenceContext.clear(); } public LockMode getCurrentLockMode(Object object) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); if ( object == null ) { throw new NullPointerException( "null object passed to getCurrentLockMode()" ); } if ( object instanceof HibernateProxy ) { object = ( (HibernateProxy) object ).getHibernateLazyInitializer().getImplementation(this); if ( object == null ) { return LockMode.NONE; } } EntityEntry e = persistenceContext.getEntry(object); if ( e == null ) { throw new TransientObjectException( "Given object not associated with the session" ); } if ( e.getStatus() != Status.MANAGED ) { throw new ObjectDeletedException( "The given object was deleted", e.getId(), e.getPersister().getEntityName() ); } return e.getLockMode(); } public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException { errorIfClosed(); // todo : should this get moved to PersistentContext? // logically, is PersistentContext the "thing" to which an interceptor gets attached? final Object result = persistenceContext.getEntity(key); if ( result == null ) { final Object newObject = interceptor.getEntity( key.getEntityName(), key.getIdentifier() ); if ( newObject != null ) { lock( newObject, LockMode.NONE ); } return newObject; } else { return result; } } // saveOrUpdate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void saveOrUpdate(Object object) throws HibernateException { saveOrUpdate(null, object); } public void saveOrUpdate(String entityName, Object obj) throws HibernateException { fireSaveOrUpdate( new SaveOrUpdateEvent(entityName, obj, this) ); } private void fireSaveOrUpdate(SaveOrUpdateEvent event) { errorIfClosed(); checkTransactionSynchStatus(); SaveOrUpdateEventListener[] saveOrUpdateEventListener = listeners.getSaveOrUpdateEventListeners(); for ( int i = 0; i < saveOrUpdateEventListener.length; i++ ) { saveOrUpdateEventListener[i].onSaveOrUpdate(event); } } // save() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void save(Object obj, Serializable id) throws HibernateException { save(null, obj, id); } public Serializable save(Object obj) throws HibernateException { return save(null, obj); } public Serializable save(String entityName, Object object) throws HibernateException { return fireSave( new SaveOrUpdateEvent(entityName, object, this) ); } public void save(String entityName, Object object, Serializable id) throws HibernateException { fireSave( new SaveOrUpdateEvent(entityName, object, id, this) ); } private Serializable fireSave(SaveOrUpdateEvent event) { errorIfClosed(); checkTransactionSynchStatus(); SaveOrUpdateEventListener[] saveEventListener = listeners.getSaveEventListeners(); for ( int i = 0; i < saveEventListener.length; i++ ) { saveEventListener[i].onSaveOrUpdate(event); } return event.getResultId(); } // update() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void update(Object obj) throws HibernateException { update(null, obj); } public void update(Object obj, Serializable id) throws HibernateException { update(null, obj, id); } public void update(String entityName, Object object) throws HibernateException { fireUpdate( new SaveOrUpdateEvent(entityName, object, this) ); } public void update(String entityName, Object object, Serializable id) throws HibernateException { fireUpdate(new SaveOrUpdateEvent(entityName, object, id, this)); } private void fireUpdate(SaveOrUpdateEvent event) { errorIfClosed(); checkTransactionSynchStatus(); SaveOrUpdateEventListener[] updateEventListener = listeners.getUpdateEventListeners(); for ( int i = 0; i < updateEventListener.length; i++ ) { updateEventListener[i].onSaveOrUpdate(event); } } // lock() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void lock(String entityName, Object object, LockMode lockMode) throws HibernateException { fireLock( new LockEvent(entityName, object, lockMode, this) ); } public LockRequest buildLockRequest(LockOptions lockOptions) { return new LockRequestImpl(lockOptions); } public void lock(Object object, LockMode lockMode) throws HibernateException { fireLock( new LockEvent(object, lockMode, this) ); } private void fireLock(String entityName, Object object, LockOptions options) { fireLock( new LockEvent( entityName, object, options, this) ); } private void fireLock( Object object, LockOptions options) { fireLock( new LockEvent( object, options, this) ); } private void fireLock(LockEvent lockEvent) { errorIfClosed(); checkTransactionSynchStatus(); LockEventListener[] lockEventListener = listeners.getLockEventListeners(); for ( int i = 0; i < lockEventListener.length; i++ ) { lockEventListener[i].onLock( lockEvent ); } } // persist() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void persist(String entityName, Object object) throws HibernateException { firePersist( new PersistEvent(entityName, object, this) ); } public void persist(Object object) throws HibernateException { persist(null, object); } public void persist(String entityName, Object object, Map copiedAlready) throws HibernateException { firePersist( copiedAlready, new PersistEvent(entityName, object, this) ); } private void firePersist(Map copiedAlready, PersistEvent event) { errorIfClosed(); checkTransactionSynchStatus(); PersistEventListener[] persistEventListener = listeners.getPersistEventListeners(); for ( int i = 0; i < persistEventListener.length; i++ ) { persistEventListener[i].onPersist(event, copiedAlready); } } private void firePersist(PersistEvent event) { errorIfClosed(); checkTransactionSynchStatus(); PersistEventListener[] createEventListener = listeners.getPersistEventListeners(); for ( int i = 0; i < createEventListener.length; i++ ) { createEventListener[i].onPersist(event); } } // persistOnFlush() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void persistOnFlush(String entityName, Object object) throws HibernateException { firePersistOnFlush( new PersistEvent(entityName, object, this) ); } public void persistOnFlush(Object object) throws HibernateException { persist(null, object); } public void persistOnFlush(String entityName, Object object, Map copiedAlready) throws HibernateException { firePersistOnFlush( copiedAlready, new PersistEvent(entityName, object, this) ); } private void firePersistOnFlush(Map copiedAlready, PersistEvent event) { errorIfClosed(); checkTransactionSynchStatus(); PersistEventListener[] persistEventListener = listeners.getPersistOnFlushEventListeners(); for ( int i = 0; i < persistEventListener.length; i++ ) { persistEventListener[i].onPersist(event, copiedAlready); } } private void firePersistOnFlush(PersistEvent event) { errorIfClosed(); checkTransactionSynchStatus(); PersistEventListener[] createEventListener = listeners.getPersistOnFlushEventListeners(); for ( int i = 0; i < createEventListener.length; i++ ) { createEventListener[i].onPersist(event); } } // merge() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public Object merge(String entityName, Object object) throws HibernateException { return fireMerge( new MergeEvent(entityName, object, this) ); } public Object merge(Object object) throws HibernateException { return merge(null, object); } public void merge(String entityName, Object object, Map copiedAlready) throws HibernateException { fireMerge( copiedAlready, new MergeEvent(entityName, object, this) ); } private Object fireMerge(MergeEvent event) { errorIfClosed(); checkTransactionSynchStatus(); MergeEventListener[] mergeEventListener = listeners.getMergeEventListeners(); for ( int i = 0; i < mergeEventListener.length; i++ ) { mergeEventListener[i].onMerge(event); } return event.getResult(); } private void fireMerge(Map copiedAlready, MergeEvent event) { errorIfClosed(); checkTransactionSynchStatus(); MergeEventListener[] mergeEventListener = listeners.getMergeEventListeners(); for ( int i = 0; i < mergeEventListener.length; i++ ) { mergeEventListener[i].onMerge(event, copiedAlready); } } // saveOrUpdateCopy() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public Object saveOrUpdateCopy(String entityName, Object object) throws HibernateException { return fireSaveOrUpdateCopy( new MergeEvent(entityName, object, this) ); } public Object saveOrUpdateCopy(Object object) throws HibernateException { return saveOrUpdateCopy( null, object ); } public Object saveOrUpdateCopy(String entityName, Object object, Serializable id) throws HibernateException { return fireSaveOrUpdateCopy( new MergeEvent(entityName, object, id, this) ); } public Object saveOrUpdateCopy(Object object, Serializable id) throws HibernateException { return saveOrUpdateCopy( null, object, id ); } public void saveOrUpdateCopy(String entityName, Object object, Map copiedAlready) throws HibernateException { fireSaveOrUpdateCopy( copiedAlready, new MergeEvent( entityName, object, this ) ); } private void fireSaveOrUpdateCopy(Map copiedAlready, MergeEvent event) { errorIfClosed(); checkTransactionSynchStatus(); MergeEventListener[] saveOrUpdateCopyEventListener = listeners.getSaveOrUpdateCopyEventListeners(); for ( int i = 0; i < saveOrUpdateCopyEventListener.length; i++ ) { saveOrUpdateCopyEventListener[i].onMerge(event, copiedAlready); } } private Object fireSaveOrUpdateCopy(MergeEvent event) { errorIfClosed(); checkTransactionSynchStatus(); MergeEventListener[] saveOrUpdateCopyEventListener = listeners.getSaveOrUpdateCopyEventListeners(); for ( int i = 0; i < saveOrUpdateCopyEventListener.length; i++ ) { saveOrUpdateCopyEventListener[i].onMerge(event); } return event.getResult(); } // delete() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Delete a persistent object */ public void delete(Object object) throws HibernateException { fireDelete( new DeleteEvent(object, this) ); } /** * Delete a persistent object (by explicit entity name) */ public void delete(String entityName, Object object) throws HibernateException { fireDelete( new DeleteEvent( entityName, object, this ) ); } /** * Delete a persistent object */ public void delete(String entityName, Object object, boolean isCascadeDeleteEnabled, Set transientEntities) throws HibernateException { fireDelete( new DeleteEvent( entityName, object, isCascadeDeleteEnabled, this ), transientEntities ); } private void fireDelete(DeleteEvent event) { errorIfClosed(); checkTransactionSynchStatus(); DeleteEventListener[] deleteEventListener = listeners.getDeleteEventListeners(); for ( int i = 0; i < deleteEventListener.length; i++ ) { deleteEventListener[i].onDelete( event ); } } private void fireDelete(DeleteEvent event, Set transientEntities) { errorIfClosed(); checkTransactionSynchStatus(); DeleteEventListener[] deleteEventListener = listeners.getDeleteEventListeners(); for ( int i = 0; i < deleteEventListener.length; i++ ) { deleteEventListener[i].onDelete( event, transientEntities ); } } // load()/get() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void load(Object object, Serializable id) throws HibernateException { LoadEvent event = new LoadEvent(id, object, this); fireLoad( event, LoadEventListener.RELOAD ); } public Object load(Class entityClass, Serializable id) throws HibernateException { return load( entityClass.getName(), id ); } public Object load(String entityName, Serializable id) throws HibernateException { LoadEvent event = new LoadEvent(id, entityName, false, this); boolean success = false; try { fireLoad( event, LoadEventListener.LOAD ); if ( event.getResult() == null ) { getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id ); } success = true; return event.getResult(); } finally { afterOperation(success); } } public Object get(Class entityClass, Serializable id) throws HibernateException { return get( entityClass.getName(), id ); } public Object get(String entityName, Serializable id) throws HibernateException { LoadEvent event = new LoadEvent(id, entityName, false, this); boolean success = false; try { fireLoad(event, LoadEventListener.GET); success = true; return event.getResult(); } finally { afterOperation(success); } } /** * Load the data for the object with the specified id into a newly created object. * This is only called when lazily initializing a proxy. * Do NOT return a proxy. */ public Object immediateLoad(String entityName, Serializable id) throws HibernateException { if ( log.isDebugEnabled() ) { EntityPersister persister = getFactory().getEntityPersister(entityName); log.debug( "initializing proxy: " + MessageHelper.infoString( persister, id, getFactory() ) ); } LoadEvent event = new LoadEvent(id, entityName, true, this); fireLoad(event, LoadEventListener.IMMEDIATE_LOAD); return event.getResult(); } public Object internalLoad(String entityName, Serializable id, boolean eager, boolean nullable) throws HibernateException { // todo : remove LoadEventListener.LoadType type = nullable ? LoadEventListener.INTERNAL_LOAD_NULLABLE : eager ? LoadEventListener.INTERNAL_LOAD_EAGER : LoadEventListener.INTERNAL_LOAD_LAZY; LoadEvent event = new LoadEvent(id, entityName, true, this); fireLoad(event, type); if ( !nullable ) { UnresolvableObjectException.throwIfNull( event.getResult(), id, entityName ); } return event.getResult(); } public Object load(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException { return load( entityClass.getName(), id, lockMode ); } public Object load(Class entityClass, Serializable id, LockOptions lockOptions) throws HibernateException { return load( entityClass.getName(), id, lockOptions); } public Object load(String entityName, Serializable id, LockMode lockMode) throws HibernateException { LoadEvent event = new LoadEvent(id, entityName, lockMode, this); fireLoad( event, LoadEventListener.LOAD ); return event.getResult(); } public Object load(String entityName, Serializable id, LockOptions lockOptions) throws HibernateException { LoadEvent event = new LoadEvent(id, entityName, lockOptions, this); fireLoad( event, LoadEventListener.LOAD ); return event.getResult(); } public Object get(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException { return get( entityClass.getName(), id, lockMode ); } public Object get(Class entityClass, Serializable id, LockOptions lockOptions) throws HibernateException { return get( entityClass.getName(), id, lockOptions); } public Object get(String entityName, Serializable id, LockMode lockMode) throws HibernateException { LoadEvent event = new LoadEvent(id, entityName, lockMode, this); fireLoad(event, LoadEventListener.GET); return event.getResult(); } public Object get(String entityName, Serializable id, LockOptions lockOptions) throws HibernateException { LoadEvent event = new LoadEvent(id, entityName, lockOptions, this); fireLoad(event, LoadEventListener.GET); return event.getResult(); } private void fireLoad(LoadEvent event, LoadType loadType) { errorIfClosed(); checkTransactionSynchStatus(); LoadEventListener[] loadEventListener = listeners.getLoadEventListeners(); for ( int i = 0; i < loadEventListener.length; i++ ) { loadEventListener[i].onLoad(event, loadType); } } // refresh() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void refresh(Object object) throws HibernateException { fireRefresh( new RefreshEvent(object, this) ); } public void refresh(Object object, LockMode lockMode) throws HibernateException { fireRefresh( new RefreshEvent(object, lockMode, this) ); } public void refresh(Object object, LockOptions lockOptions) throws HibernateException { fireRefresh( new RefreshEvent(object, lockOptions, this) ); } public void refresh(Object object, Map refreshedAlready) throws HibernateException { fireRefresh( refreshedAlready, new RefreshEvent(object, this) ); } private void fireRefresh(RefreshEvent refreshEvent) { errorIfClosed(); checkTransactionSynchStatus(); RefreshEventListener[] refreshEventListener = listeners.getRefreshEventListeners(); for ( int i = 0; i < refreshEventListener.length; i++ ) { refreshEventListener[i].onRefresh( refreshEvent ); } } private void fireRefresh(Map refreshedAlready, RefreshEvent refreshEvent) { errorIfClosed(); checkTransactionSynchStatus(); RefreshEventListener[] refreshEventListener = listeners.getRefreshEventListeners(); for ( int i = 0; i < refreshEventListener.length; i++ ) { refreshEventListener[i].onRefresh( refreshEvent, refreshedAlready ); } } // replicate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public void replicate(Object obj, ReplicationMode replicationMode) throws HibernateException { fireReplicate( new ReplicateEvent(obj, replicationMode, this) ); } public void replicate(String entityName, Object obj, ReplicationMode replicationMode) throws HibernateException { fireReplicate( new ReplicateEvent(entityName, obj, replicationMode, this) ); } private void fireReplicate(ReplicateEvent event) { errorIfClosed(); checkTransactionSynchStatus(); ReplicateEventListener[] replicateEventListener = listeners.getReplicateEventListeners(); for ( int i = 0; i < replicateEventListener.length; i++ ) { replicateEventListener[i].onReplicate(event); } } // evict() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * remove any hard references to the entity that are held by the infrastructure * (references held by application or other persistant instances are okay) */ public void evict(Object object) throws HibernateException { fireEvict( new EvictEvent(object, this) ); } private void fireEvict(EvictEvent evictEvent) { errorIfClosed(); checkTransactionSynchStatus(); EvictEventListener[] evictEventListener = listeners.getEvictEventListeners(); for ( int i = 0; i < evictEventListener.length; i++ ) { evictEventListener[i].onEvict( evictEvent ); } } /** * detect in-memory changes, determine if the changes are to tables * named in the query and, if so, complete execution the flush */ protected boolean autoFlushIfRequired(Set querySpaces) throws HibernateException { errorIfClosed(); if ( ! isTransactionInProgress() ) { // do not auto-flush while outside a transaction return false; } AutoFlushEvent event = new AutoFlushEvent(querySpaces, this); AutoFlushEventListener[] autoFlushEventListener = listeners.getAutoFlushEventListeners(); for ( int i = 0; i < autoFlushEventListener.length; i++ ) { autoFlushEventListener[i].onAutoFlush(event); } return event.isFlushRequired(); } public boolean isDirty() throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); log.debug("checking session dirtiness"); if ( actionQueue.areInsertionsOrDeletionsQueued() ) { log.debug("session dirty (scheduled updates and insertions)"); return true; } else { DirtyCheckEvent event = new DirtyCheckEvent(this); DirtyCheckEventListener[] dirtyCheckEventListener = listeners.getDirtyCheckEventListeners(); for ( int i = 0; i < dirtyCheckEventListener.length; i++ ) { dirtyCheckEventListener[i].onDirtyCheck(event); } return event.isDirty(); } } public void flush() throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); if ( persistenceContext.getCascadeLevel() > 0 ) { throw new HibernateException("Flush during cascade is dangerous"); } FlushEventListener[] flushEventListener = listeners.getFlushEventListeners(); for ( int i = 0; i < flushEventListener.length; i++ ) { flushEventListener[i].onFlush( new FlushEvent(this) ); } } public void forceFlush(EntityEntry entityEntry) throws HibernateException { errorIfClosed(); if ( log.isDebugEnabled() ) { log.debug( "flushing to force deletion of re-saved object: " + MessageHelper.infoString( entityEntry.getPersister(), entityEntry.getId(), getFactory() ) ); } if ( persistenceContext.getCascadeLevel() > 0 ) { throw new ObjectDeletedException( "deleted object would be re-saved by cascade (remove deleted object from associations)", entityEntry.getId(), entityEntry.getPersister().getEntityName() ); } flush(); } /** * Retrieve a list of persistent objects using a hibernate query */ public List find(String query) throws HibernateException { return list( query, new QueryParameters() ); } public List find(String query, Object value, Type type) throws HibernateException { return list( query, new QueryParameters(type, value) ); } public List find(String query, Object[] values, Type[] types) throws HibernateException { return list( query, new QueryParameters(types, values) ); } public List list(String query, QueryParameters queryParameters) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); queryParameters.validateParameters(); HQLQueryPlan plan = getHQLQueryPlan( query, false ); autoFlushIfRequired( plan.getQuerySpaces() ); List results = CollectionHelper.EMPTY_LIST; boolean success = false; dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called try { results = plan.performList( queryParameters, this ); success = true; } finally { dontFlushFromFind--; afterOperation(success); } return results; } public int executeUpdate(String query, QueryParameters queryParameters) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); queryParameters.validateParameters(); HQLQueryPlan plan = getHQLQueryPlan( query, false ); autoFlushIfRequired( plan.getQuerySpaces() ); boolean success = false; int result = 0; try { result = plan.performExecuteUpdate( queryParameters, this ); success = true; } finally { afterOperation(success); } return result; } public int executeNativeUpdate(NativeSQLQuerySpecification nativeQuerySpecification, QueryParameters queryParameters) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); queryParameters.validateParameters(); NativeSQLQueryPlan plan = getNativeSQLQueryPlan(nativeQuerySpecification); autoFlushIfRequired( plan.getCustomQuery().getQuerySpaces() ); boolean success = false; int result = 0; try { result = plan.performExecuteUpdate(queryParameters, this); success = true; } finally { afterOperation(success); } return result; } public Iterator iterate(String query) throws HibernateException { return iterate( query, new QueryParameters() ); } public Iterator iterate(String query, Object value, Type type) throws HibernateException { return iterate( query, new QueryParameters(type, value) ); } public Iterator iterate(String query, Object[] values, Type[] types) throws HibernateException { return iterate( query, new QueryParameters(types, values) ); } public Iterator iterate(String query, QueryParameters queryParameters) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); queryParameters.validateParameters(); HQLQueryPlan plan = getHQLQueryPlan( query, true ); autoFlushIfRequired( plan.getQuerySpaces() ); dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called try { return plan.performIterate( queryParameters, this ); } finally { dontFlushFromFind--; } } public ScrollableResults scroll(String query, QueryParameters queryParameters) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); HQLQueryPlan plan = getHQLQueryPlan( query, false ); autoFlushIfRequired( plan.getQuerySpaces() ); dontFlushFromFind++; try { return plan.performScroll( queryParameters, this ); } finally { dontFlushFromFind--; } } public int delete(String query) throws HibernateException { return delete( query, ArrayHelper.EMPTY_OBJECT_ARRAY, ArrayHelper.EMPTY_TYPE_ARRAY ); } public int delete(String query, Object value, Type type) throws HibernateException { return delete( query, new Object[]{value}, new Type[]{type} ); } public int delete(String query, Object[] values, Type[] types) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); if ( query == null ) { throw new IllegalArgumentException("attempt to doAfterTransactionCompletion delete-by-query with null query"); } if ( log.isTraceEnabled() ) { log.trace( "delete: " + query ); if ( values.length != 0 ) { log.trace( "parameters: " + StringHelper.toString( values ) ); } } List list = find( query, values, types ); int deletionCount = list.size(); for ( int i = 0; i < deletionCount; i++ ) { delete( list.get( i ) ); } return deletionCount; } public Query createFilter(Object collection, String queryString) { errorIfClosed(); checkTransactionSynchStatus(); CollectionFilterImpl filter = new CollectionFilterImpl( queryString, collection, this, getFilterQueryPlan( collection, queryString, null, false ).getParameterMetadata() ); filter.setComment( queryString ); return filter; } public Query getNamedQuery(String queryName) throws MappingException { errorIfClosed(); checkTransactionSynchStatus(); return super.getNamedQuery(queryName); } public Object instantiate(String entityName, Serializable id) throws HibernateException { return instantiate( factory.getEntityPersister(entityName), id ); } /** * give the interceptor an opportunity to override the default instantiation */ public Object instantiate(EntityPersister persister, Serializable id) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); Object result = interceptor.instantiate( persister.getEntityName(), entityMode, id ); if ( result == null ) { result = persister.instantiate( id, this ); } return result; } public EntityMode getEntityMode() { checkTransactionSynchStatus(); return entityMode; } public void setFlushMode(FlushMode flushMode) { errorIfClosed(); checkTransactionSynchStatus(); if ( log.isTraceEnabled() ) { log.trace("setting flush mode to: " + flushMode); } this.flushMode = flushMode; } public FlushMode getFlushMode() { checkTransactionSynchStatus(); return flushMode; } public CacheMode getCacheMode() { checkTransactionSynchStatus(); return cacheMode; } public void setCacheMode(CacheMode cacheMode) { errorIfClosed(); checkTransactionSynchStatus(); if ( log.isTraceEnabled() ) { log.trace("setting cache mode to: " + cacheMode); } this.cacheMode= cacheMode; } public Transaction getTransaction() throws HibernateException { errorIfClosed(); return jdbcContext.getTransaction(); } public Transaction beginTransaction() throws HibernateException { errorIfClosed(); if ( rootSession != null ) { // todo : should seriously consider not allowing a txn to begin from a child session // can always route the request to the root session... log.warn( "Transaction started on non-root session" ); } Transaction result = getTransaction(); result.begin(); return result; } public void afterTransactionBegin(Transaction tx) { errorIfClosed(); interceptor.afterTransactionBegin(tx); } public EntityPersister getEntityPersister(final String entityName, final Object object) { errorIfClosed(); if (entityName==null) { return factory.getEntityPersister( guessEntityName( object ) ); } else { // try block is a hack around fact that currently tuplizers are not // given the opportunity to resolve a subclass entity name. this // allows the (we assume custom) interceptor the ability to // influence this decision if we were not able to based on the // given entityName try { return factory.getEntityPersister( entityName ) .getSubclassEntityPersister( object, getFactory(), entityMode ); } catch( HibernateException e ) { try { return getEntityPersister( null, object ); } catch( HibernateException e2 ) { throw e; } } } } // not for internal use: public Serializable getIdentifier(Object object) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); if ( object instanceof HibernateProxy ) { LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer(); if ( li.getSession() != this ) { throw new TransientObjectException( "The proxy was not associated with this session" ); } return li.getIdentifier(); } else { EntityEntry entry = persistenceContext.getEntry(object); if ( entry == null ) { throw new TransientObjectException( "The instance was not associated with this session" ); } return entry.getId(); } } /** * Get the id value for an object that is actually associated with the session. This * is a bit stricter than getEntityIdentifierIfNotUnsaved(). */ public Serializable getContextEntityIdentifier(Object object) { errorIfClosed(); if ( object instanceof HibernateProxy ) { return getProxyIdentifier(object); } else { EntityEntry entry = persistenceContext.getEntry(object); return entry != null ? entry.getId() : null; } } private Serializable getProxyIdentifier(Object proxy) { return ( (HibernateProxy) proxy ).getHibernateLazyInitializer().getIdentifier(); } public Collection filter(Object collection, String filter) throws HibernateException { return listFilter( collection, filter, new QueryParameters( new Type[1], new Object[1] ) ); } public Collection filter(Object collection, String filter, Object value, Type type) throws HibernateException { return listFilter( collection, filter, new QueryParameters( new Type[]{null, type}, new Object[]{null, value} ) ); } public Collection filter(Object collection, String filter, Object[] values, Type[] types) throws HibernateException { Object[] vals = new Object[values.length + 1]; Type[] typs = new Type[types.length + 1]; System.arraycopy( values, 0, vals, 1, values.length ); System.arraycopy( types, 0, typs, 1, types.length ); return listFilter( collection, filter, new QueryParameters( typs, vals ) ); } private FilterQueryPlan getFilterQueryPlan( Object collection, String filter, QueryParameters parameters, boolean shallow) throws HibernateException { if ( collection == null ) { throw new NullPointerException( "null collection passed to filter" ); } CollectionEntry entry = persistenceContext.getCollectionEntryOrNull( collection ); final CollectionPersister roleBeforeFlush = (entry == null) ? null : entry.getLoadedPersister(); FilterQueryPlan plan = null; if ( roleBeforeFlush == null ) { // if it was previously unreferenced, we need to flush in order to // get its state into the database in order to execute query flush(); entry = persistenceContext.getCollectionEntryOrNull( collection ); CollectionPersister roleAfterFlush = (entry == null) ? null : entry.getLoadedPersister(); if ( roleAfterFlush == null ) { throw new QueryException( "The collection was unreferenced" ); } plan = factory.getQueryPlanCache().getFilterQueryPlan( filter, roleAfterFlush.getRole(), shallow, getEnabledFilters() ); } else { // otherwise, we only need to flush if there are in-memory changes // to the queried tables plan = factory.getQueryPlanCache().getFilterQueryPlan( filter, roleBeforeFlush.getRole(), shallow, getEnabledFilters() ); if ( autoFlushIfRequired( plan.getQuerySpaces() ) ) { // might need to run a different filter entirely after the flush // because the collection role may have changed entry = persistenceContext.getCollectionEntryOrNull( collection ); CollectionPersister roleAfterFlush = (entry == null) ? null : entry.getLoadedPersister(); if ( roleBeforeFlush != roleAfterFlush ) { if ( roleAfterFlush == null ) { throw new QueryException( "The collection was dereferenced" ); } plan = factory.getQueryPlanCache().getFilterQueryPlan( filter, roleAfterFlush.getRole(), shallow, getEnabledFilters() ); } } } if ( parameters != null ) { parameters.getPositionalParameterValues()[0] = entry.getLoadedKey(); parameters.getPositionalParameterTypes()[0] = entry.getLoadedPersister().getKeyType(); } return plan; } public List listFilter(Object collection, String filter, QueryParameters queryParameters) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); FilterQueryPlan plan = getFilterQueryPlan( collection, filter, queryParameters, false ); List results = CollectionHelper.EMPTY_LIST; boolean success = false; dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called try { results = plan.performList( queryParameters, this ); success = true; } finally { dontFlushFromFind--; afterOperation(success); } return results; } public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); FilterQueryPlan plan = getFilterQueryPlan( collection, filter, queryParameters, true ); return plan.performIterate( queryParameters, this ); } public Criteria createCriteria(Class persistentClass, String alias) { errorIfClosed(); checkTransactionSynchStatus(); return new CriteriaImpl( persistentClass.getName(), alias, this ); } public Criteria createCriteria(String entityName, String alias) { errorIfClosed(); checkTransactionSynchStatus(); return new CriteriaImpl(entityName, alias, this); } public Criteria createCriteria(Class persistentClass) { errorIfClosed(); checkTransactionSynchStatus(); return new CriteriaImpl( persistentClass.getName(), this ); } public Criteria createCriteria(String entityName) { errorIfClosed(); checkTransactionSynchStatus(); return new CriteriaImpl(entityName, this); } public ScrollableResults scroll(CriteriaImpl criteria, ScrollMode scrollMode) { errorIfClosed(); checkTransactionSynchStatus(); String entityName = criteria.getEntityOrClassName(); CriteriaLoader loader = new CriteriaLoader( getOuterJoinLoadable(entityName), factory, criteria, entityName, getLoadQueryInfluencers() ); autoFlushIfRequired( loader.getQuerySpaces() ); dontFlushFromFind++; try { return loader.scroll(this, scrollMode); } finally { dontFlushFromFind--; } } public List list(CriteriaImpl criteria) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); String[] implementors = factory.getImplementors( criteria.getEntityOrClassName() ); int size = implementors.length; CriteriaLoader[] loaders = new CriteriaLoader[size]; Set spaces = new HashSet(); for( int i=0; i <size; i++ ) { loaders[i] = new CriteriaLoader( getOuterJoinLoadable( implementors[i] ), factory, criteria, implementors[i], getLoadQueryInfluencers() ); spaces.addAll( loaders[i].getQuerySpaces() ); } autoFlushIfRequired(spaces); List results = Collections.EMPTY_LIST; dontFlushFromFind++; boolean success = false; try { for( int i=0; i<size; i++ ) { final List currentResults = loaders[i].list(this); currentResults.addAll(results); results = currentResults; } success = true; } finally { dontFlushFromFind--; afterOperation(success); } return results; } private OuterJoinLoadable getOuterJoinLoadable(String entityName) throws MappingException { EntityPersister persister = factory.getEntityPersister(entityName); if ( !(persister instanceof OuterJoinLoadable) ) { throw new MappingException( "class persister is not OuterJoinLoadable: " + entityName ); } return ( OuterJoinLoadable ) persister; } public boolean contains(Object object) { errorIfClosed(); checkTransactionSynchStatus(); if ( object instanceof HibernateProxy ) { //do not use proxiesByKey, since not all //proxies that point to this session's //instances are in that collection! LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer(); if ( li.isUninitialized() ) { //if it is an uninitialized proxy, pointing //with this session, then when it is accessed, //the underlying instance will be "contained" return li.getSession()==this; } else { //if it is initialized, see if the underlying //instance is contained, since we need to //account for the fact that it might have been //evicted object = li.getImplementation(); } } // A session is considered to contain an entity only if the entity has // an entry in the session's persistence context and the entry reports // that the entity has not been removed EntityEntry entry = persistenceContext.getEntry( object ); return entry != null && entry.getStatus() != Status.DELETED && entry.getStatus() != Status.GONE; } public Query createQuery(String queryString) { errorIfClosed(); checkTransactionSynchStatus(); return super.createQuery(queryString); } public SQLQuery createSQLQuery(String sql) { errorIfClosed(); checkTransactionSynchStatus(); return super.createSQLQuery(sql); } public Query createSQLQuery(String sql, String returnAlias, Class returnClass) { errorIfClosed(); checkTransactionSynchStatus(); return new SQLQueryImpl( sql, new String[] { returnAlias }, new Class[] { returnClass }, this, factory.getQueryPlanCache().getSQLParameterMetadata( sql ) ); } public Query createSQLQuery(String sql, String returnAliases[], Class returnClasses[]) { errorIfClosed(); checkTransactionSynchStatus(); return new SQLQueryImpl( sql, returnAliases, returnClasses, this, factory.getQueryPlanCache().getSQLParameterMetadata( sql ) ); } public ScrollableResults scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); if ( log.isTraceEnabled() ) { log.trace( "scroll SQL query: " + customQuery.getSQL() ); } CustomLoader loader = new CustomLoader( customQuery, getFactory() ); autoFlushIfRequired( loader.getQuerySpaces() ); dontFlushFromFind++; //stops flush being called multiple times if this method is recursively called try { return loader.scroll(queryParameters, this); } finally { dontFlushFromFind--; } } // basically just an adapted copy of find(CriteriaImpl) public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); if ( log.isTraceEnabled() ) { log.trace( "SQL query: " + customQuery.getSQL() ); } CustomLoader loader = new CustomLoader( customQuery, getFactory() ); autoFlushIfRequired( loader.getQuerySpaces() ); dontFlushFromFind++; boolean success = false; try { List results = loader.list(this, queryParameters); success = true; return results; } finally { dontFlushFromFind--; afterOperation(success); } } public SessionFactory getSessionFactory() { checkTransactionSynchStatus(); return factory; } public void initializeCollection(PersistentCollection collection, boolean writing) throws HibernateException { errorIfClosed(); checkTransactionSynchStatus(); InitializeCollectionEventListener[] listener = listeners.getInitializeCollectionEventListeners(); for ( int i = 0; i < listener.length; i++ ) { listener[i].onInitializeCollection( new InitializeCollectionEvent(collection, this) ); } } public String bestGuessEntityName(Object object) { if (object instanceof HibernateProxy) { LazyInitializer initializer = ( ( HibernateProxy ) object ).getHibernateLazyInitializer(); // it is possible for this method to be called during flush processing, // so make certain that we do not accidently initialize an uninitialized proxy if ( initializer.isUninitialized() ) { return initializer.getEntityName(); } object = initializer.getImplementation(); } EntityEntry entry = persistenceContext.getEntry(object); if (entry==null) { return guessEntityName(object); } else { return entry.getPersister().getEntityName(); } } public String getEntityName(Object object) { errorIfClosed(); checkTransactionSynchStatus(); if (object instanceof HibernateProxy) { if ( !persistenceContext.containsProxy( object ) ) { throw new TransientObjectException("proxy was not associated with the session"); } object = ( (HibernateProxy) object ).getHibernateLazyInitializer().getImplementation(); } EntityEntry entry = persistenceContext.getEntry(object); if ( entry == null ) { throwTransientObjectException( object ); } return entry.getPersister().getEntityName(); } private void throwTransientObjectException(Object object) throws HibernateException { throw new TransientObjectException( "object references an unsaved transient instance - save the transient instance before flushing: " + guessEntityName(object) ); } public String guessEntityName(Object object) throws HibernateException { errorIfClosed(); return entityNameResolver.resolveEntityName( object ); } public void cancelQuery() throws HibernateException { errorIfClosed(); getBatcher().cancelLastQuery(); } public Interceptor getInterceptor() { checkTransactionSynchStatus(); return interceptor; } public int getDontFlushFromFind() { return dontFlushFromFind; } public String toString() { StringBuffer buf = new StringBuffer(500) .append( "SessionImpl(" ); if ( !isClosed() ) { buf.append(persistenceContext) .append(";") .append(actionQueue); } else { buf.append("<closed>"); } return buf.append(')').toString(); } public EventListeners getListeners() { return listeners; } public ActionQueue getActionQueue() { errorIfClosed(); checkTransactionSynchStatus(); return actionQueue; } public PersistenceContext getPersistenceContext() { errorIfClosed(); checkTransactionSynchStatus(); return persistenceContext; } public SessionStatistics getStatistics() { checkTransactionSynchStatus(); return new SessionStatisticsImpl(this); } public boolean isEventSource() { checkTransactionSynchStatus(); return true; } /** * {@inheritDoc} */ public boolean isDefaultReadOnly() { return persistenceContext.isDefaultReadOnly(); } /** * {@inheritDoc} */ public void setDefaultReadOnly(boolean defaultReadOnly) { persistenceContext.setDefaultReadOnly( defaultReadOnly ); } public boolean isReadOnly(Object entityOrProxy) { errorIfClosed(); checkTransactionSynchStatus(); return persistenceContext.isReadOnly( entityOrProxy ); } public void setReadOnly(Object entity, boolean readOnly) { errorIfClosed(); checkTransactionSynchStatus(); persistenceContext.setReadOnly(entity, readOnly); } public void doWork(Work work) throws HibernateException { try { work.execute( jdbcContext.getConnectionManager().getConnection() ); jdbcContext.getConnectionManager().afterStatement(); } catch ( SQLException e ) { throw JDBCExceptionHelper.convert( factory.getSettings().getSQLExceptionConverter(), e, "error executing work" ); } } public void afterScrollOperation() { // nothing to do in a stateful session } public JDBCContext getJDBCContext() { errorIfClosed(); checkTransactionSynchStatus(); return jdbcContext; } public LoadQueryInfluencers getLoadQueryInfluencers() { return loadQueryInfluencers; } // filter support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * {@inheritDoc} */ public Filter getEnabledFilter(String filterName) { checkTransactionSynchStatus(); return loadQueryInfluencers.getEnabledFilter( filterName ); } /** * {@inheritDoc} */ public Filter enableFilter(String filterName) { errorIfClosed(); checkTransactionSynchStatus(); return loadQueryInfluencers.enableFilter( filterName ); } /** * {@inheritDoc} */ public void disableFilter(String filterName) { errorIfClosed(); checkTransactionSynchStatus(); loadQueryInfluencers.disableFilter( filterName ); } /** * {@inheritDoc} */ public Object getFilterParameterValue(String filterParameterName) { errorIfClosed(); checkTransactionSynchStatus(); return loadQueryInfluencers.getFilterParameterValue( filterParameterName ); } /** * {@inheritDoc} */ public Type getFilterParameterType(String filterParameterName) { errorIfClosed(); checkTransactionSynchStatus(); return loadQueryInfluencers.getFilterParameterType( filterParameterName ); } /** * {@inheritDoc} */ public Map getEnabledFilters() { errorIfClosed(); checkTransactionSynchStatus(); return loadQueryInfluencers.getEnabledFilters(); } // internal fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * {@inheritDoc} */ public String getFetchProfile() { checkTransactionSynchStatus(); return loadQueryInfluencers.getInternalFetchProfile(); } /** * {@inheritDoc} */ public void setFetchProfile(String fetchProfile) { errorIfClosed(); checkTransactionSynchStatus(); loadQueryInfluencers.setInternalFetchProfile( fetchProfile ); } // fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ public boolean isFetchProfileEnabled(String name) throws UnknownProfileException { return loadQueryInfluencers.isFetchProfileEnabled( name ); } public void enableFetchProfile(String name) throws UnknownProfileException { loadQueryInfluencers.enableFetchProfile( name ); } public void disableFetchProfile(String name) throws UnknownProfileException { loadQueryInfluencers.disableFetchProfile( name ); } private void checkTransactionSynchStatus() { if ( jdbcContext != null && !isClosed() ) { jdbcContext.registerSynchronizationIfPossible(); } } /** * Used by JDK serialization... * * @param ois The input stream from which we are being read... * @throws IOException Indicates a general IO stream exception * @throws ClassNotFoundException Indicates a class resolution issue */ private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { log.trace( "deserializing session" ); ois.defaultReadObject(); entityNameResolver = new CoordinatingEntityNameResolver(); boolean isRootSession = ois.readBoolean(); connectionReleaseMode = ConnectionReleaseMode.parse( ( String ) ois.readObject() ); entityMode = EntityMode.parse( ( String ) ois.readObject() ); autoClear = ois.readBoolean(); flushMode = FlushMode.parse( ( String ) ois.readObject() ); cacheMode = CacheMode.parse( ( String ) ois.readObject() ); flushBeforeCompletionEnabled = ois.readBoolean(); autoCloseSessionEnabled = ois.readBoolean(); interceptor = ( Interceptor ) ois.readObject(); factory = SessionFactoryImpl.deserialize( ois ); listeners = factory.getEventListeners(); if ( isRootSession ) { jdbcContext = JDBCContext.deserialize( ois, this, interceptor ); } persistenceContext = StatefulPersistenceContext.deserialize( ois, this ); actionQueue = ActionQueue.deserialize( ois, this ); loadQueryInfluencers = ( LoadQueryInfluencers ) ois.readObject(); childSessionsByEntityMode = ( Map ) ois.readObject(); // LoadQueryInfluencers.getEnabledFilters() tries to validate each enabled // filter, which will fail when called before FilterImpl.afterDeserialize( factory ); // Instead lookup the filter by name and then call FilterImpl.afterDeserialize( factory ). Iterator iter = loadQueryInfluencers.getEnabledFilterNames().iterator(); while ( iter.hasNext() ) { String filterName = ( String ) iter.next(); ( ( FilterImpl ) loadQueryInfluencers.getEnabledFilter( filterName ) ) .afterDeserialize( factory ); } if ( isRootSession && childSessionsByEntityMode != null ) { iter = childSessionsByEntityMode.values().iterator(); while ( iter.hasNext() ) { final SessionImpl child = ( ( SessionImpl ) iter.next() ); child.rootSession = this; child.jdbcContext = this.jdbcContext; } } } /** * Used by JDK serialization... * * @param oos The output stream to which we are being written... * @throws IOException Indicates a general IO stream exception */ private void writeObject(ObjectOutputStream oos) throws IOException { if ( !jdbcContext.getConnectionManager().isReadyForSerialization() ) { throw new IllegalStateException( "Cannot serialize a session while connected" ); } log.trace( "serializing session" ); oos.defaultWriteObject(); oos.writeBoolean( rootSession == null ); oos.writeObject( connectionReleaseMode.toString() ); oos.writeObject( entityMode.toString() ); oos.writeBoolean( autoClear ); oos.writeObject( flushMode.toString() ); oos.writeObject( cacheMode.toString() ); oos.writeBoolean( flushBeforeCompletionEnabled ); oos.writeBoolean( autoCloseSessionEnabled ); // we need to writeObject() on this since interceptor is user defined oos.writeObject( interceptor ); factory.serialize( oos ); if ( rootSession == null ) { jdbcContext.serialize( oos ); } persistenceContext.serialize( oos ); actionQueue.serialize( oos ); // todo : look at optimizing these... oos.writeObject( loadQueryInfluencers ); oos.writeObject( childSessionsByEntityMode ); } /** * {@inheritDoc} */ public Object execute(Callback callback) { Connection connection = jdbcContext.getConnectionManager().getConnection(); try { return callback.executeOnConnection( connection ); } catch ( SQLException e ) { throw JDBCExceptionHelper.convert( getFactory().getSQLExceptionConverter(), e, "Error creating contextual LOB : " + e.getMessage() ); } finally { jdbcContext.getConnectionManager().afterStatement(); } } /** * {@inheritDoc} */ public TypeHelper getTypeHelper() { return getSessionFactory().getTypeHelper(); } /** * {@inheritDoc} */ public LobHelper getLobHelper() { if ( lobHelper == null ) { lobHelper = new LobHelperImpl( this ); } return lobHelper; } private transient LobHelperImpl lobHelper; private static class LobHelperImpl implements LobHelper { private final SessionImpl session; private LobHelperImpl(SessionImpl session) { this.session = session; } /** * {@inheritDoc} */ public Blob createBlob(byte[] bytes) { return lobCreator().createBlob( bytes ); } private LobCreator lobCreator() { return session.getFactory().getSettings().getJdbcSupport().getLobCreator( session ); } /** * {@inheritDoc} */ public Blob createBlob(InputStream stream, long length) { return lobCreator().createBlob( stream, length ); } /** * {@inheritDoc} */ public Clob createClob(String string) { return lobCreator().createClob( string ); } /** * {@inheritDoc} */ public Clob createClob(Reader reader, long length) { return lobCreator().createClob( reader, length ); } /** * {@inheritDoc} */ public Clob createNClob(String string) { return lobCreator().createNClob( string ); } /** * {@inheritDoc} */ public Clob createNClob(Reader reader, long length) { return lobCreator().createNClob( reader, length ); } } private class CoordinatingEntityNameResolver implements EntityNameResolver { public String resolveEntityName(Object entity) { String entityName = interceptor.getEntityName( entity ); if ( entityName != null ) { return entityName; } Iterator itr = factory.iterateEntityNameResolvers( entityMode ); while ( itr.hasNext() ) { final EntityNameResolver resolver = ( EntityNameResolver ) itr.next(); entityName = resolver.resolveEntityName( entity ); if ( entityName != null ) { break; } } if ( entityName != null ) { return entityName; } // the old-time stand-by... return entity.getClass().getName(); } } private class LockRequestImpl implements LockRequest { private final LockOptions lockOptions; private LockRequestImpl(LockOptions lo) { lockOptions = new LockOptions(); LockOptions.copy(lo, lockOptions); } public LockMode getLockMode() { return lockOptions.getLockMode(); } public LockRequest setLockMode(LockMode lockMode) { lockOptions.setLockMode(lockMode); return this; } public int getTimeOut() { return lockOptions.getTimeOut(); } public LockRequest setTimeOut(int timeout) { lockOptions.setTimeOut(timeout); return this; } public boolean getScope() { return lockOptions.getScope(); } public LockRequest setScope(boolean scope) { lockOptions.setScope(scope); return this; } public void lock(String entityName, Object object) throws HibernateException { fireLock( entityName, object, lockOptions ); } public void lock(Object object) throws HibernateException { fireLock( object, lockOptions ); } } }