org.hibernate.internal.SessionImpl Maven / Gradle / Ivy
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.internal;
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.sql.Blob;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.NClob;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Supplier;
import jakarta.persistence.CacheRetrieveMode;
import jakarta.persistence.CacheStoreMode;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityNotFoundException;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.LockModeType;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.StoredProcedureQuery;
import jakarta.persistence.TransactionRequiredException;
import jakarta.persistence.criteria.CriteriaBuilder;
import org.hibernate.CacheMode;
import org.hibernate.Criteria;
import org.hibernate.Filter;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.IdentifierLoadAccess;
import org.hibernate.JDBCException;
import org.hibernate.LobHelper;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.MultiIdentifierLoadAccess;
import org.hibernate.NaturalIdLoadAccess;
import org.hibernate.ObjectDeletedException;
import org.hibernate.ObjectNotFoundException;
import org.hibernate.QueryException;
import org.hibernate.ReplicationMode;
import org.hibernate.ScrollMode;
import org.hibernate.Session;
import org.hibernate.SessionEventListener;
import org.hibernate.SessionException;
import org.hibernate.SharedSessionBuilder;
import org.hibernate.SimpleNaturalIdLoadAccess;
import org.hibernate.Transaction;
import org.hibernate.TransientObjectException;
import org.hibernate.TypeHelper;
import org.hibernate.TypeMismatchException;
import org.hibernate.UnknownProfileException;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.collection.spi.PersistentCollection;
import org.hibernate.criterion.NaturalIdentifier;
import org.hibernate.engine.internal.StatefulPersistenceContext;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.NonContextualLobCreator;
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.query.spi.FilterQueryPlan;
import org.hibernate.engine.query.spi.HQLQueryPlan;
import org.hibernate.engine.query.spi.NativeSQLQueryPlan;
import org.hibernate.engine.query.spi.sql.NativeSQLQuerySpecification;
import org.hibernate.engine.spi.ActionQueue;
import org.hibernate.engine.spi.CollectionEntry;
import org.hibernate.engine.spi.EffectiveEntityGraph;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.NamedQueryDefinition;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.spi.Status;
import org.hibernate.engine.spi.TypedValue;
import org.hibernate.engine.transaction.spi.TransactionImplementor;
import org.hibernate.engine.transaction.spi.TransactionObserver;
import org.hibernate.event.spi.AutoFlushEvent;
import org.hibernate.event.spi.AutoFlushEventListener;
import org.hibernate.event.spi.ClearEvent;
import org.hibernate.event.spi.ClearEventListener;
import org.hibernate.event.spi.DeleteEvent;
import org.hibernate.event.spi.DeleteEventListener;
import org.hibernate.event.spi.DirtyCheckEvent;
import org.hibernate.event.spi.DirtyCheckEventListener;
import org.hibernate.event.spi.EventSource;
import org.hibernate.event.spi.EvictEvent;
import org.hibernate.event.spi.EvictEventListener;
import org.hibernate.event.spi.FlushEvent;
import org.hibernate.event.spi.FlushEventListener;
import org.hibernate.event.spi.InitializeCollectionEvent;
import org.hibernate.event.spi.InitializeCollectionEventListener;
import org.hibernate.event.spi.LoadEvent;
import org.hibernate.event.spi.LoadEventListener;
import org.hibernate.event.spi.LoadEventListener.LoadType;
import org.hibernate.event.spi.LockEvent;
import org.hibernate.event.spi.LockEventListener;
import org.hibernate.event.spi.MergeEvent;
import org.hibernate.event.spi.MergeEventListener;
import org.hibernate.event.spi.PersistEvent;
import org.hibernate.event.spi.PersistEventListener;
import org.hibernate.event.spi.RefreshEvent;
import org.hibernate.event.spi.RefreshEventListener;
import org.hibernate.event.spi.ReplicateEvent;
import org.hibernate.event.spi.ReplicateEventListener;
import org.hibernate.event.spi.ResolveNaturalIdEvent;
import org.hibernate.event.spi.ResolveNaturalIdEventListener;
import org.hibernate.event.spi.SaveOrUpdateEvent;
import org.hibernate.event.spi.SaveOrUpdateEventListener;
import org.hibernate.graph.GraphSemantic;
import org.hibernate.graph.RootGraph;
import org.hibernate.graph.internal.RootGraphImpl;
import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.hql.spi.QueryTranslator;
import org.hibernate.internal.CriteriaImpl.CriterionEntry;
import org.hibernate.internal.log.DeprecationLogger;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.QueryHints;
import org.hibernate.jpa.internal.util.CacheModeHelper;
import org.hibernate.jpa.internal.util.ConfigurationHelper;
import org.hibernate.jpa.internal.util.FlushModeTypeHelper;
import org.hibernate.jpa.internal.util.LockModeTypeHelper;
import org.hibernate.jpa.internal.util.LockOptionsHelper;
import org.hibernate.jpa.spi.HibernateEntityManagerImplementor;
import org.hibernate.loader.criteria.CriteriaLoader;
import org.hibernate.loader.custom.CustomLoader;
import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.metamodel.spi.MetamodelImplementor;
import org.hibernate.param.CollectionFilterKeyParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.MultiLoadOptions;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.procedure.ProcedureCallMemento;
import org.hibernate.procedure.UnknownSqlResultSetMappingException;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.LazyInitializer;
import org.hibernate.query.ImmutableEntityUpdateQueryHandlingMode;
import org.hibernate.query.Query;
import org.hibernate.query.internal.CollectionFilterImpl;
import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.resource.transaction.TransactionRequiredForJoinException;
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl;
import org.hibernate.resource.transaction.spi.TransactionCoordinator;
import org.hibernate.resource.transaction.spi.TransactionStatus;
import org.hibernate.stat.SessionStatistics;
import org.hibernate.stat.internal.SessionStatisticsImpl;
import org.hibernate.stat.spi.StatisticsImplementor;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_JPA_LOCK_SCOPE;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_JPA_LOCK_TIMEOUT;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_JPA_SHARED_CACHE_RETRIEVE_MODE;
import static org.hibernate.cfg.AvailableSettings.JAKARTA_JPA_SHARED_CACHE_STORE_MODE;
import static org.hibernate.cfg.AvailableSettings.JPA_LOCK_SCOPE;
import static org.hibernate.cfg.AvailableSettings.JPA_LOCK_TIMEOUT;
import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_RETRIEVE_MODE;
import static org.hibernate.cfg.AvailableSettings.JPA_SHARED_CACHE_STORE_MODE;
/**
* Concrete implementation of a Session.
*
* Exposes two interfaces:
* - {@link org.hibernate.Session} to the application
* - {@link org.hibernate.engine.spi.SessionImplementor} to other Hibernate components (SPI)
*
*
* This class is not thread-safe.
*
* @author Gavin King
* @author Steve Ebersole
* @author Brett Meyer
* @author Chris Cranford
* @author Sanne Grinovero
*/
public class SessionImpl
extends AbstractSessionImpl
implements EventSource, SessionImplementor, HibernateEntityManagerImplementor {
private static final EntityManagerMessageLogger log = HEMLogging.messageLogger( SessionImpl.class );
// Defaults to null which means the properties are the default - as defined in FastSessionServices#defaultSessionProperties
private Map properties;
private transient ActionQueue actionQueue;
private transient StatefulPersistenceContext persistenceContext;
private transient LoadQueryInfluencers loadQueryInfluencers;
private LockOptions lockOptions;
private boolean autoClear;
private boolean autoClose;
private boolean queryParametersValidationEnabled;
private transient int dontFlushFromFind;
private transient LoadEvent loadEvent; //cached LoadEvent instance
private transient TransactionObserver transactionObserver;
private transient boolean isEnforcingFetchGraph;
public SessionImpl(SessionFactoryImpl factory, SessionCreationOptions options) {
super( factory, options );
this.persistenceContext = createPersistenceContext();
this.actionQueue = createActionQueue();
this.autoClear = options.shouldAutoClear();
this.autoClose = options.shouldAutoClose();
this.queryParametersValidationEnabled = options.isQueryParametersValidationEnabled();
if ( options instanceof SharedSessionCreationOptions ) {
final SharedSessionCreationOptions sharedOptions = (SharedSessionCreationOptions) options;
final ActionQueue.TransactionCompletionProcesses transactionCompletionProcesses = sharedOptions.getTransactionCompletionProcesses();
if ( sharedOptions.isTransactionCoordinatorShared() && transactionCompletionProcesses != null ) {
actionQueue.setTransactionCompletionProcesses(
transactionCompletionProcesses,
true
);
}
}
loadQueryInfluencers = new LoadQueryInfluencers( factory );
final StatisticsImplementor statistics = factory.getStatistics();
if ( statistics.isStatisticsEnabled() ) {
statistics.openSession();
}
if ( this.properties != null ) {
//There might be custom properties for this session that affect the LockOptions state
LockOptionsHelper.applyPropertiesToLockOptions( this.properties, this::getLockOptionsForWrite );
}
getSession().setCacheMode( fastSessionServices.initialSessionCacheMode );
// NOTE : pulse() already handles auto-join-ability correctly
getTransactionCoordinator().pulse();
// do not override explicitly set flush mode ( SessionBuilder#flushMode() )
if ( getHibernateFlushMode() == null ) {
final FlushMode initialMode;
if ( this.properties == null ) {
initialMode = fastSessionServices.initialSessionFlushMode;
}
else {
initialMode = ConfigurationHelper.getFlushMode( getSessionProperty( AvailableSettings.FLUSH_MODE ), FlushMode.AUTO );
}
getSession().setHibernateFlushMode( initialMode );
}
if ( log.isTraceEnabled() ) {
log.tracef( "Opened Session [%s] at timestamp: %s", getSessionIdentifier(), getTimestamp() );
}
}
protected StatefulPersistenceContext createPersistenceContext() {
return new StatefulPersistenceContext( this );
}
protected ActionQueue createActionQueue() {
return new ActionQueue( this );
}
private LockOptions getLockOptionsForRead() {
return this.lockOptions == null ? fastSessionServices.defaultLockOptions : this.lockOptions;
}
private LockOptions getLockOptionsForWrite() {
if ( this.lockOptions == null ) {
this.lockOptions = new LockOptions();
}
return this.lockOptions;
}
protected void applyQuerySettingsAndHints(Query query) {
final LockOptions lockOptionsForRead = getLockOptionsForRead();
if ( lockOptionsForRead.getLockMode() != LockMode.NONE ) {
query.setLockMode( getLockMode( lockOptionsForRead.getLockMode() ) );
}
final Object queryTimeout;
if ( ( queryTimeout = getSessionProperty( QueryHints.SPEC_HINT_TIMEOUT ) ) != null ) {
query.setHint( QueryHints.SPEC_HINT_TIMEOUT, queryTimeout );
}
final Object jakartaQueryTimeout;
if ( ( jakartaQueryTimeout = getSessionProperty( QueryHints.JAKARTA_SPEC_HINT_TIMEOUT ) ) != null ) {
query.setHint( QueryHints.JAKARTA_SPEC_HINT_TIMEOUT, jakartaQueryTimeout );
}
final Object lockTimeout;
final Object jpaLockTimeout = getSessionProperty( JPA_LOCK_TIMEOUT );
if ( jpaLockTimeout == null ) {
lockTimeout = getSessionProperty( JAKARTA_JPA_LOCK_TIMEOUT );
}
else if ( Integer.valueOf( LockOptions.WAIT_FOREVER ).equals( jpaLockTimeout ) ) {
final Object jakartaLockTimeout = getSessionProperty( JAKARTA_JPA_LOCK_TIMEOUT );
if ( jakartaLockTimeout == null ) {
lockTimeout = jpaLockTimeout;
}
else {
lockTimeout = jakartaLockTimeout;
}
}
else {
lockTimeout = jpaLockTimeout;
}
if ( lockTimeout != null ) {
query.setHint( JPA_LOCK_TIMEOUT, lockTimeout );
}
}
private Object getSessionProperty(final String name) {
if ( properties == null ) {
return fastSessionServices.defaultSessionProperties.get( name );
}
else {
return properties.get( name );
}
}
@Override
public SharedSessionBuilder sessionWithOptions() {
return new SharedSessionBuilderImpl( this );
}
@Override
public void clear() {
checkOpen();
// Do not call checkTransactionSynchStatus() here -- if a delayed
// afterCompletion exists, it can cause an infinite loop.
pulseTransactionCoordinator();
try {
internalClear();
}
catch (RuntimeException e) {
throw getExceptionConverter().convert( e );
}
}
private void internalClear() {
persistenceContext.clear();
actionQueue.clear();
fastSessionServices.eventListenerGroup_CLEAR.fireLazyEventOnEachListener( this::createClearEvent, ClearEventListener::onClear );
}
private ClearEvent createClearEvent() {
return new ClearEvent( this );
}
@Override
@SuppressWarnings("StatementWithEmptyBody")
public void close() throws HibernateException {
if ( isClosed() ) {
if ( getFactory().getSessionFactoryOptions().getJpaCompliance().isJpaClosedComplianceEnabled() ) {
throw new IllegalStateException( "Illegal call to #close() on already closed Session/EntityManager" );
}
log.trace( "Already closed" );
return;
}
closeWithoutOpenChecks();
}
public void closeWithoutOpenChecks() throws HibernateException {
if ( log.isTraceEnabled() ) {
log.tracef( "Closing session [%s]", getSessionIdentifier() );
}
// todo : we want this check if usage is JPA, but not native Hibernate usage
final SessionFactoryImplementor sessionFactory = getSessionFactory();
if ( sessionFactory.getSessionFactoryOptions().isJpaBootstrap() ) {
// Original hibernate-entitymanager EM#close behavior
checkSessionFactoryOpen();
checkOpenOrWaitingForAutoClose();
if ( fastSessionServices.discardOnClose || !isTransactionInProgress( false ) ) {
super.close();
}
else {
//Otherwise, session auto-close will be enabled by shouldAutoCloseSession().
prepareForAutoClose();
}
}
else {
super.close();
}
final StatisticsImplementor statistics = sessionFactory.getStatistics();
if ( statistics.isStatisticsEnabled() ) {
statistics.closeSession();
}
}
private boolean isTransactionInProgress(boolean isMarkedRollbackConsideredActive) {
if ( waitingForAutoClose ) {
return getSessionFactory().isOpen() &&
getTransactionCoordinator().isTransactionActive( isMarkedRollbackConsideredActive );
}
return !isClosed() &&
getTransactionCoordinator().isTransactionActive( isMarkedRollbackConsideredActive );
}
@Override
protected boolean shouldCloseJdbcCoordinatorOnClose(boolean isTransactionCoordinatorShared) {
if ( !isTransactionCoordinatorShared ) {
return super.shouldCloseJdbcCoordinatorOnClose( isTransactionCoordinatorShared );
}
final ActionQueue actionQueue = getActionQueue();
if ( actionQueue.hasBeforeTransactionActions() || actionQueue.hasAfterTransactionActions() ) {
log.warn(
"On close, shared Session had before/after transaction actions that have not yet been processed"
);
}
return false;
}
@Override
public boolean isAutoCloseSessionEnabled() {
return autoClose;
}
@Override
public boolean isQueryParametersValidationEnabled() {
return queryParametersValidationEnabled;
}
@Override
public boolean isOpen() {
checkSessionFactoryOpen();
checkTransactionSynchStatus();
try {
return !isClosed();
}
catch (HibernateException he) {
throw getExceptionConverter().convert( he );
}
}
protected void checkSessionFactoryOpen() {
if ( !getFactory().isOpen() ) {
log.debug( "Forcing Session/EntityManager closed as SessionFactory/EntityManagerFactory has been closed" );
setClosed();
}
}
private void managedFlush() {
if ( isClosed() && !waitingForAutoClose ) {
log.trace( "Skipping auto-flush due to session closed" );
return;
}
log.trace( "Automatically flushing session" );
doFlush();
}
@Override
public boolean shouldAutoClose() {
if ( waitingForAutoClose ) {
return true;
}
else if ( isClosed() ) {
return false;
}
else {
// JPA technically requires that this be a PersistentUnityTransactionType#JTA to work,
// but we do not assert that here...
//return isAutoCloseSessionEnabled() && getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
return isAutoCloseSessionEnabled();
}
}
private void managedClose() {
log.trace( "Automatically closing session" );
closeWithoutOpenChecks();
}
@Override
public Connection connection() throws HibernateException {
checkOpenOrWaitingForAutoClose();
return getJdbcCoordinator().getLogicalConnection().getPhysicalConnection();
}
@Override
public Connection disconnect() throws HibernateException {
checkOpen();
log.debug( "Disconnecting session" );
return getJdbcCoordinator().getLogicalConnection().manualDisconnect();
}
@Override
public void reconnect(Connection conn) throws HibernateException {
checkOpen();
log.debug( "Reconnecting session" );
checkTransactionSynchStatus();
getJdbcCoordinator().getLogicalConnection().manualReconnect( conn );
}
@Override
public void setAutoClear(boolean enabled) {
checkOpenOrWaitingForAutoClose();
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
*
* @param success Was the operation a success
*/
public void afterOperation(boolean success) {
if ( !isTransactionInProgress() ) {
getJdbcCoordinator().afterTransaction();
}
}
@Override
public void addEventListeners(SessionEventListener... listeners) {
getEventListenerManager().addListener( listeners );
}
/**
* clear all the internal collections, just
* to help the garbage collector, does not
* clear anything that is needed during the
* afterTransactionCompletion() phase
*/
@Override
protected void cleanupOnClose() {
persistenceContext.clear();
}
@Override
public LockMode getCurrentLockMode(Object object) throws HibernateException {
checkOpen();
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();
}
@Override
public Object getEntityUsingInterceptor(EntityKey key) throws HibernateException {
checkOpenOrWaitingForAutoClose();
// 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 = getInterceptor().getEntity( key.getEntityName(), key.getIdentifier() );
if ( newObject != null ) {
lock( newObject, LockMode.NONE );
}
return newObject;
}
else {
return result;
}
}
protected void checkNoUnresolvedActionsBeforeOperation() {
if ( persistenceContext.getCascadeLevel() == 0 && actionQueue.hasUnresolvedEntityInsertActions() ) {
throw new IllegalStateException( "There are delayed insert actions before operation as cascade level 0." );
}
}
protected void checkNoUnresolvedActionsAfterOperation() {
if ( persistenceContext.getCascadeLevel() == 0 ) {
actionQueue.checkNoUnresolvedActionsAfterOperation();
}
delayedAfterCompletion();
}
@Override
protected void delayedAfterCompletion() {
if ( getTransactionCoordinator() instanceof JtaTransactionCoordinatorImpl ) {
( (JtaTransactionCoordinatorImpl) getTransactionCoordinator() ).getSynchronizationCallbackCoordinator()
.processAnyDelayedAfterCompletion();
}
}
// saveOrUpdate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void saveOrUpdate(Object object) throws HibernateException {
saveOrUpdate( null, object );
}
@Override
public void saveOrUpdate(String entityName, Object obj) throws HibernateException {
fireSaveOrUpdate( new SaveOrUpdateEvent( entityName, obj, this ) );
}
private void fireSaveOrUpdate(final SaveOrUpdateEvent event) {
checkOpen();
checkTransactionSynchStatus();
checkNoUnresolvedActionsBeforeOperation();
fastSessionServices.eventListenerGroup_SAVE_UPDATE.fireEventOnEachListener( event, SaveOrUpdateEventListener::onSaveOrUpdate );
checkNoUnresolvedActionsAfterOperation();
}
// save() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public Serializable save(Object obj) throws HibernateException {
return save( null, obj );
}
@Override
public Serializable save(String entityName, Object object) throws HibernateException {
return fireSave( new SaveOrUpdateEvent( entityName, object, this ) );
}
private Serializable fireSave(final SaveOrUpdateEvent event) {
checkOpen();
checkTransactionSynchStatus();
checkNoUnresolvedActionsBeforeOperation();
fastSessionServices.eventListenerGroup_SAVE.fireEventOnEachListener( event, SaveOrUpdateEventListener::onSaveOrUpdate );
checkNoUnresolvedActionsAfterOperation();
return event.getResultId();
}
// update() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void update(Object obj) throws HibernateException {
update( null, obj );
}
@Override
public void update(String entityName, Object object) throws HibernateException {
fireUpdate( new SaveOrUpdateEvent( entityName, object, this ) );
}
private void fireUpdate(SaveOrUpdateEvent event) {
checkOpen();
checkTransactionSynchStatus();
checkNoUnresolvedActionsBeforeOperation();
fastSessionServices.eventListenerGroup_UPDATE.fireEventOnEachListener( event, SaveOrUpdateEventListener::onSaveOrUpdate );
checkNoUnresolvedActionsAfterOperation();
}
// lock() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void lock(String entityName, Object object, LockMode lockMode) throws HibernateException {
fireLock( new LockEvent( entityName, object, lockMode, this ) );
}
@Override
public LockRequest buildLockRequest(LockOptions lockOptions) {
return new LockRequestImpl( lockOptions );
}
@Override
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 event) {
checkOpen();
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_LOCK.fireEventOnEachListener( event, LockEventListener::onLock );
delayedAfterCompletion();
}
// persist() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void persist(String entityName, Object object) throws HibernateException {
checkOpen();
firePersist( new PersistEvent( entityName, object, this ) );
}
@Override
public void persist(Object object) throws HibernateException {
checkOpen();
firePersist( new PersistEvent( null, object, this ) );
}
@Override
public void persist(String entityName, Object object, Map copiedAlready) throws HibernateException {
checkOpenOrWaitingForAutoClose();
firePersist( copiedAlready, new PersistEvent( entityName, object, this ) );
}
private void firePersist(final PersistEvent event) {
try {
checkTransactionSynchStatus();
checkNoUnresolvedActionsBeforeOperation();
fastSessionServices.eventListenerGroup_PERSIST.fireEventOnEachListener( event, PersistEventListener::onPersist );
}
catch (MappingException e) {
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage() ) );
}
catch (RuntimeException e) {
throw getExceptionConverter().convert( e );
}
finally {
try {
checkNoUnresolvedActionsAfterOperation();
}
catch (RuntimeException e) {
throw getExceptionConverter().convert( e );
}
}
}
private void firePersist(final Map copiedAlready, final PersistEvent event) {
pulseTransactionCoordinator();
try {
//Uses a capturing lambda in this case as we need to carry the additional Map parameter:
fastSessionServices.eventListenerGroup_PERSIST
.fireEventOnEachListener( event, copiedAlready, PersistEventListener::onPersist );
}
catch ( MappingException e ) {
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage() ) ) ;
}
catch ( RuntimeException e ) {
throw getExceptionConverter().convert( e );
}
finally {
delayedAfterCompletion();
}
}
// persistOnFlush() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void persistOnFlush(String entityName, Object object, Map copiedAlready) {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
PersistEvent event = new PersistEvent( entityName, object, this );
fastSessionServices.eventListenerGroup_PERSIST_ONFLUSH.fireEventOnEachListener( event, copiedAlready, PersistEventListener::onPersist );
delayedAfterCompletion();
}
// merge() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public Object merge(String entityName, Object object) throws HibernateException {
checkOpen();
return fireMerge( new MergeEvent( entityName, object, this ) );
}
@Override
public Object merge(Object object) throws HibernateException {
checkOpen();
return fireMerge( new MergeEvent( null, object, this ));
}
@Override
public void merge(String entityName, Object object, Map copiedAlready) throws HibernateException {
checkOpenOrWaitingForAutoClose();
fireMerge( copiedAlready, new MergeEvent( entityName, object, this ) );
}
private Object fireMerge(MergeEvent event) {
try {
checkTransactionSynchStatus();
checkNoUnresolvedActionsBeforeOperation();
fastSessionServices.eventListenerGroup_MERGE.fireEventOnEachListener( event, MergeEventListener::onMerge );
checkNoUnresolvedActionsAfterOperation();
}
catch ( ObjectDeletedException sse ) {
throw getExceptionConverter().convert( new IllegalArgumentException( sse ) );
}
catch ( MappingException e ) {
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage(), e ) );
}
catch ( RuntimeException e ) {
//including HibernateException
throw getExceptionConverter().convert( e );
}
return event.getResult();
}
private void fireMerge(final Map copiedAlready, final MergeEvent event) {
try {
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_MERGE.fireEventOnEachListener( event, copiedAlready, MergeEventListener::onMerge );
}
catch ( ObjectDeletedException sse ) {
throw getExceptionConverter().convert( new IllegalArgumentException( sse ) );
}
catch ( MappingException e ) {
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage(), e ) );
}
catch ( RuntimeException e ) {
//including HibernateException
throw getExceptionConverter().convert( e );
}
finally {
delayedAfterCompletion();
}
}
// delete() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void delete(Object object) throws HibernateException {
checkOpen();
fireDelete( new DeleteEvent( object, this ) );
}
@Override
public void delete(String entityName, Object object) throws HibernateException {
checkOpen();
fireDelete( new DeleteEvent( entityName, object, this ) );
}
@Override
public void delete(String entityName, Object object, boolean isCascadeDeleteEnabled, Set transientEntities)
throws HibernateException {
checkOpenOrWaitingForAutoClose();
final boolean removingOrphanBeforeUpates = persistenceContext.isRemovingOrphanBeforeUpates();
final boolean traceEnabled = log.isTraceEnabled();
if ( traceEnabled && removingOrphanBeforeUpates ) {
logRemoveOrphanBeforeUpdates( "before continuing", entityName, object );
}
fireDelete(
new DeleteEvent(
entityName,
object,
isCascadeDeleteEnabled,
removingOrphanBeforeUpates,
this
),
transientEntities
);
if ( traceEnabled && removingOrphanBeforeUpates ) {
logRemoveOrphanBeforeUpdates( "after continuing", entityName, object );
}
}
@Override
public void removeOrphanBeforeUpdates(String entityName, Object child) {
// TODO: The removeOrphan concept is a temporary "hack" for HHH-6484. This should be removed once action/task
// ordering is improved.
final boolean traceEnabled = log.isTraceEnabled();
if ( traceEnabled ) {
logRemoveOrphanBeforeUpdates( "begin", entityName, child );
}
persistenceContext.beginRemoveOrphanBeforeUpdates();
try {
checkOpenOrWaitingForAutoClose();
fireDelete( new DeleteEvent( entityName, child, false, true, this ) );
}
finally {
persistenceContext.endRemoveOrphanBeforeUpdates();
if ( traceEnabled ) {
logRemoveOrphanBeforeUpdates( "end", entityName, child );
}
}
}
private void logRemoveOrphanBeforeUpdates(String timing, String entityName, Object entity) {
if ( log.isTraceEnabled() ) {
final EntityEntry entityEntry = persistenceContext.getEntry( entity );
log.tracef(
"%s remove orphan before updates: [%s]",
timing,
entityEntry == null ? entityName : MessageHelper.infoString( entityName, entityEntry.getId() )
);
}
}
private void fireDelete(final DeleteEvent event) {
try{
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_DELETE.fireEventOnEachListener( event, DeleteEventListener::onDelete );
}
catch ( ObjectDeletedException sse ) {
throw getExceptionConverter().convert( new IllegalArgumentException( sse ) );
}
catch ( MappingException e ) {
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage(), e ) );
}
catch ( RuntimeException e ) {
//including HibernateException
throw getExceptionConverter().convert( e );
}
finally {
delayedAfterCompletion();
}
}
private void fireDelete(final DeleteEvent event, final Set transientEntities) {
try{
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_DELETE.fireEventOnEachListener( event, transientEntities, DeleteEventListener::onDelete );
}
catch ( ObjectDeletedException sse ) {
throw getExceptionConverter().convert( new IllegalArgumentException( sse ) );
}
catch ( MappingException e ) {
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage(), e ) );
}
catch ( RuntimeException e ) {
//including HibernateException
throw getExceptionConverter().convert( e );
}
finally {
delayedAfterCompletion();
}
}
// load()/get() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void load(Object object, Serializable id) throws HibernateException {
LoadEvent event = loadEvent;
loadEvent = null;
if ( event == null ) {
event = new LoadEvent( id, object, this, getReadOnlyFromLoadQueryInfluencers() );
}
else {
event.setEntityClassName( null );
event.setEntityId( id );
event.setInstanceToLoad( object );
event.setLockMode( LoadEvent.DEFAULT_LOCK_MODE );
event.setLockScope( LoadEvent.DEFAULT_LOCK_OPTIONS.getScope() );
event.setLockTimeout( LoadEvent.DEFAULT_LOCK_OPTIONS.getTimeOut() );
}
fireLoad( event, LoadEventListener.RELOAD );
if ( loadEvent == null ) {
event.setEntityClassName( null );
event.setEntityId( null );
event.setInstanceToLoad( null );
event.setResult( null );
loadEvent = event;
}
}
@Override
public T load(Class entityClass, Serializable id) throws HibernateException {
return this.byId( entityClass ).getReference( id );
}
@Override
public Object load(String entityName, Serializable id) throws HibernateException {
return this.byId( entityName ).getReference( id );
}
@Override
public T get(Class entityClass, Serializable id) throws HibernateException {
return this.byId( entityClass ).load( id );
}
@Override
public Object get(String entityName, Serializable id) throws HibernateException {
return this.byId( entityName ).load( id );
}
/**
* 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.
*/
@Override
public Object immediateLoad(String entityName, Serializable id) throws HibernateException {
if ( log.isDebugEnabled() ) {
EntityPersister persister = getFactory().getMetamodel().entityPersister( entityName );
log.debugf( "Initializing proxy: %s", MessageHelper.infoString( persister, id, getFactory() ) );
}
LoadEvent event = loadEvent;
loadEvent = null;
event = recycleEventInstance( event, id, entityName );
fireLoadNoChecks( event, LoadEventListener.IMMEDIATE_LOAD );
Object result = event.getResult();
if ( loadEvent == null ) {
event.setEntityClassName( null );
event.setEntityId( null );
event.setInstanceToLoad( null );
event.setResult( null );
loadEvent = event;
}
return result;
}
@Override
public Object internalLoad(
String entityName,
Serializable id,
boolean eager,
boolean nullable) {
final EffectiveEntityGraph effectiveEntityGraph = getLoadQueryInfluencers().getEffectiveEntityGraph();
final GraphSemantic semantic = effectiveEntityGraph.getSemantic();
final RootGraphImplementor> graph = effectiveEntityGraph.getGraph();
boolean clearedEffectiveGraph = false;
if ( semantic != null ) {
if ( ! graph.appliesTo( entityName ) ) {
log.debug( "Clearing effective entity graph for subsequent-select" );
clearedEffectiveGraph = true;
effectiveEntityGraph.clear();
}
}
try {
final LoadEventListener.LoadType type;
if ( nullable ) {
type = LoadEventListener.INTERNAL_LOAD_NULLABLE;
}
else {
type = eager
? LoadEventListener.INTERNAL_LOAD_EAGER
: LoadEventListener.INTERNAL_LOAD_LAZY;
}
LoadEvent event = loadEvent;
loadEvent = null;
event = recycleEventInstance( event, id, entityName );
fireLoadNoChecks( event, type );
Object result = event.getResult();
if ( !nullable ) {
UnresolvableObjectException.throwIfNull( result, id, entityName );
}
if ( loadEvent == null ) {
event.setEntityClassName( null );
event.setEntityId( null );
event.setInstanceToLoad( null );
event.setResult( null );
loadEvent = event;
}
return result;
}
finally {
if ( clearedEffectiveGraph ) {
effectiveEntityGraph.applyGraph( graph, semantic );
}
}
}
/**
* Helper to avoid creating many new instances of LoadEvent: it's an allocation hot spot.
*/
private LoadEvent recycleEventInstance(final LoadEvent event, final Serializable id, final String entityName) {
if ( event == null ) {
return new LoadEvent( id, entityName, true, this, getReadOnlyFromLoadQueryInfluencers() );
}
else {
event.setEntityClassName( entityName );
event.setEntityId( id );
event.setInstanceToLoad( null );
event.setLockMode( LoadEvent.DEFAULT_LOCK_MODE );
event.setLockScope( LoadEvent.DEFAULT_LOCK_OPTIONS.getScope() );
event.setLockTimeout( LoadEvent.DEFAULT_LOCK_OPTIONS.getTimeOut() );
return event;
}
}
@Override
public T load(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException {
return this.byId( entityClass ).with( new LockOptions( lockMode ) ).getReference( id );
}
@Override
public T load(Class entityClass, Serializable id, LockOptions lockOptions) throws HibernateException {
return this.byId( entityClass ).with( lockOptions ).getReference( id );
}
@Override
public Object load(String entityName, Serializable id, LockMode lockMode) throws HibernateException {
return this.byId( entityName ).with( new LockOptions( lockMode ) ).getReference( id );
}
@Override
public Object load(String entityName, Serializable id, LockOptions lockOptions) throws HibernateException {
return this.byId( entityName ).with( lockOptions ).getReference( id );
}
@Override
public T get(Class entityClass, Serializable id, LockMode lockMode) throws HibernateException {
return this.byId( entityClass ).with( new LockOptions( lockMode ) ).load( id );
}
@Override
public T get(Class entityClass, Serializable id, LockOptions lockOptions) throws HibernateException {
return this.byId( entityClass ).with( lockOptions ).load( id );
}
@Override
public Object get(String entityName, Serializable id, LockMode lockMode) throws HibernateException {
return this.byId( entityName ).with( new LockOptions( lockMode ) ).load( id );
}
@Override
public Object get(String entityName, Serializable id, LockOptions lockOptions) throws HibernateException {
return this.byId( entityName ).with( lockOptions ).load( id );
}
@Override
public IdentifierLoadAccessImpl byId(String entityName) {
return new IdentifierLoadAccessImpl( entityName );
}
@Override
public IdentifierLoadAccessImpl byId(Class entityClass) {
return new IdentifierLoadAccessImpl( entityClass );
}
@Override
public MultiIdentifierLoadAccess byMultipleIds(Class entityClass) {
return new MultiIdentifierLoadAccessImpl( locateEntityPersister( entityClass ) );
}
@Override
public MultiIdentifierLoadAccess byMultipleIds(String entityName) {
return new MultiIdentifierLoadAccessImpl( locateEntityPersister( entityName ) );
}
@Override
public NaturalIdLoadAccess byNaturalId(String entityName) {
return new NaturalIdLoadAccessImpl( entityName );
}
@Override
public NaturalIdLoadAccess byNaturalId(Class entityClass) {
return new NaturalIdLoadAccessImpl( entityClass );
}
@Override
public SimpleNaturalIdLoadAccess bySimpleNaturalId(String entityName) {
return new SimpleNaturalIdLoadAccessImpl( entityName );
}
@Override
public SimpleNaturalIdLoadAccess bySimpleNaturalId(Class entityClass) {
return new SimpleNaturalIdLoadAccessImpl( entityClass );
}
private void fireLoad(LoadEvent event, LoadType loadType) {
checkOpenOrWaitingForAutoClose();
fireLoadNoChecks( event, loadType );
delayedAfterCompletion();
}
//Performance note:
// This version of #fireLoad is meant to be invoked by internal methods only,
// so to skip the session open, transaction synch, etc.. checks,
// which have been proven to be not particularly cheap:
// it seems they prevent these hot methods from being inlined.
private void fireLoadNoChecks(final LoadEvent event, final LoadType loadType) {
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_LOAD.fireEventOnEachListener( event, loadType, LoadEventListener::onLoad );
}
private void fireResolveNaturalId(final ResolveNaturalIdEvent event) {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_RESOLVE_NATURAL_ID.fireEventOnEachListener( event, ResolveNaturalIdEventListener::onResolveNaturalId );
delayedAfterCompletion();
}
// refresh() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void refresh(Object object) throws HibernateException {
checkOpen();
fireRefresh( new RefreshEvent( null, object, this ) );
}
@Override
public void refresh(String entityName, Object object) throws HibernateException {
checkOpen();
fireRefresh( new RefreshEvent( entityName, object, this ) );
}
@Override
public void refresh(Object object, LockMode lockMode) throws HibernateException {
checkOpen();
fireRefresh( new RefreshEvent( object, lockMode, this ) );
}
@Override
public void refresh(Object object, LockOptions lockOptions) throws HibernateException {
checkOpen();
refresh( null, object, lockOptions );
}
@Override
public void refresh(String entityName, Object object, LockOptions lockOptions) throws HibernateException {
checkOpen();
fireRefresh( new RefreshEvent( entityName, object, lockOptions, this ) );
}
@Override
public void refresh(String entityName, Object object, Map refreshedAlready) throws HibernateException {
checkOpenOrWaitingForAutoClose();
fireRefresh( refreshedAlready, new RefreshEvent( entityName, object, this ) );
}
private void fireRefresh(final RefreshEvent event) {
try {
if ( !getSessionFactory().getSessionFactoryOptions().isAllowRefreshDetachedEntity() ) {
if ( event.getEntityName() != null ) {
if ( !contains( event.getEntityName(), event.getObject() ) ) {
throw new IllegalArgumentException( "Entity not managed" );
}
}
else {
if ( !contains( event.getObject() ) ) {
throw new IllegalArgumentException( "Entity not managed" );
}
}
}
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_REFRESH.fireEventOnEachListener( event, RefreshEventListener::onRefresh );
}
catch (RuntimeException e) {
if ( !getSessionFactory().getSessionFactoryOptions().isJpaBootstrap() ) {
if ( e instanceof HibernateException ) {
throw e;
}
}
//including HibernateException
throw getExceptionConverter().convert( e );
}
finally {
delayedAfterCompletion();
}
}
private void fireRefresh(final Map refreshedAlready, final RefreshEvent event) {
try {
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_REFRESH.fireEventOnEachListener( event, refreshedAlready, RefreshEventListener::onRefresh );
}
catch (RuntimeException e) {
throw getExceptionConverter().convert( e );
}
finally {
delayedAfterCompletion();
}
}
// replicate() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void replicate(Object obj, ReplicationMode replicationMode) throws HibernateException {
fireReplicate( new ReplicateEvent( obj, replicationMode, this ) );
}
@Override
public void replicate(String entityName, Object obj, ReplicationMode replicationMode)
throws HibernateException {
fireReplicate( new ReplicateEvent( entityName, obj, replicationMode, this ) );
}
private void fireReplicate(final ReplicateEvent event) {
checkOpen();
pulseTransactionCoordinator();
fastSessionServices.eventListenerGroup_REPLICATE.fireEventOnEachListener( event, ReplicateEventListener::onReplicate );
delayedAfterCompletion();
}
// evict() operations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* remove any hard references to the entity that are held by the infrastructure
* (references held by application or other persistent instances are okay)
*/
@Override
public void evict(Object object) throws HibernateException {
checkOpen();
pulseTransactionCoordinator();
final EvictEvent event = new EvictEvent( object, this );
fastSessionServices.eventListenerGroup_EVICT.fireEventOnEachListener( event, EvictEventListener::onEvict );
delayedAfterCompletion();
}
/**
* 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 {
checkOpen();
if ( !isTransactionInProgress() ) {
// do not auto-flush while outside a transaction
return false;
}
AutoFlushEvent event = new AutoFlushEvent( querySpaces, this );
fastSessionServices.eventListenerGroup_AUTO_FLUSH.fireEventOnEachListener( event, AutoFlushEventListener::onAutoFlush );
return event.isFlushRequired();
}
@Override
public boolean isDirty() throws HibernateException {
checkOpen();
pulseTransactionCoordinator();
log.debug( "Checking session dirtiness" );
if ( actionQueue.areInsertionsOrDeletionsQueued() ) {
log.debug( "Session dirty (scheduled updates and insertions)" );
return true;
}
DirtyCheckEvent event = new DirtyCheckEvent( this );
fastSessionServices.eventListenerGroup_DIRTY_CHECK.fireEventOnEachListener( event, DirtyCheckEventListener::onDirtyCheck );
delayedAfterCompletion();
return event.isDirty();
}
@Override
public void flush() throws HibernateException {
checkOpen();
doFlush();
}
private void doFlush() {
pulseTransactionCoordinator();
checkTransactionNeededForUpdateOperation();
try {
if ( persistenceContext.getCascadeLevel() > 0 ) {
throw new HibernateException( "Flush during cascade is dangerous" );
}
FlushEvent event = new FlushEvent( this );
fastSessionServices.eventListenerGroup_FLUSH.fireEventOnEachListener( event, FlushEventListener::onFlush );
delayedAfterCompletion();
}
catch ( RuntimeException e ) {
throw getExceptionConverter().convert( e );
}
}
@Override
public void setFlushMode(FlushModeType flushModeType) {
checkOpen();
setHibernateFlushMode( FlushModeTypeHelper.getFlushMode( flushModeType ) );
}
@Override
public void forceFlush(EntityEntry entityEntry) throws HibernateException {
if ( log.isDebugEnabled() ) {
log.debugf(
"Flushing to force deletion of re-saved object: %s",
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()
);
}
checkOpenOrWaitingForAutoClose();
doFlush();
}
@Override
public List list(String query, QueryParameters queryParameters) throws HibernateException {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
queryParameters.validateParameters();
HQLQueryPlan plan = queryParameters.getQueryPlan();
if ( plan == null ) {
plan = getQueryPlan( query, false );
}
autoFlushIfRequired( plan.getQuerySpaces() );
final List results;
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 );
delayedAfterCompletion();
}
return results;
}
@Override
public int executeUpdate(String query, QueryParameters queryParameters) throws HibernateException {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
queryParameters.validateParameters();
HQLQueryPlan plan = getQueryPlan( query, false );
autoFlushIfRequired( plan.getQuerySpaces() );
verifyImmutableEntityUpdate( plan );
boolean success = false;
int result = 0;
try {
result = plan.performExecuteUpdate( queryParameters, this );
success = true;
}
finally {
afterOperation( success );
delayedAfterCompletion();
}
return result;
}
protected void verifyImmutableEntityUpdate(HQLQueryPlan plan) {
if ( plan.isUpdate() ) {
List primaryFromClauseTables = new ArrayList<>();
for ( QueryTranslator queryTranslator : plan.getTranslators() ) {
primaryFromClauseTables.addAll( queryTranslator.getPrimaryFromClauseTables() );
}
for ( EntityPersister entityPersister : getSessionFactory().getMetamodel().entityPersisters().values() ) {
if ( !entityPersister.isMutable() ) {
List entityQuerySpaces = new ArrayList<>(
Arrays.asList( entityPersister.getQuerySpaces() )
);
boolean matching = false;
for ( Serializable entityQuerySpace : entityQuerySpaces ) {
if ( primaryFromClauseTables.contains( entityQuerySpace ) ) {
matching = true;
break;
}
}
if ( matching ) {
ImmutableEntityUpdateQueryHandlingMode immutableEntityUpdateQueryHandlingMode = getSessionFactory()
.getSessionFactoryOptions()
.getImmutableEntityUpdateQueryHandlingMode();
String querySpaces = Arrays.toString( entityQuerySpaces.toArray() );
switch ( immutableEntityUpdateQueryHandlingMode ) {
case WARNING:
log.immutableEntityUpdateQuery( plan.getSourceQuery(), querySpaces );
break;
case EXCEPTION:
throw new HibernateException(
"The query: [" + plan.getSourceQuery() + "] attempts to update an immutable entity: " + querySpaces
);
default:
throw new UnsupportedOperationException(
"The " + immutableEntityUpdateQueryHandlingMode + " is not supported!"
);
}
}
}
}
}
}
@Override
public int executeNativeUpdate(
NativeSQLQuerySpecification nativeQuerySpecification,
QueryParameters queryParameters) throws HibernateException {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
queryParameters.validateParameters();
NativeSQLQueryPlan plan = getNativeQueryPlan( nativeQuerySpecification );
autoFlushIfRequired( plan.getCustomQuery().getQuerySpaces() );
boolean success = false;
int result = 0;
try {
result = plan.performExecuteUpdate( queryParameters, this );
success = true;
}
finally {
afterOperation( success );
delayedAfterCompletion();
}
return result;
}
@Override
public Iterator iterate(String query, QueryParameters queryParameters) throws HibernateException {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
queryParameters.validateParameters();
HQLQueryPlan plan = queryParameters.getQueryPlan();
if ( plan == null || !plan.isShallow() ) {
plan = getQueryPlan( 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 {
delayedAfterCompletion();
dontFlushFromFind--;
}
}
@Override
public ScrollableResultsImplementor scroll(String query, QueryParameters queryParameters) throws HibernateException {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
HQLQueryPlan plan = queryParameters.getQueryPlan();
if ( plan == null ) {
plan = getQueryPlan( query, false );
}
autoFlushIfRequired( plan.getQuerySpaces() );
dontFlushFromFind++;
try {
return plan.performScroll( queryParameters, this );
}
finally {
delayedAfterCompletion();
dontFlushFromFind--;
}
}
@Override
public org.hibernate.query.Query createFilter(Object collection, String queryString) {
checkOpen();
pulseTransactionCoordinator();
CollectionFilterImpl filter = new CollectionFilterImpl(
queryString,
collection,
this,
getFilterQueryPlan( collection, queryString, null, false ).getParameterMetadata()
);
filter.setComment( queryString );
delayedAfterCompletion();
return filter;
}
@Override
public Object instantiate(String entityName, Serializable id) throws HibernateException {
return instantiate( getFactory().getMetamodel().entityPersister( entityName ), id );
}
/**
* give the interceptor an opportunity to override the default instantiation
*/
@Override
public Object instantiate(EntityPersister persister, Serializable id) throws HibernateException {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
Object result = getInterceptor().instantiate(
persister.getEntityName(),
persister.getEntityMetamodel().getEntityMode(),
id
);
if ( result == null ) {
result = persister.instantiate( id, this );
}
delayedAfterCompletion();
return result;
}
@Override
public EntityPersister getEntityPersister(final String entityName, final Object object) {
checkOpenOrWaitingForAutoClose();
if ( entityName == null ) {
return getFactory().getMetamodel().entityPersister( 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 getFactory().getMetamodel().entityPersister( entityName ).getSubclassEntityPersister( object, getFactory() );
}
catch (HibernateException e) {
try {
return getEntityPersister( null, object );
}
catch (HibernateException e2) {
throw e;
}
}
}
}
// not for internal use:
@Override
public Serializable getIdentifier(Object object) throws HibernateException {
checkOpen();
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.getInternalIdentifier();
}
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().
*/
@Override
public Serializable getContextEntityIdentifier(Object object) {
checkOpenOrWaitingForAutoClose();
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().getInternalIdentifier();
}
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;
final Map enabledFilters = getLoadQueryInfluencers().getEnabledFilters();
final SessionFactoryImplementor factory = getFactory();
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,
enabledFilters
);
}
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,
enabledFilters
);
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,
enabledFilters
);
}
}
}
if ( parameters != null ) {
parameters.getNamedParameters().put(
CollectionFilterKeyParameterSpecification.PARAM_KEY,
new TypedValue(
entry.getLoadedPersister().getKeyType(),
entry.getLoadedKey()
)
);
}
return plan;
}
@Override
public List listFilter(Object collection, String filter, QueryParameters queryParameters) {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
FilterQueryPlan plan = getFilterQueryPlan( collection, filter, queryParameters, false );
List results = Collections.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 );
delayedAfterCompletion();
}
return results;
}
@Override
public Iterator iterateFilter(Object collection, String filter, QueryParameters queryParameters) {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
FilterQueryPlan plan = getFilterQueryPlan( collection, filter, queryParameters, true );
Iterator itr = plan.performIterate( queryParameters, this );
delayedAfterCompletion();
return itr;
}
@Override
public Criteria createCriteria(Class persistentClass, String alias) {
DeprecationLogger.DEPRECATION_LOGGER.deprecatedLegacyCriteria();
checkOpen();
checkTransactionSynchStatus();
return new CriteriaImpl( persistentClass.getName(), alias, this );
}
@Override
public Criteria createCriteria(String entityName, String alias) {
DeprecationLogger.DEPRECATION_LOGGER.deprecatedLegacyCriteria();
checkOpen();
checkTransactionSynchStatus();
return new CriteriaImpl( entityName, alias, this );
}
@Override
public Criteria createCriteria(Class persistentClass) {
DeprecationLogger.DEPRECATION_LOGGER.deprecatedLegacyCriteria();
checkOpen();
checkTransactionSynchStatus();
return new CriteriaImpl( persistentClass.getName(), this );
}
@Override
public Criteria createCriteria(String entityName) {
DeprecationLogger.DEPRECATION_LOGGER.deprecatedLegacyCriteria();
checkOpen();
checkTransactionSynchStatus();
return new CriteriaImpl( entityName, this );
}
@Override
public ScrollableResultsImplementor scroll(Criteria criteria, ScrollMode scrollMode) {
// TODO: Is this guaranteed to always be CriteriaImpl?
CriteriaImpl criteriaImpl = (CriteriaImpl) criteria;
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
String entityName = criteriaImpl.getEntityOrClassName();
CriteriaLoader loader = new CriteriaLoader(
getOuterJoinLoadable( entityName ),
getFactory(),
criteriaImpl,
entityName,
getLoadQueryInfluencers()
);
autoFlushIfRequired( loader.getQuerySpaces() );
dontFlushFromFind++;
try {
return loader.scroll( this, scrollMode );
}
finally {
delayedAfterCompletion();
dontFlushFromFind--;
}
}
@Override
public List list(Criteria criteria) throws HibernateException {
// TODO: Is this guaranteed to always be CriteriaImpl?
CriteriaImpl criteriaImpl = (CriteriaImpl) criteria;
if ( criteriaImpl.getMaxResults() != null && criteriaImpl.getMaxResults() == 0 ) {
return Collections.EMPTY_LIST;
}
final NaturalIdLoadAccess naturalIdLoadAccess = this.tryNaturalIdLoadAccess( criteriaImpl );
if ( naturalIdLoadAccess != null ) {
// EARLY EXIT!
return Arrays.asList( naturalIdLoadAccess.load() );
}
checkOpenOrWaitingForAutoClose();
// checkTransactionSynchStatus();
String[] implementors = getFactory().getMetamodel().getImplementors( criteriaImpl.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] ),
getFactory(),
criteriaImpl,
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 );
delayedAfterCompletion();
}
return results;
}
/**
* Checks to see if the CriteriaImpl is a naturalId lookup that can be done via
* NaturalIdLoadAccess
*
* @param criteria The criteria to check as a complete natural identifier lookup.
*
* @return A fully configured NaturalIdLoadAccess or null, if null is returned the standard CriteriaImpl execution
* should be performed
*/
private NaturalIdLoadAccess tryNaturalIdLoadAccess(CriteriaImpl criteria) {
// See if the criteria lookup is by naturalId
if ( !criteria.isLookupByNaturalKey() ) {
return null;
}
final String entityName = criteria.getEntityOrClassName();
final EntityPersister entityPersister = getFactory().getMetamodel().entityPersister( entityName );
// Verify the entity actually has a natural id, needed for legacy support as NaturalIdentifier criteria
// queries did no natural id validation
if ( !entityPersister.hasNaturalIdentifier() ) {
return null;
}
// Since isLookupByNaturalKey is true there can be only one CriterionEntry and getCriterion() will
// return an instance of NaturalIdentifier
final CriterionEntry criterionEntry = criteria.iterateExpressionEntries().next();
final NaturalIdentifier naturalIdentifier = (NaturalIdentifier) criterionEntry.getCriterion();
final Map naturalIdValues = naturalIdentifier.getNaturalIdValues();
final int[] naturalIdentifierProperties = entityPersister.getNaturalIdentifierProperties();
// Verify the NaturalIdentifier criterion includes all naturalId properties, first check that the property counts match
if ( naturalIdentifierProperties.length != naturalIdValues.size() ) {
return null;
}
final String[] propertyNames = entityPersister.getPropertyNames();
final NaturalIdLoadAccess naturalIdLoader = this.byNaturalId( entityName );
// Build NaturalIdLoadAccess and in the process verify all naturalId properties were specified
for ( int naturalIdentifierProperty : naturalIdentifierProperties ) {
final String naturalIdProperty = propertyNames[naturalIdentifierProperty];
final Object naturalIdValue = naturalIdValues.get( naturalIdProperty );
if ( naturalIdValue == null ) {
// A NaturalId property is missing from the criteria query, can't use NaturalIdLoadAccess
return null;
}
naturalIdLoader.using( naturalIdProperty, naturalIdValue );
}
// Criteria query contains a valid naturalId, use the new API
log.warn(
"Session.byNaturalId(" + entityName
+ ") should be used for naturalId queries instead of Restrictions.naturalId() from a Criteria"
);
return naturalIdLoader;
}
private OuterJoinLoadable getOuterJoinLoadable(String entityName) throws MappingException {
EntityPersister persister = getFactory().getMetamodel().entityPersister( entityName );
if ( !( persister instanceof OuterJoinLoadable ) ) {
throw new MappingException( "class persister is not OuterJoinLoadable: " + entityName );
}
return (OuterJoinLoadable) persister;
}
@Override
public boolean contains(Object object) {
checkOpen();
pulseTransactionCoordinator();
if ( object == null ) {
return false;
}
try {
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 );
delayedAfterCompletion();
if ( entry == null ) {
if ( !HibernateProxy.class.isInstance( object ) && persistenceContext.getEntry( object ) == null ) {
// check if it is even an entity -> if not throw an exception (per JPA)
try {
final String entityName = getEntityNameResolver().resolveEntityName( object );
if ( entityName == null ) {
throw new IllegalArgumentException( "Could not resolve entity-name [" + object + "]" );
}
getSessionFactory().getMetamodel().entityPersister( entityName );
}
catch (HibernateException e) {
throw new IllegalArgumentException( "Not an entity [" + object.getClass() + "]", e );
}
}
return false;
}
else {
return entry.getStatus() != Status.DELETED && entry.getStatus() != Status.GONE;
}
}
catch (MappingException e) {
throw new IllegalArgumentException( e.getMessage(), e );
}
catch (RuntimeException e) {
throw getExceptionConverter().convert( e );
}
}
@Override
public boolean contains(String entityName, Object object) {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
if ( object == null ) {
return false;
}
try {
if ( !HibernateProxy.class.isInstance( object ) && persistenceContext.getEntry( object ) == null ) {
// check if it is an entity -> if not throw an exception (per JPA)
try {
getSessionFactory().getMetamodel().entityPersister( entityName );
}
catch (HibernateException e) {
throw new IllegalArgumentException( "Not an entity [" + entityName + "] : " + object );
}
}
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 );
delayedAfterCompletion();
return entry != null && entry.getStatus() != Status.DELETED && entry.getStatus() != Status.GONE;
}
catch (MappingException e) {
throw new IllegalArgumentException( e.getMessage(), e );
}
catch (RuntimeException e) {
throw getExceptionConverter().convert( e );
}
}
@Override
public ProcedureCall createStoredProcedureCall(String procedureName) {
checkOpen();
// checkTransactionSynchStatus();
return super.createStoredProcedureCall( procedureName );
}
@Override
public ProcedureCall createStoredProcedureCall(String procedureName, String... resultSetMappings) {
checkOpen();
// checkTransactionSynchStatus();
return super.createStoredProcedureCall( procedureName, resultSetMappings );
}
@Override
public ProcedureCall createStoredProcedureCall(String procedureName, Class... resultClasses) {
checkOpen();
// checkTransactionSynchStatus();
return super.createStoredProcedureCall( procedureName, resultClasses );
}
@Override
public ScrollableResultsImplementor scrollCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) {
checkOpenOrWaitingForAutoClose();
// checkTransactionSynchStatus();
if ( log.isTraceEnabled() ) {
log.tracev( "Scroll SQL query: {0}", customQuery.getSQL() );
}
CustomLoader loader = getFactory().getQueryPlanCache().getNativeQueryInterpreter().createCustomLoader( 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 {
delayedAfterCompletion();
dontFlushFromFind--;
}
}
// basically just an adapted copy of find(CriteriaImpl)
@Override
public List listCustomQuery(CustomQuery customQuery, QueryParameters queryParameters) {
checkOpenOrWaitingForAutoClose();
// checkTransactionSynchStatus();
if ( log.isTraceEnabled() ) {
log.tracev( "SQL query: {0}", customQuery.getSQL() );
}
CustomLoader loader = getFactory().getQueryPlanCache().getNativeQueryInterpreter().createCustomLoader( customQuery, getFactory() );
autoFlushIfRequired( loader.getQuerySpaces() );
dontFlushFromFind++;
boolean success = false;
try {
List results = loader.list( this, queryParameters );
success = true;
return results;
}
finally {
dontFlushFromFind--;
delayedAfterCompletion();
afterOperation( success );
}
}
@Override
public SessionFactoryImplementor getSessionFactory() {
// checkTransactionSynchStatus();
return getFactory();
}
@Override
public void initializeCollection(PersistentCollection collection, boolean writing) {
checkOpenOrWaitingForAutoClose();
pulseTransactionCoordinator();
InitializeCollectionEvent event = new InitializeCollectionEvent( collection, this );
fastSessionServices.eventListenerGroup_INIT_COLLECTION.fireEventOnEachListener( event, InitializeCollectionEventListener::onInitializeCollection );
delayedAfterCompletion();
}
@Override
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 accidentally 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();
}
}
@Override
public String getEntityName(Object object) {
checkOpen();
// 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 )
);
}
@Override
public String guessEntityName(Object object) throws HibernateException {
checkOpenOrWaitingForAutoClose();
return getEntityNameResolver().resolveEntityName( object );
}
@Override
public void cancelQuery() throws HibernateException {
checkOpen();
getJdbcCoordinator().cancelLastQuery();
}
@Override
public int getDontFlushFromFind() {
return dontFlushFromFind;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder( 500 )
.append( "SessionImpl(" ).append( System.identityHashCode( this ) );
if ( !isClosed() ) {
if ( log.isTraceEnabled() ) {
buf.append( persistenceContext )
.append( ";" )
.append( actionQueue );
}
else {
buf.append( "" );
}
}
else {
buf.append( "" );
}
return buf.append( ')' ).toString();
}
@Override
public ActionQueue getActionQueue() {
checkOpenOrWaitingForAutoClose();
// checkTransactionSynchStatus();
return actionQueue;
}
@Override
public PersistenceContext getPersistenceContext() {
checkOpenOrWaitingForAutoClose();
// checkTransactionSynchStatus();
return persistenceContext;
}
@Override
public PersistenceContext getPersistenceContextInternal() {
return persistenceContext;
}
@Override
public SessionStatistics getStatistics() {
pulseTransactionCoordinator();
return new SessionStatisticsImpl( this );
}
@Override
public boolean isEventSource() {
pulseTransactionCoordinator();
return true;
}
@Override
public boolean isDefaultReadOnly() {
return persistenceContext.isDefaultReadOnly();
}
@Override
public void setDefaultReadOnly(boolean defaultReadOnly) {
persistenceContext.setDefaultReadOnly( defaultReadOnly );
}
@Override
public boolean isReadOnly(Object entityOrProxy) {
checkOpen();
// checkTransactionSynchStatus();
return persistenceContext.isReadOnly( entityOrProxy );
}
@Override
public void setReadOnly(Object entity, boolean readOnly) {
checkOpen();
// checkTransactionSynchStatus();
persistenceContext.setReadOnly( entity, readOnly );
}
@Override
public void afterScrollOperation() {
// nothing to do in a stateful session
}
@Override
public LoadQueryInfluencers getLoadQueryInfluencers() {
return loadQueryInfluencers;
}
// filter support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public Filter getEnabledFilter(String filterName) {
pulseTransactionCoordinator();
return loadQueryInfluencers.getEnabledFilter( filterName );
}
@Override
public Filter enableFilter(String filterName) {
checkOpen();
pulseTransactionCoordinator();
return loadQueryInfluencers.enableFilter( filterName );
}
@Override
public void disableFilter(String filterName) {
checkOpen();
pulseTransactionCoordinator();
loadQueryInfluencers.disableFilter( filterName );
}
// fetch profile support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean isFetchProfileEnabled(String name) throws UnknownProfileException {
return loadQueryInfluencers.isFetchProfileEnabled( name );
}
@Override
public void enableFetchProfile(String name) throws UnknownProfileException {
loadQueryInfluencers.enableFetchProfile( name );
}
@Override
public void disableFetchProfile(String name) throws UnknownProfileException {
loadQueryInfluencers.disableFetchProfile( name );
}
@Override
public TypeHelper getTypeHelper() {
return getSessionFactory().getTypeHelper();
}
@Override
public LobHelper getLobHelper() {
if ( lobHelper == null ) {
lobHelper = new LobHelperImpl( this );
}
return lobHelper;
}
private transient LobHelperImpl lobHelper;
private Transaction getTransactionIfAccessible() {
// We do not want an exception to be thrown if the transaction
// is not accessible. If the transaction is not accessible,
// then return null.
return fastSessionServices.isJtaTransactionAccessible ? accessTransaction() : null;
}
@Override
public void beforeTransactionCompletion() {
log.trace( "SessionImpl#beforeTransactionCompletion()" );
flushBeforeTransactionCompletion();
actionQueue.beforeTransactionCompletion();
try {
getInterceptor().beforeTransactionCompletion( getTransactionIfAccessible() );
}
catch (Throwable t) {
log.exceptionInBeforeTransactionCompletionInterceptor( t );
}
super.beforeTransactionCompletion();
}
@Override
public void afterTransactionCompletion(boolean successful, boolean delayed) {
if ( log.isTraceEnabled() ) {
log.tracef( "SessionImpl#afterTransactionCompletion(successful=%s, delayed=%s)", successful, delayed );
}
if ( !isClosed() || waitingForAutoClose ) {
if ( autoClear ||!successful ) {
internalClear();
}
}
persistenceContext.afterTransactionCompletion();
actionQueue.afterTransactionCompletion( successful );
getEventListenerManager().transactionCompletion( successful );
final StatisticsImplementor statistics = getFactory().getStatistics();
if ( statistics.isStatisticsEnabled() ) {
statistics.endTransaction( successful );
}
try {
getInterceptor().afterTransactionCompletion( getTransactionIfAccessible() );
}
catch (Throwable t) {
log.exceptionInAfterTransactionCompletionInterceptor( t );
}
if ( !delayed ) {
if ( shouldAutoClose() && (!isClosed() || waitingForAutoClose) ) {
managedClose();
}
}
super.afterTransactionCompletion( successful, delayed );
}
private static class LobHelperImpl implements LobHelper {
private final SessionImpl session;
private LobHelperImpl(SessionImpl session) {
this.session = session;
}
@Override
public Blob createBlob(byte[] bytes) {
return lobCreator().createBlob( bytes );
}
private LobCreator lobCreator() {
// Always use NonContextualLobCreator. If ContextualLobCreator is
// used both here and in WrapperOptions,
return NonContextualLobCreator.INSTANCE;
}
@Override
public Blob createBlob(InputStream stream, long length) {
return lobCreator().createBlob( stream, length );
}
@Override
public Clob createClob(String string) {
return lobCreator().createClob( string );
}
@Override
public Clob createClob(Reader reader, long length) {
return lobCreator().createClob( reader, length );
}
@Override
public NClob createNClob(String string) {
return lobCreator().createNClob( string );
}
@Override
public NClob createNClob(Reader reader, long length) {
return lobCreator().createNClob( reader, length );
}
}
private static class SharedSessionBuilderImpl
extends SessionFactoryImpl.SessionBuilderImpl
implements SharedSessionBuilder, SharedSessionCreationOptions {
private final SessionImpl session;
private boolean shareTransactionContext;
private SharedSessionBuilderImpl(SessionImpl session) {
super( (SessionFactoryImpl) session.getFactory() );
this.session = session;
super.tenantIdentifier( session.getTenantIdentifier() );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SharedSessionBuilder
@Override
public T tenantIdentifier(String tenantIdentifier) {
// todo : is this always true? Or just in the case of sharing JDBC resources?
throw new SessionException( "Cannot redefine tenant identifier on child session" );
}
@Override
public T interceptor() {
return interceptor( session.getInterceptor() );
}
@Override
@SuppressWarnings("unchecked")
public T connection() {
this.shareTransactionContext = true;
return (T) this;
}
@Override
public T connectionReleaseMode() {
return connectionReleaseMode( session.getJdbcCoordinator().getLogicalConnection().getConnectionHandlingMode().getReleaseMode() );
}
@Override
public T connectionHandlingMode() {
return connectionHandlingMode( session.getJdbcCoordinator().getLogicalConnection().getConnectionHandlingMode() );
}
@Override
public T autoJoinTransactions() {
return autoJoinTransactions( session.isAutoCloseSessionEnabled() );
}
@Override
public T flushMode() {
return flushMode( session.getHibernateFlushMode() );
}
@Override
public T autoClose() {
return autoClose( session.autoClose );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SharedSessionCreationOptions
@Override
public boolean isTransactionCoordinatorShared() {
return shareTransactionContext;
}
@Override
public TransactionCoordinator getTransactionCoordinator() {
return shareTransactionContext ? session.getTransactionCoordinator() : null;
}
@Override
public JdbcCoordinator getJdbcCoordinator() {
return shareTransactionContext ? session.getJdbcCoordinator() : null;
}
@Override
public TransactionImplementor getTransaction() {
return shareTransactionContext ? session.getCurrentTransaction() : null;
}
@Override
public ActionQueue.TransactionCompletionProcesses getTransactionCompletionProcesses() {
return shareTransactionContext ?
session.getActionQueue().getTransactionCompletionProcesses() :
null;
}
@Override
public boolean isQueryParametersValidationEnabled() {
return session.isQueryParametersValidationEnabled();
}
}
private class LockRequestImpl implements LockRequest {
private final LockOptions lockOptions;
private LockRequestImpl(LockOptions lo) {
lockOptions = new LockOptions();
LockOptions.copy( lo, lockOptions );
}
@Override
public LockMode getLockMode() {
return lockOptions.getLockMode();
}
@Override
public LockRequest setLockMode(LockMode lockMode) {
lockOptions.setLockMode( lockMode );
return this;
}
@Override
public int getTimeOut() {
return lockOptions.getTimeOut();
}
@Override
public LockRequest setTimeOut(int timeout) {
lockOptions.setTimeOut( timeout );
return this;
}
@Override
public boolean getScope() {
return lockOptions.getScope();
}
@Override
public LockRequest setScope(boolean scope) {
lockOptions.setScope( scope );
return this;
}
@Override
public void lock(String entityName, Object object) throws HibernateException {
fireLock( entityName, object, lockOptions );
}
@Override
public void lock(Object object) throws HibernateException {
fireLock( object, lockOptions );
}
}
@Override
protected void addSharedSessionTransactionObserver(TransactionCoordinator transactionCoordinator) {
this.transactionObserver = new TransactionObserver() {
@Override
public void afterBegin() {
}
@Override
public void beforeCompletion() {
if ( isOpen() && getHibernateFlushMode() != FlushMode.MANUAL ) {
managedFlush();
}
actionQueue.beforeTransactionCompletion();
try {
getInterceptor().beforeTransactionCompletion( getTransactionIfAccessible() );
}
catch (Throwable t) {
log.exceptionInBeforeTransactionCompletionInterceptor( t );
}
}
@Override
public void afterCompletion(boolean successful, boolean delayed) {
afterTransactionCompletion( successful, delayed );
if ( !isClosed() && autoClose ) {
managedClose();
}
}
};
transactionCoordinator.addObserver(transactionObserver);
}
@Override
protected void removeSharedSessionTransactionObserver(TransactionCoordinator transactionCoordinator) {
super.removeSharedSessionTransactionObserver( transactionCoordinator );
transactionCoordinator.removeObserver( transactionObserver );
}
private class IdentifierLoadAccessImpl implements IdentifierLoadAccess {
private final EntityPersister entityPersister;
private LockOptions lockOptions;
private CacheMode cacheMode;
private RootGraphImplementor rootGraph;
private GraphSemantic graphSemantic;
private IdentifierLoadAccessImpl(EntityPersister entityPersister) {
this.entityPersister = entityPersister;
}
private IdentifierLoadAccessImpl(String entityName) {
this( locateEntityPersister( entityName ) );
}
private IdentifierLoadAccessImpl(Class entityClass) {
this( locateEntityPersister( entityClass ) );
}
@Override
public final IdentifierLoadAccessImpl with(LockOptions lockOptions) {
this.lockOptions = lockOptions;
return this;
}
@Override
public IdentifierLoadAccess with(CacheMode cacheMode) {
this.cacheMode = cacheMode;
return this;
}
@Override
public IdentifierLoadAccess with(RootGraph graph, GraphSemantic semantic) {
this.rootGraph = (RootGraphImplementor) graph;
this.graphSemantic = semantic;
return this;
}
@Override
public final T getReference(Serializable id) {
return perform( () -> doGetReference( id ) );
}
protected T perform(Supplier executor) {
CacheMode sessionCacheMode = getCacheMode();
boolean cacheModeChanged = false;
if ( cacheMode != null ) {
// naive check for now...
// todo : account for "conceptually equal"
if ( cacheMode != sessionCacheMode ) {
setCacheMode( cacheMode );
cacheModeChanged = true;
}
}
try {
if ( graphSemantic != null ) {
if ( rootGraph == null ) {
throw new IllegalArgumentException( "Graph semantic specified, but no RootGraph was supplied" );
}
loadQueryInfluencers.getEffectiveEntityGraph().applyGraph( rootGraph, graphSemantic );
}
try {
return executor.get();
}
finally {
if ( graphSemantic != null ) {
loadQueryInfluencers.getEffectiveEntityGraph().clear();
}
}
}
finally {
if ( cacheModeChanged ) {
// change it back
setCacheMode( sessionCacheMode );
}
}
}
@SuppressWarnings("unchecked")
protected T doGetReference(Serializable id) {
if ( this.lockOptions != null ) {
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, SessionImpl.this, getReadOnlyFromLoadQueryInfluencers() );
fireLoad( event, LoadEventListener.LOAD );
return (T) event.getResult();
}
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), false, SessionImpl.this, getReadOnlyFromLoadQueryInfluencers() );
boolean success = false;
try {
fireLoad( event, LoadEventListener.LOAD );
if ( event.getResult() == null ) {
getFactory().getEntityNotFoundDelegate().handleEntityNotFound(
entityPersister.getEntityName(),
id
);
}
success = true;
return (T) event.getResult();
}
finally {
afterOperation( success );
}
}
@Override
public final T load(Serializable id) {
return perform( () -> doLoad( id ) );
}
@Override
public Optional loadOptional(Serializable id) {
return Optional.ofNullable( perform( () -> doLoad( id ) ) );
}
@SuppressWarnings("unchecked")
protected final T doLoad(Serializable id) {
if ( this.lockOptions != null ) {
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), lockOptions, SessionImpl.this, getReadOnlyFromLoadQueryInfluencers() );
fireLoad( event, LoadEventListener.GET );
final Object result = event.getResult();
initializeIfNecessary( result );
return (T) result;
}
LoadEvent event = new LoadEvent( id, entityPersister.getEntityName(), false, SessionImpl.this, getReadOnlyFromLoadQueryInfluencers() );
boolean success = false;
try {
fireLoad( event, LoadEventListener.GET );
success = true;
}
catch (ObjectNotFoundException e) {
// if session cache contains proxy for non-existing object
}
finally {
afterOperation( success );
}
final Object result = event.getResult();
initializeIfNecessary( result );
return (T) result;
}
private void initializeIfNecessary(Object result) {
if ( result == null ) {
return;
}
if ( result instanceof HibernateProxy ) {
final HibernateProxy hibernateProxy = (HibernateProxy) result;
final LazyInitializer initializer = hibernateProxy.getHibernateLazyInitializer();
if ( initializer.isUninitialized() ) {
initializer.initialize();
}
return;
}
final BytecodeEnhancementMetadata enhancementMetadata = entityPersister.getEntityMetamodel().getBytecodeEnhancementMetadata();
if ( ! enhancementMetadata.isEnhancedForLazyLoading() ) {
return;
}
final BytecodeLazyAttributeInterceptor interceptor = enhancementMetadata.extractLazyInterceptor(result);
if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) {
( (EnhancementAsProxyLazinessInterceptor) interceptor ).forceInitialize( result, null );
}
}
}
private class MultiIdentifierLoadAccessImpl implements MultiIdentifierLoadAccess, MultiLoadOptions {
private final EntityPersister entityPersister;
private LockOptions lockOptions;
private CacheMode cacheMode;
private RootGraphImplementor rootGraph;
private GraphSemantic graphSemantic;
private Integer batchSize;
private boolean sessionCheckingEnabled;
private boolean returnOfDeletedEntitiesEnabled;
private boolean orderedReturnEnabled = true;
public MultiIdentifierLoadAccessImpl(EntityPersister entityPersister) {
this.entityPersister = entityPersister;
}
@Override
public LockOptions getLockOptions() {
return lockOptions;
}
@Override
public final MultiIdentifierLoadAccess with(LockOptions lockOptions) {
this.lockOptions = lockOptions;
return this;
}
@Override
public MultiIdentifierLoadAccess with(CacheMode cacheMode) {
this.cacheMode = cacheMode;
return this;
}
@Override
public MultiIdentifierLoadAccess with(RootGraph graph, GraphSemantic semantic) {
this.rootGraph = (RootGraphImplementor) graph;
this.graphSemantic = semantic;
return this;
}
@Override
public Integer getBatchSize() {
return batchSize;
}
@Override
public MultiIdentifierLoadAccess withBatchSize(int batchSize) {
if ( batchSize < 1 ) {
this.batchSize = null;
}
else {
this.batchSize = batchSize;
}
return this;
}
@Override
public boolean isSessionCheckingEnabled() {
return sessionCheckingEnabled;
}
@Override
public boolean isSecondLevelCacheCheckingEnabled() {
return cacheMode == CacheMode.NORMAL || cacheMode == CacheMode.GET;
}
@Override
public MultiIdentifierLoadAccess enableSessionCheck(boolean enabled) {
this.sessionCheckingEnabled = enabled;
return this;
}
@Override
public boolean isReturnOfDeletedEntitiesEnabled() {
return returnOfDeletedEntitiesEnabled;
}
@Override
public MultiIdentifierLoadAccess enableReturnOfDeletedEntities(boolean enabled) {
this.returnOfDeletedEntitiesEnabled = enabled;
return this;
}
@Override
public boolean isOrderReturnEnabled() {
return orderedReturnEnabled;
}
@Override
public MultiIdentifierLoadAccess enableOrderedReturn(boolean enabled) {
this.orderedReturnEnabled = enabled;
return this;
}
@Override
@SuppressWarnings("unchecked")
public List multiLoad(K... ids) {
return perform( () -> entityPersister.multiLoad( ids, SessionImpl.this, this ) );
}
public List perform(Supplier> executor) {
CacheMode sessionCacheMode = getCacheMode();
boolean cacheModeChanged = false;
if ( cacheMode != null ) {
// naive check for now...
// todo : account for "conceptually equal"
if ( cacheMode != sessionCacheMode ) {
setCacheMode( cacheMode );
cacheModeChanged = true;
}
}
try {
if ( graphSemantic != null ) {
if ( rootGraph == null ) {
throw new IllegalArgumentException( "Graph semantic specified, but no RootGraph was supplied" );
}
loadQueryInfluencers.getEffectiveEntityGraph().applyGraph( rootGraph, graphSemantic );
}
try {
return executor.get();
}
finally {
if ( graphSemantic != null ) {
loadQueryInfluencers.getEffectiveEntityGraph().clear();
}
}
}
finally {
if ( cacheModeChanged ) {
// change it back
setCacheMode( sessionCacheMode );
}
}
}
@Override
@SuppressWarnings("unchecked")
public List multiLoad(List ids) {
return perform( () -> entityPersister.multiLoad( ids.toArray( new Serializable[ ids.size() ] ), SessionImpl.this, this ) );
}
}
private EntityPersister locateEntityPersister(Class entityClass) {
return getFactory().getMetamodel().locateEntityPersister( entityClass );
}
private EntityPersister locateEntityPersister(String entityName) {
return getFactory().getMetamodel().locateEntityPersister( entityName );
}
private abstract class BaseNaturalIdLoadAccessImpl {
private final EntityPersister entityPersister;
private LockOptions lockOptions;
private boolean synchronizationEnabled = true;
private BaseNaturalIdLoadAccessImpl(EntityPersister entityPersister) {
this.entityPersister = entityPersister;
if ( !entityPersister.hasNaturalIdentifier() ) {
throw new HibernateException(
String.format( "Entity [%s] did not define a natural id", entityPersister.getEntityName() )
);
}
}
public BaseNaturalIdLoadAccessImpl with(LockOptions lockOptions) {
this.lockOptions = lockOptions;
return this;
}
protected void synchronizationEnabled(boolean synchronizationEnabled) {
this.synchronizationEnabled = synchronizationEnabled;
}
protected final Serializable resolveNaturalId(Map naturalIdParameters) {
performAnyNeededCrossReferenceSynchronizations();
final ResolveNaturalIdEvent event =
new ResolveNaturalIdEvent( naturalIdParameters, entityPersister, SessionImpl.this );
fireResolveNaturalId( event );
if ( event.getEntityId() == PersistenceContext.NaturalIdHelper.INVALID_NATURAL_ID_REFERENCE ) {
return null;
}
else {
return event.getEntityId();
}
}
protected void performAnyNeededCrossReferenceSynchronizations() {
if ( !synchronizationEnabled ) {
// synchronization (this process) was disabled
return;
}
if ( entityPersister.getEntityMetamodel().hasImmutableNaturalId() ) {
// only mutable natural-ids need this processing
return;
}
if ( !isTransactionInProgress() ) {
// not in a transaction so skip synchronization
return;
}
final PersistenceContext persistenceContext = getPersistenceContextInternal();
final boolean debugEnabled = log.isDebugEnabled();
for ( Serializable pk : persistenceContext.getNaturalIdHelper()
.getCachedPkResolutions( entityPersister ) ) {
final EntityKey entityKey = generateEntityKey( pk, entityPersister );
final Object entity = persistenceContext.getEntity( entityKey );
final EntityEntry entry = persistenceContext.getEntry( entity );
if ( entry == null ) {
if ( debugEnabled ) {
log.debug(
"Cached natural-id/pk resolution linked to null EntityEntry in persistence context : "
+ MessageHelper.infoString( entityPersister, pk, getFactory() )
);
}
continue;
}
if ( !entry.requiresDirtyCheck( entity ) ) {
continue;
}
// MANAGED is the only status we care about here...
if ( entry.getStatus() != Status.MANAGED ) {
continue;
}
persistenceContext.getNaturalIdHelper().handleSynchronization(
entityPersister,
pk,
entity
);
}
}
protected final IdentifierLoadAccess getIdentifierLoadAccess() {
final IdentifierLoadAccessImpl identifierLoadAccess = new IdentifierLoadAccessImpl( entityPersister );
if ( this.lockOptions != null ) {
identifierLoadAccess.with( lockOptions );
}
return identifierLoadAccess;
}
protected EntityPersister entityPersister() {
return entityPersister;
}
}
private class NaturalIdLoadAccessImpl extends BaseNaturalIdLoadAccessImpl implements NaturalIdLoadAccess {
private final Map naturalIdParameters = new LinkedHashMap();
private NaturalIdLoadAccessImpl(EntityPersister entityPersister) {
super( entityPersister );
}
private NaturalIdLoadAccessImpl(String entityName) {
this( locateEntityPersister( entityName ) );
}
private NaturalIdLoadAccessImpl(Class entityClass) {
this( locateEntityPersister( entityClass ) );
}
@Override
public NaturalIdLoadAccessImpl with(LockOptions lockOptions) {
return (NaturalIdLoadAccessImpl) super.with( lockOptions );
}
@Override
public NaturalIdLoadAccess using(String attributeName, Object value) {
naturalIdParameters.put( attributeName, value );
return this;
}
@Override
public NaturalIdLoadAccessImpl setSynchronizationEnabled(boolean synchronizationEnabled) {
super.synchronizationEnabled( synchronizationEnabled );
return this;
}
@Override
@SuppressWarnings("unchecked")
public final T getReference() {
final Serializable entityId = resolveNaturalId( this.naturalIdParameters );
if ( entityId == null ) {
return null;
}
return (T) this.getIdentifierLoadAccess().getReference( entityId );
}
@Override
@SuppressWarnings("unchecked")
public final T load() {
final Serializable entityId = resolveNaturalId( this.naturalIdParameters );
if ( entityId == null ) {
return null;
}
try {
return (T) this.getIdentifierLoadAccess().load( entityId );
}
catch (EntityNotFoundException | ObjectNotFoundException enf) {
// OK
}
return null;
}
@Override
public Optional loadOptional() {
return Optional.ofNullable( load() );
}
}
private class SimpleNaturalIdLoadAccessImpl extends BaseNaturalIdLoadAccessImpl
implements SimpleNaturalIdLoadAccess {
private final String naturalIdAttributeName;
private SimpleNaturalIdLoadAccessImpl(EntityPersister entityPersister) {
super( entityPersister );
if ( entityPersister.getNaturalIdentifierProperties().length != 1 ) {
throw new HibernateException(
String.format(
"Entity [%s] did not define a simple natural id",
entityPersister.getEntityName()
)
);
}
final int naturalIdAttributePosition = entityPersister.getNaturalIdentifierProperties()[0];
this.naturalIdAttributeName = entityPersister.getPropertyNames()[naturalIdAttributePosition];
}
private SimpleNaturalIdLoadAccessImpl(String entityName) {
this( locateEntityPersister( entityName ) );
}
private SimpleNaturalIdLoadAccessImpl(Class entityClass) {
this( locateEntityPersister( entityClass ) );
}
@Override
public final SimpleNaturalIdLoadAccessImpl with(LockOptions lockOptions) {
return (SimpleNaturalIdLoadAccessImpl) super.with( lockOptions );
}
private Map getNaturalIdParameters(Object naturalIdValue) {
return Collections.singletonMap( naturalIdAttributeName, naturalIdValue );
}
@Override
public SimpleNaturalIdLoadAccessImpl setSynchronizationEnabled(boolean synchronizationEnabled) {
super.synchronizationEnabled( synchronizationEnabled );
return this;
}
@Override
@SuppressWarnings("unchecked")
public T getReference(Object naturalIdValue) {
final Serializable entityId = resolveNaturalId( getNaturalIdParameters( naturalIdValue ) );
if ( entityId == null ) {
return null;
}
return (T) this.getIdentifierLoadAccess().getReference( entityId );
}
@Override
@SuppressWarnings("unchecked")
public T load(Object naturalIdValue) {
final Serializable entityId = resolveNaturalId( getNaturalIdParameters( naturalIdValue ) );
if ( entityId == null ) {
return null;
}
try {
return (T) this.getIdentifierLoadAccess().load( entityId );
}
catch (EntityNotFoundException | ObjectNotFoundException e) {
// OK
}
return null;
}
@Override
public Optional loadOptional(Serializable naturalIdValue) {
return Optional.ofNullable( load( naturalIdValue ) );
}
}
@Override
public void startTransactionBoundary() {
checkOpenOrWaitingForAutoClose();
super.startTransactionBoundary();
}
@Override
public void afterTransactionBegin() {
checkOpenOrWaitingForAutoClose();
getInterceptor().afterTransactionBegin( getTransactionIfAccessible() );
}
@Override
public void flushBeforeTransactionCompletion() {
final boolean doFlush = isTransactionFlushable()
&& getHibernateFlushMode() != FlushMode.MANUAL;
try {
if ( doFlush ) {
managedFlush();
}
}
catch (RuntimeException re) {
throw ExceptionMapperStandardImpl.INSTANCE.mapManagedFlushFailure( "error during managed flush", re, this );
}
}
private boolean isTransactionFlushable() {
if ( getCurrentTransaction() == null ) {
// assume it is flushable - CMT, auto-commit, etc
return true;
}
final TransactionStatus status = getCurrentTransaction().getStatus();
return status == TransactionStatus.ACTIVE || status == TransactionStatus.COMMITTING;
}
@Override
public boolean isFlushBeforeCompletionEnabled() {
return getHibernateFlushMode() != FlushMode.MANUAL;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// HibernateEntityManager impl
@Override
public SessionImplementor getSession() {
return this;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// HibernateEntityManagerImplementor impl
@Override
public LockOptions getLockRequest(LockModeType lockModeType, Map properties) {
LockOptions lockOptions = new LockOptions();
if ( this.lockOptions != null ) { //otherwise the default LockOptions constructor is the same as DEFAULT_LOCK_OPTIONS
LockOptions.copy( this.lockOptions, lockOptions );
}
lockOptions.setLockMode( LockModeTypeHelper.getLockMode( lockModeType ) );
if ( properties != null ) {
LockOptionsHelper.applyPropertiesToLockOptions( properties, () -> lockOptions );
}
return lockOptions;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// EntityManager impl
@Override
public void remove(Object entity) {
checkOpen();
try {
delete( entity );
}
catch (MappingException e) {
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage(), e ) );
}
catch ( RuntimeException e ) {
//including HibernateException
throw getExceptionConverter().convert( e );
}
}
@Override
public T find(Class entityClass, Object primaryKey) {
return find( entityClass, primaryKey, null, null );
}
@Override
public T find(Class entityClass, Object primaryKey, Map properties) {
return find( entityClass, primaryKey, null, properties );
}
@Override
public T find(Class entityClass, Object primaryKey, LockModeType lockModeType) {
return find( entityClass, primaryKey, lockModeType, null );
}
@Override
public T find(Class entityClass, Object primaryKey, LockModeType lockModeType, Map properties) {
checkOpen();
LockOptions lockOptions = null;
try {
getLoadQueryInfluencers().getEffectiveEntityGraph().applyConfiguredGraph( properties );
Boolean readOnly = properties == null ? null : (Boolean) properties.get( QueryHints.HINT_READONLY );
getLoadQueryInfluencers().setReadOnly( readOnly );
final IdentifierLoadAccess loadAccess = byId( entityClass );
loadAccess.with( determineAppropriateLocalCacheMode( properties ) );
if ( lockModeType != null ) {
if ( !LockModeType.NONE.equals( lockModeType) ) {
checkTransactionNeededForUpdateOperation();
}
lockOptions = buildLockOptions( lockModeType, properties );
loadAccess.with( lockOptions );
}
if ( getLoadQueryInfluencers().getEffectiveEntityGraph().getSemantic() == GraphSemantic.FETCH ) {
setEnforcingFetchGraph( true );
}
return loadAccess.load( (Serializable) primaryKey );
}
catch ( EntityNotFoundException ignored ) {
// DefaultLoadEventListener#returnNarrowedProxy() may throw ENFE (see HHH-7861 for details),
// which find() should not throw. Find() should return null if the entity was not found.
if ( log.isDebugEnabled() ) {
String entityName = entityClass != null ? entityClass.getName(): null;
String identifierValue = primaryKey != null ? primaryKey.toString() : null ;
log.ignoringEntityNotFound( entityName, identifierValue );
}
return null;
}
catch ( ObjectDeletedException e ) {
//the spec is silent about people doing remove() find() on the same PC
return null;
}
catch ( ObjectNotFoundException e ) {
//should not happen on the entity itself with get
throw new IllegalArgumentException( e.getMessage(), e );
}
catch ( MappingException | TypeMismatchException | ClassCastException e ) {
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage(), e ) );
}
catch ( JDBCException e ) {
if ( accessTransaction().isActive() && accessTransaction().getRollbackOnly() ) {
// Assume this is similar to the WildFly / IronJacamar "feature" described under HHH-12472.
// Just log the exception and return null.
if ( log.isDebugEnabled() ) {
log.debug( "JDBCException was thrown for a transaction marked for rollback; " +
"this is probably due to an operation failing fast due to the " +
"transaction marked for rollback.", e );
}
return null;
}
else {
throw getExceptionConverter().convert( e, lockOptions );
}
}
catch ( RuntimeException e ) {
throw getExceptionConverter().convert( e, lockOptions );
}
finally {
getLoadQueryInfluencers().getEffectiveEntityGraph().clear();
getLoadQueryInfluencers().setReadOnly( null );
setEnforcingFetchGraph( false );
}
}
protected CacheMode determineAppropriateLocalCacheMode(Map localProperties) {
CacheRetrieveMode retrieveMode = null;
CacheStoreMode storeMode = null;
if ( localProperties != null ) {
retrieveMode = determineCacheRetrieveMode( localProperties );
storeMode = determineCacheStoreMode( localProperties );
}
if ( retrieveMode == null ) {
// use the EM setting
retrieveMode = fastSessionServices.getCacheRetrieveMode( this.properties );
}
if ( storeMode == null ) {
// use the EM setting
storeMode = fastSessionServices.getCacheStoreMode( this.properties );
}
return CacheModeHelper.interpretCacheMode( storeMode, retrieveMode );
}
private static CacheRetrieveMode determineCacheRetrieveMode(Map settings) {
final CacheRetrieveMode cacheRetrieveMode = (CacheRetrieveMode) settings.get( JPA_SHARED_CACHE_RETRIEVE_MODE );
if ( cacheRetrieveMode == null ) {
return (CacheRetrieveMode) settings.get( JAKARTA_JPA_SHARED_CACHE_RETRIEVE_MODE );
}
return cacheRetrieveMode;
}
private static CacheStoreMode determineCacheStoreMode(Map settings) {
final CacheStoreMode cacheStoreMode = (CacheStoreMode) settings.get( JPA_SHARED_CACHE_STORE_MODE );
if ( cacheStoreMode == null ) {
return ( CacheStoreMode ) settings.get( JAKARTA_JPA_SHARED_CACHE_STORE_MODE );
}
return cacheStoreMode;
}
private void checkTransactionNeededForUpdateOperation() {
checkTransactionNeededForUpdateOperation( "no transaction is in progress" );
}
@Override
public T getReference(Class entityClass, Object primaryKey) {
checkOpen();
try {
return byId( entityClass ).getReference( (Serializable) primaryKey );
}
catch ( MappingException | TypeMismatchException | ClassCastException e ) {
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage(), e ) );
}
catch ( RuntimeException e ) {
throw getExceptionConverter().convert( e );
}
}
@Override
public void lock(Object entity, LockModeType lockModeType) {
lock( entity, lockModeType, null );
}
@Override
public void lock(Object entity, LockModeType lockModeType, Map properties) {
checkOpen();
checkTransactionNeededForUpdateOperation();
if ( !contains( entity ) ) {
throw new IllegalArgumentException( "entity not in the persistence context" );
}
final LockOptions lockOptions = buildLockOptions( lockModeType, properties );
try {
buildLockRequest( lockOptions ).lock( entity );
}
catch (RuntimeException e) {
throw getExceptionConverter().convert( e, lockOptions );
}
}
@Override
public void refresh(Object entity, Map properties) {
refresh( entity, null, properties );
}
@Override
public void refresh(Object entity, LockModeType lockModeType) {
refresh( entity, lockModeType, null );
}
@Override
public void refresh(Object entity, LockModeType lockModeType, Map properties) {
checkOpen();
final CacheMode previousCacheMode = getCacheMode();
final CacheMode refreshCacheMode = determineAppropriateLocalCacheMode( properties );
LockOptions lockOptions = null;
try {
setCacheMode( refreshCacheMode );
if ( !contains( entity ) ) {
throw getExceptionConverter().convert( new IllegalArgumentException( "Entity not managed" ) );
}
if ( lockModeType != null ) {
if ( !LockModeType.NONE.equals( lockModeType) ) {
checkTransactionNeededForUpdateOperation();
}
lockOptions = buildLockOptions( lockModeType, properties );
refresh( entity, lockOptions );
}
else {
refresh( entity );
}
}
catch (MappingException e) {
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage(), e ) );
}
catch (RuntimeException e) {
throw getExceptionConverter().convert( e, lockOptions );
}
finally {
setCacheMode( previousCacheMode );
}
}
@Override
public void detach(Object entity) {
checkOpen();
try {
evict( entity );
}
catch (RuntimeException e) {
throw getExceptionConverter().convert( e );
}
}
@Override
public LockModeType getLockMode(Object entity) {
checkOpen();
if ( !isTransactionInProgress() ) {
throw new TransactionRequiredException( "Call to EntityManager#getLockMode should occur within transaction according to spec" );
}
if ( !contains( entity ) ) {
throw getExceptionConverter().convert( new IllegalArgumentException( "entity not in the persistence context" ) );
}
return LockModeTypeHelper.getLockModeType( getCurrentLockMode( entity ) );
}
@Override
public void setProperty(String propertyName, Object value) {
checkOpen();
if ( !( value instanceof Serializable ) ) {
log.warnf( "Property '" + propertyName + "' is not serializable, value won't be set." );
return;
}
if ( propertyName == null ) {
log.warnf( "Property having key null is illegal; value won't be set." );
return;
}
//Store property for future reference:
if ( properties == null ) {
properties = computeCurrentSessionProperties();
}
properties.put( propertyName, value );
//now actually update settings, if it's any of these which have a direct impact on this Session state:
if ( AvailableSettings.FLUSH_MODE.equals( propertyName ) ) {
setHibernateFlushMode( ConfigurationHelper.getFlushMode( value, FlushMode.AUTO ) );
}
else if ( JPA_LOCK_SCOPE.equals( propertyName ) || JPA_LOCK_TIMEOUT.equals( propertyName )
|| JAKARTA_JPA_LOCK_SCOPE.equals( propertyName ) || JAKARTA_JPA_LOCK_TIMEOUT.equals( propertyName ) ) {
LockOptionsHelper.applyPropertiesToLockOptions( properties, this::getLockOptionsForWrite );
}
else if ( JPA_SHARED_CACHE_RETRIEVE_MODE.equals( propertyName )
|| JPA_SHARED_CACHE_STORE_MODE.equals( propertyName )
|| JAKARTA_JPA_SHARED_CACHE_RETRIEVE_MODE.equals( propertyName )
|| JAKARTA_JPA_SHARED_CACHE_STORE_MODE.equals( propertyName ) ) {
getSession().setCacheMode(
CacheModeHelper.interpretCacheMode(
determineCacheStoreMode( properties ),
determineCacheRetrieveMode( properties )
)
);
}
}
private Map computeCurrentSessionProperties() {
final HashMap map = new HashMap<>( fastSessionServices.defaultSessionProperties );
//The FLUSH_MODE is always set at Session creation time, so it needs special treatment to not eagerly initialize this Map:
map.put( AvailableSettings.FLUSH_MODE, getHibernateFlushMode().name() );
return map;
}
@Override
public Map getProperties() {
if ( properties == null ) {
properties = computeCurrentSessionProperties();
}
return Collections.unmodifiableMap( properties );
}
@Override
protected void initQueryFromNamedDefinition(Query query, NamedQueryDefinition namedQueryDefinition) {
super.initQueryFromNamedDefinition( query, namedQueryDefinition );
if ( namedQueryDefinition.getLockOptions() != null ) {
if ( namedQueryDefinition.getLockOptions().getLockMode() != null ) {
query.setLockMode(
LockModeTypeHelper.getLockModeType( namedQueryDefinition.getLockOptions().getLockMode() )
);
}
}
}
@Override
public StoredProcedureQuery createNamedStoredProcedureQuery(String name) {
checkOpen();
try {
final ProcedureCallMemento memento = getFactory().getNamedQueryRepository().getNamedProcedureCallMemento( name );
if ( memento == null ) {
throw new IllegalArgumentException( "No @NamedStoredProcedureQuery was found with that name : " + name );
}
return memento.makeProcedureCall( this );
}
catch ( RuntimeException e ) {
throw getExceptionConverter().convert( e );
}
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName) {
try {
return createStoredProcedureCall( procedureName );
}
catch ( RuntimeException e ) {
throw getExceptionConverter().convert( e );
}
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) {
try {
return createStoredProcedureCall( procedureName, resultClasses );
}
catch ( RuntimeException e ) {
throw getExceptionConverter().convert( e );
}
}
@Override
public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) {
checkOpen();
try {
try {
return createStoredProcedureCall( procedureName, resultSetMappings );
}
catch (UnknownSqlResultSetMappingException unknownResultSetMapping) {
throw new IllegalArgumentException( unknownResultSetMapping.getMessage(), unknownResultSetMapping );
}
}
catch ( RuntimeException e ) {
throw getExceptionConverter().convert( e );
}
}
@Override
public void joinTransaction() {
checkOpen();
joinTransaction( true );
}
private void joinTransaction(boolean explicitRequest) {
if ( !getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta() ) {
if ( explicitRequest ) {
log.callingJoinTransactionOnNonJtaEntityManager();
}
return;
}
try {
getTransactionCoordinator().explicitJoin();
}
catch (TransactionRequiredForJoinException e) {
throw new TransactionRequiredException( e.getMessage() );
}
catch (HibernateException he) {
throw getExceptionConverter().convert( he );
}
}
@Override
public boolean isJoinedToTransaction() {
checkOpen();
return getTransactionCoordinator().isJoined();
}
@Override
@SuppressWarnings("unchecked")
public T unwrap(Class clazz) {
checkOpen();
if ( Session.class.isAssignableFrom( clazz ) ) {
return (T) this;
}
if ( SessionImplementor.class.isAssignableFrom( clazz ) ) {
return (T) this;
}
if ( SharedSessionContractImplementor.class.isAssignableFrom( clazz ) ) {
return (T) this;
}
if ( EntityManager.class.isAssignableFrom( clazz ) ) {
return (T) this;
}
throw new PersistenceException( "Hibernate cannot unwrap " + clazz );
}
@Override
public Object getDelegate() {
checkOpen();
return this;
}
@Override
public SessionFactoryImplementor getEntityManagerFactory() {
checkOpen();
return getFactory();
}
@Override
public CriteriaBuilder getCriteriaBuilder() {
checkOpen();
return getFactory().getCriteriaBuilder();
}
@Override
public MetamodelImplementor getMetamodel() {
checkOpen();
return getFactory().getMetamodel();
}
@Override
public RootGraphImplementor createEntityGraph(Class rootType) {
checkOpen();
return new RootGraphImpl( null, getMetamodel().entity( rootType ), getEntityManagerFactory() );
}
@Override
public RootGraphImplementor> createEntityGraph(String graphName) {
checkOpen();
final RootGraphImplementor named = getEntityManagerFactory().findEntityGraphByName( graphName );
if ( named != null ) {
return named.makeRootGraph( graphName, true );
}
return named;
}
@Override
@SuppressWarnings("unchecked")
public RootGraphImplementor> getEntityGraph(String graphName) {
checkOpen();
final RootGraphImplementor named = getEntityManagerFactory().findEntityGraphByName( graphName );
if ( named == null ) {
throw new IllegalArgumentException( "Could not locate EntityGraph with given name : " + graphName );
}
return named;
}
@Override
public List getEntityGraphs(Class entityClass) {
checkOpen();
return getEntityManagerFactory().findEntityGraphsByType( entityClass );
}
/**
* 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 ( log.isTraceEnabled() ) {
log.tracef( "Serializing Session [%s]", getSessionIdentifier() );
}
oos.defaultWriteObject();
persistenceContext.serialize( oos );
actionQueue.serialize( oos );
oos.writeObject( loadQueryInfluencers );
}
/**
* 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, SQLException {
if ( log.isTraceEnabled() ) {
log.tracef( "Deserializing Session [%s]", getSessionIdentifier() );
}
ois.defaultReadObject();
persistenceContext = StatefulPersistenceContext.deserialize( ois, this );
actionQueue = ActionQueue.deserialize( ois, this );
loadQueryInfluencers = (LoadQueryInfluencers) 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 ).
for ( String filterName : loadQueryInfluencers.getEnabledFilterNames() ) {
( (FilterImpl) loadQueryInfluencers.getEnabledFilter( filterName ) ).afterDeserialize( getFactory() );
}
}
private Boolean getReadOnlyFromLoadQueryInfluencers() {
Boolean readOnly = null;
if ( loadQueryInfluencers != null ) {
readOnly = loadQueryInfluencers.getReadOnly();
}
return readOnly;
}
@Override
public boolean isEnforcingFetchGraph() {
return this.isEnforcingFetchGraph;
}
@Override
public void setEnforcingFetchGraph(boolean isEnforcingFetchGraph) {
this.isEnforcingFetchGraph = isEnforcingFetchGraph;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy