X-Git-Url: https://dev.e-taxonomy.eu/gitweb/taxeditor.git/blobdiff_plain/59695a56595a8865672a2aac733c23b50e54509f..58696c3534a439d3e9efe70b6c28e632971f83ff:/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java
index de085411f..39adf8830 100644
--- a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java
+++ b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java
@@ -1,463 +1,470 @@
-/*
- * Hibernate, Relational Persistence for Idiomatic Java
- *
- * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
- * indicated by the @author tags or express copyright attribution
- * statements applied by the authors. All third-party contributions are
- * distributed under license by Red Hat Inc.
- *
- * This copyrighted material is made available to anyone wishing to use, modify,
- * copy, or redistribute it subject to the terms and conditions of the GNU
- * Lesser General Public License, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this distribution; if not, write to:
- * Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor
- * Boston, MA 02110-1301 USA
- */
-package org.hibernate.proxy;
-
-import java.io.Serializable;
-
-import javax.naming.NamingException;
-
-import org.hibernate.HibernateException;
-import org.hibernate.LazyInitializationException;
-import org.hibernate.Session;
-import org.hibernate.SessionException;
-import org.hibernate.TransientObjectException;
-import org.hibernate.engine.spi.EntityKey;
-import org.hibernate.engine.spi.SessionFactoryImplementor;
-import org.hibernate.engine.spi.SessionImplementor;
-import org.hibernate.internal.SessionFactoryRegistry;
-import org.hibernate.persister.entity.EntityPersister;
-import org.jboss.logging.Logger;
-
-import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteConfiguration;
-import eu.etaxonomy.cdm.cache.ProxyUtils;
-import eu.etaxonomy.cdm.model.common.CdmBase;
-import eu.etaxonomy.taxeditor.service.ICachedCommonService;
-
-/**
- * Convenience base class for lazy initialization handlers. Centralizes the basic plumbing of doing lazy
- * initialization freeing subclasses to acts as essentially adapters to their intended entity mode and/or
- * proxy generation strategy.
- *
- * @author Gavin King
- */
-public abstract class AbstractLazyInitializer implements LazyInitializer {
- private static final Logger log = Logger.getLogger( AbstractLazyInitializer.class );
-
- private String entityName;
- private Serializable id;
- private Object target;
- private boolean initialized;
- private boolean readOnly;
- private boolean unwrap;
- private transient SessionImplementor session;
- private Boolean readOnlyBeforeAttachedToSession;
-
- private String sessionFactoryUuid;
- private boolean specjLazyLoad = false;
-
- /**
- * For serialization from the non-pojo initializers (HHH-3309)
- */
- protected AbstractLazyInitializer() {
- }
-
- /**
- * Main constructor.
- *
- * @param entityName The name of the entity being proxied.
- * @param id The identifier of the entity being proxied.
- * @param session The session owning the proxy.
- */
- protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
- this.entityName = entityName;
- this.id = id;
- // initialize other fields depending on session state
- if ( session == null ) {
- unsetSession();
- }
- else {
- setSession( session );
- }
- }
-
- @Override
- public final String getEntityName() {
- return entityName;
- }
-
- @Override
- public final Serializable getIdentifier() {
- return id;
- }
-
- @Override
- public final void setIdentifier(Serializable id) {
- this.id = id;
- }
-
- @Override
- public final boolean isUninitialized() {
- return !initialized;
- }
-
- @Override
- public final SessionImplementor getSession() {
- return session;
- }
-
- @Override
- public final void setSession(SessionImplementor s) throws HibernateException {
- if ( s != session ) {
- // check for s == null first, since it is least expensive
- if ( s == null ) {
- unsetSession();
- }
- else if ( isConnectedToSession() ) {
- //TODO: perhaps this should be some other RuntimeException...
- throw new HibernateException( "illegally attempted to associate a proxy with two open Sessions" );
- }
- else {
- // s != null
- session = s;
- if ( readOnlyBeforeAttachedToSession == null ) {
- // use the default read-only/modifiable setting
- final EntityPersister persister = s.getFactory().getEntityPersister( entityName );
- setReadOnly( s.getPersistenceContext().isDefaultReadOnly() || !persister.isMutable() );
- }
- else {
- // use the read-only/modifiable setting indicated during deserialization
- setReadOnly( readOnlyBeforeAttachedToSession.booleanValue() );
- readOnlyBeforeAttachedToSession = null;
- }
- }
- }
- }
-
- private static EntityKey generateEntityKeyOrNull(Serializable id, SessionImplementor s, String entityName) {
- if ( id == null || s == null || entityName == null ) {
- return null;
- }
- return s.generateEntityKey( id, s.getFactory().getEntityPersister( entityName ) );
- }
-
- @Override
- public final void unsetSession() {
- prepareForPossibleSpecialSpecjInitialization();
- session = null;
- readOnly = false;
- readOnlyBeforeAttachedToSession = null;
- }
-
- @Override
- public final void initialize() throws HibernateException {
- // In remoting we are sure that session is null
- // both when using property paths and switching off conversations
- if(session == null && remoting) {
- remoteInitialize();
- }
- if ( !initialized ) {
- if ( specjLazyLoad ) {
- specialSpecjInitialization();
- }
- else if ( session == null ) {
- throw new LazyInitializationException( "could not initialize proxy - no Session" );
- }
- else if ( !session.isOpen() ) {
- throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" );
- }
- else if ( !session.isConnected() ) {
- throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
- }
- else {
- target = session.immediateLoad( entityName, id );
- initialized = true;
- checkTargetState();
- }
- }
- else {
- checkTargetState();
- }
- }
-
- protected void specialSpecjInitialization() {
- if ( session == null ) {
- //we have a detached collection thats set to null, reattach
- if ( sessionFactoryUuid == null ) {
- throw new LazyInitializationException( "could not initialize proxy - no Session" );
- }
- try {
- SessionFactoryImplementor sf = (SessionFactoryImplementor)
- SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
- SessionImplementor session = (SessionImplementor) sf.openSession();
-
- // TODO: On the next major release, add an
- // 'isJTA' or 'getTransactionFactory' method to Session.
- /*boolean isJTA = session.getTransactionCoordinator()
- .getTransactionContext().getTransactionEnvironment()
- .getTransactionFactory()
- .compatibleWithJtaSynchronization();
- */
- boolean isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
-
- if ( !isJTA ) {
- // Explicitly handle the transactions only if we're not in
- // a JTA environment. A lazy loading temporary session can
- // be created even if a current session and transaction are
- // open (ex: session.clear() was used). We must prevent
- // multiple transactions.
- ( ( Session) session ).beginTransaction();
- }
-
- try {
- target = session.immediateLoad( entityName, id );
- }
- finally {
- // make sure the just opened temp session gets closed!
- try {
- if ( !isJTA ) {
- ( ( Session) session ).getTransaction().commit();
- }
- ( (Session) session ).close();
- }
- catch (Exception e) {
- log.warn( "Unable to close temporary session used to load lazy proxy associated to no session" );
- }
- }
- initialized = true;
- checkTargetState();
- }
- catch (Exception e) {
- e.printStackTrace();
- throw new LazyInitializationException( e.getMessage() );
- }
- }
- else if ( session.isOpen() && session.isConnected() ) {
- target = session.immediateLoad( entityName, id );
- initialized = true;
- checkTargetState();
- }
- else {
- throw new LazyInitializationException( "could not initialize proxy - Session was closed or disced" );
- }
- }
-
- protected void prepareForPossibleSpecialSpecjInitialization() {
- if ( session != null ) {
- specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
-
- if ( specjLazyLoad && sessionFactoryUuid == null ) {
- try {
- sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
- }
- catch (NamingException e) {
- //not much we can do if this fails...
- }
- }
- }
- }
-
- private void checkTargetState() {
- if ( !unwrap ) {
- if ( target == null ) {
- getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id );
- }
- }
- }
-
- /**
- * Getter for property 'connectedToSession'.
- *
- * @return Value for property 'connectedToSession'.
- */
- protected final boolean isConnectedToSession() {
- return getProxyOrNull() != null;
- }
-
- private Object getProxyOrNull() {
- final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
- if ( entityKey != null && session != null && session.isOpen() ) {
- return session.getPersistenceContext().getProxy( entityKey );
- }
- return null;
- }
-
- @Override
- public final Object getImplementation() {
- initialize();
- return target;
- }
-
- @Override
- public final void setImplementation(Object target) {
- this.target = target;
- initialized = true;
- }
-
- @Override
- public final Object getImplementation(SessionImplementor s) throws HibernateException {
- final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), s, getEntityName() );
- return (entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ));
- }
-
- /**
- * Getter for property 'target'.
- *
- * Same as {@link #getImplementation()} except that this method will not force initialization.
- *
- * @return Value for property 'target'.
- */
- protected final Object getTarget() {
- return target;
- }
-
- @Override
- public final boolean isReadOnlySettingAvailable() {
- return (session != null && !session.isClosed());
- }
-
- private void errorIfReadOnlySettingNotAvailable() {
- if ( session == null ) {
- throw new TransientObjectException(
- "Proxy is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
- );
- }
- if ( session.isClosed() ) {
- throw new SessionException(
- "Session is closed. The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
- );
- }
- }
-
- @Override
- public final boolean isReadOnly() {
- errorIfReadOnlySettingNotAvailable();
- return readOnly;
- }
-
- @Override
- public final void setReadOnly(boolean readOnly) {
- errorIfReadOnlySettingNotAvailable();
- // only update if readOnly is different from current setting
- if ( this.readOnly != readOnly ) {
- final EntityPersister persister = session.getFactory().getEntityPersister( entityName );
- if ( !persister.isMutable() && !readOnly ) {
- throw new IllegalStateException( "cannot make proxies for immutable entities modifiable" );
- }
- this.readOnly = readOnly;
- if ( initialized ) {
- EntityKey key = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
- if ( key != null && session.getPersistenceContext().containsEntity( key ) ) {
- session.getPersistenceContext().setReadOnly( target, readOnly );
- }
- }
- }
- }
-
- /**
- * Get the read-only/modifiable setting that should be put in affect when it is
- * attached to a session.
- *
- * This method should only be called during serialization when read-only/modifiable setting
- * is not available (i.e., isReadOnlySettingAvailable() == false)
- *
- * @return null, if the default setting should be used;
- * true, for read-only;
- * false, for modifiable
- *
- * @throws IllegalStateException if isReadOnlySettingAvailable() == true
- */
- protected final Boolean isReadOnlyBeforeAttachedToSession() {
- if ( isReadOnlySettingAvailable() ) {
- throw new IllegalStateException(
- "Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
- );
- }
- return readOnlyBeforeAttachedToSession;
- }
-
- /**
- * Set the read-only/modifiable setting that should be put in affect when it is
- * attached to a session.
- *
- * This method should only be called during deserialization, before associating
- * the proxy with a session.
- *
- * @param readOnlyBeforeAttachedToSession, the read-only/modifiable setting to use when
- * associated with a session; null indicates that the default should be used.
- *
- * @throws IllegalStateException if isReadOnlySettingAvailable() == true
- */
- /* package-private */
- final void setReadOnlyBeforeAttachedToSession(Boolean readOnlyBeforeAttachedToSession) {
- if ( isReadOnlySettingAvailable() ) {
- throw new IllegalStateException(
- "Cannot call setReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
- );
- }
- this.readOnlyBeforeAttachedToSession = readOnlyBeforeAttachedToSession;
- }
-
- @Override
- public boolean isUnwrap() {
- return unwrap;
- }
-
- @Override
- public void setUnwrap(boolean unwrap) {
- this.unwrap = unwrap;
- }
-
- /** Below is section of code which makes remote service calls */
-
- private static CdmApplicationRemoteConfiguration configuration;
- private static boolean remoting = false;
-
- public static void setConfiguration(CdmApplicationRemoteConfiguration conf) {
- remoting = true;
- configuration = conf;
- }
-
-
- private void remoteInitialize() {
-
- if(!initialized) {
- int classid = ((Integer)getIdentifier()).intValue();
- log.info("--> Remote Lazy Initializing Object " + getEntityName() + " with id " + classid);
- Class clazz;
- try {
- clazz = Class.forName(getEntityName());
- } catch (ClassNotFoundException e) {
- throw new HibernateException("Class for " + getEntityName() + " not found", e);
- }
- if(configuration == null) {
- throw new HibernateException("CdmApplicationRemoteConfiguration not initialized (null)");
- }
- ICachedCommonService cachedCommonService = configuration.getCachedCommonService();
- if(cachedCommonService == null) {
- throw new HibernateException("commonService not initialized (null)");
- }
-
- CdmBase cdmBase = cachedCommonService.find(clazz,classid);
- if(ProxyUtils.isUninitializedProxy(cdmBase)) {
- throw new HibernateException("CdmBase Object initialized but is still a proxy");
- }
- setImplementation(cdmBase);
-
- }
- }
-
- public static boolean isInitialized(AbstractLazyInitializer obj) {
- return obj.initialized;
- }
-}
+/* 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.proxy;
+
+import java.io.Serializable;
+
+import org.hibernate.FlushMode;
+import org.hibernate.HibernateException;
+import org.hibernate.LazyInitializationException;
+import org.hibernate.SessionException;
+import org.hibernate.TransientObjectException;
+import org.hibernate.boot.spi.SessionFactoryOptions;
+import org.hibernate.engine.spi.EntityKey;
+import org.hibernate.engine.spi.PersistenceContext;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+import org.hibernate.engine.spi.SharedSessionContractImplementor;
+import org.hibernate.internal.CoreLogging;
+import org.hibernate.internal.CoreMessageLogger;
+import org.hibernate.internal.SessionFactoryRegistry;
+import org.hibernate.persister.entity.EntityPersister;
+
+/**
+ * Convenience base class for lazy initialization handlers. Centralizes the basic plumbing of doing lazy
+ * initialization freeing subclasses to acts as essentially adapters to their intended entity mode and/or
+ * proxy generation strategy.
+ *
+ * @author Gavin King
+ */
+public abstract class AbstractLazyInitializer implements LazyInitializer {
+ private static final CoreMessageLogger LOG = CoreLogging.messageLogger( AbstractLazyInitializer.class );
+
+ private String entityName;
+ private Serializable id;
+ private Object target;
+ private boolean initialized;
+ private boolean readOnly;
+ private boolean unwrap;
+ private transient SharedSessionContractImplementor session;
+ private Boolean readOnlyBeforeAttachedToSession;
+
+ private String sessionFactoryUuid;
+ private boolean allowLoadOutsideTransaction;
+
+ /**
+ * @deprecated This constructor was initially intended for serialization only, and is not useful anymore.
+ * In any case it should not be relied on by user code.
+ * Subclasses should rather implement Serializable with an {@code Object writeReplace()} method returning
+ * a subclass of {@link AbstractSerializableProxy},
+ * which in turn implements Serializable and an {@code Object readResolve()} method
+ * instantiating the {@link AbstractLazyInitializer} subclass
+ * and calling {@link AbstractSerializableProxy#afterDeserialization(AbstractLazyInitializer)} on it.
+ * See {@link org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor} and
+ * {@link org.hibernate.proxy.pojo.bytebuddy.SerializableProxy} for examples.
+ */
+ @Deprecated
+ protected AbstractLazyInitializer() {
+ }
+
+ /**
+ * Main constructor.
+ *
+ * @param entityName The name of the entity being proxied.
+ * @param id The identifier of the entity being proxied.
+ * @param session The session owning the proxy.
+ */
+ protected AbstractLazyInitializer(String entityName, Serializable id, SharedSessionContractImplementor session) {
+ this.entityName = entityName;
+ this.id = id;
+ // initialize other fields depending on session state
+ if ( session == null ) {
+ unsetSession();
+ }
+ else {
+ setSession( session );
+ }
+ }
+
+ @Override
+ public final String getEntityName() {
+ return entityName;
+ }
+
+ @Override
+ public final Serializable getInternalIdentifier() {
+ return id;
+ }
+
+ @Override
+ public final Serializable getIdentifier() {
+ if ( isUninitialized() && isInitializeProxyWhenAccessingIdentifier() ) {
+ initialize();
+ }
+ return id;
+ }
+
+ private boolean isInitializeProxyWhenAccessingIdentifier() {
+ return getSession() != null && getSession().getFactory()
+ .getSessionFactoryOptions()
+ .getJpaCompliance().isJpaProxyComplianceEnabled();
+ }
+
+ @Override
+ public final void setIdentifier(Serializable id) {
+ this.id = id;
+ }
+
+ @Override
+ public final boolean isUninitialized() {
+ return !initialized;
+ }
+
+ @Override
+ public final SharedSessionContractImplementor getSession() {
+ return session;
+ }
+
+ @Override
+ public final void setSession(SharedSessionContractImplementor s) throws HibernateException {
+ if ( s != session ) {
+ // check for s == null first, since it is least expensive
+ if ( s == null ) {
+ unsetSession();
+ }
+ else if ( isConnectedToSession() ) {
+ //TODO: perhaps this should be some other RuntimeException...
+ LOG.attemptToAssociateProxyWithTwoOpenSessions(
+ entityName,
+ id
+ );
+ throw new HibernateException( "illegally attempted to associate proxy [" + entityName + "#" + id + "] with two open Sessions" );
+ }
+ else {
+ // s != null
+ session = s;
+ if ( readOnlyBeforeAttachedToSession == null ) {
+ // use the default read-only/modifiable setting
+ final EntityPersister persister = s.getFactory().getEntityPersister( entityName );
+ setReadOnly( s.getPersistenceContext().isDefaultReadOnly() || !persister.isMutable() );
+ }
+ else {
+ // use the read-only/modifiable setting indicated during deserialization
+ setReadOnly( readOnlyBeforeAttachedToSession );
+ readOnlyBeforeAttachedToSession = null;
+ }
+ }
+ }
+ }
+
+ private static EntityKey generateEntityKeyOrNull(Serializable id, SharedSessionContractImplementor s, String entityName) {
+ if ( id == null || s == null || entityName == null ) {
+ return null;
+ }
+ return s.generateEntityKey( id, s.getFactory().getEntityPersister( entityName ) );
+ }
+
+ @Override
+ public final void unsetSession() {
+ prepareForPossibleLoadingOutsideTransaction();
+ session = null;
+ readOnly = false;
+ readOnlyBeforeAttachedToSession = null;
+ }
+
+ @Override
+ public final void initialize() throws HibernateException {
+ if ( !initialized ) {
+ if ( allowLoadOutsideTransaction ) {
+ permissiveInitialization();
+ }
+ else if ( session == null ) {
+ throw new LazyInitializationException( "could not initialize proxy [" + entityName + "#" + id + "] - no Session" );
+ }
+ else if ( !session.isOpenOrWaitingForAutoClose() ) {
+ throw new LazyInitializationException( "could not initialize proxy [" + entityName + "#" + id + "] - the owning Session was closed" );
+ }
+ else if ( !session.isConnected() ) {
+ throw new LazyInitializationException( "could not initialize proxy [" + entityName + "#" + id + "] - the owning Session is disconnected" );
+ }
+ else {
+ target = session.immediateLoad( entityName, id );
+ initialized = true;
+ checkTargetState(session);
+ }
+ }
+ else {
+ checkTargetState(session);
+ }
+ }
+
+ protected void permissiveInitialization() {
+ if ( session == null ) {
+ //we have a detached collection that is set to null, reattach
+ if ( sessionFactoryUuid == null ) {
+ throw new LazyInitializationException( "could not initialize proxy [" + entityName + "#" + id + "] - no Session" );
+ }
+ try {
+ SessionFactoryImplementor sf = (SessionFactoryImplementor)
+ SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
+ SharedSessionContractImplementor session = (SharedSessionContractImplementor) sf.openSession();
+ session.getPersistenceContext().setDefaultReadOnly( true );
+ session.setFlushMode( FlushMode.MANUAL );
+
+ boolean isJTA = session.getTransactionCoordinator().getTransactionCoordinatorBuilder().isJta();
+
+ if ( !isJTA ) {
+ // Explicitly handle the transactions only if we're not in
+ // a JTA environment. A lazy loading temporary session can
+ // be created even if a current session and transaction are
+ // open (ex: session.clear() was used). We must prevent
+ // multiple transactions.
+ session.beginTransaction();
+ }
+
+ try {
+ target = session.immediateLoad( entityName, id );
+ initialized = true;
+ checkTargetState(session);
+ }
+ finally {
+ // make sure the just opened temp session gets closed!
+ try {
+ if ( !isJTA ) {
+ session.getTransaction().commit();
+ }
+ session.close();
+ }
+ catch (Exception e) {
+ LOG.warn( "Unable to close temporary session used to load lazy proxy associated to no session" );
+ }
+ }
+ }
+ catch (Exception e) {
+ LOG.error( "Initialization failure [" + entityName + "#" + id + "]", e );
+ throw new LazyInitializationException( e.getMessage() );
+ }
+ }
+ else if ( session.isOpenOrWaitingForAutoClose() && session.isConnected() ) {
+ target = session.immediateLoad( entityName, id );
+ initialized = true;
+ checkTargetState(session);
+ }
+ else {
+ throw new LazyInitializationException( "could not initialize proxy [" + entityName + "#" + id + "] - Session was closed or disced" );
+ }
+ }
+
+ /**
+ * Attempt to initialize the proxy without loading anything from the database.
+ *
+ * This will only have any effect if the proxy is still attached to a session,
+ * and the entity being proxied has been loaded and added to the persistence context
+ * of that session since the proxy was created.
+ */
+ public final void initializeWithoutLoadIfPossible() {
+ if ( !initialized && session != null && session.isOpenOrWaitingForAutoClose() ) {
+ final EntityKey key = session.generateEntityKey(
+ getInternalIdentifier(),
+ session.getFactory().getMetamodel().entityPersister( getEntityName() )
+ );
+ final Object entity = session.getPersistenceContextInternal().getEntity( key );
+ if ( entity != null ) {
+ setImplementation( entity );
+ }
+ }
+ }
+
+ /**
+ * Initialize internal state based on the currently attached session,
+ * in order to be ready to load data even after the proxy is detached from the session.
+ *
+ * This method only has any effect if
+ * {@link SessionFactoryOptions#isInitializeLazyStateOutsideTransactionsEnabled()} is {@code true}.
+ */
+ protected void prepareForPossibleLoadingOutsideTransaction() {
+ if ( session != null ) {
+ allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
+
+ if ( allowLoadOutsideTransaction && sessionFactoryUuid == null ) {
+ sessionFactoryUuid = session.getFactory().getUuid();
+ }
+ }
+ }
+
+ private void checkTargetState(SharedSessionContractImplementor session) {
+ if ( !unwrap ) {
+ if ( target == null ) {
+ session.getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id );
+ }
+ }
+ }
+
+ /**
+ * Getter for property 'connectedToSession'.
+ *
+ * @return Value for property 'connectedToSession'.
+ */
+ protected final boolean isConnectedToSession() {
+ return getProxyOrNull() != null;
+ }
+
+ private Object getProxyOrNull() {
+ final EntityKey entityKey = generateEntityKeyOrNull( getInternalIdentifier(), session, getEntityName() );
+ if ( entityKey != null && session != null && session.isOpenOrWaitingForAutoClose() ) {
+ return session.getPersistenceContextInternal().getProxy( entityKey );
+ }
+ return null;
+ }
+
+ @Override
+ public final Object getImplementation() {
+ initialize();
+ return target;
+ }
+
+ @Override
+ public final void setImplementation(Object target) {
+ this.target = target;
+ initialized = true;
+ }
+
+ @Override
+ public final Object getImplementation(SharedSessionContractImplementor s) throws HibernateException {
+ final EntityKey entityKey = generateEntityKeyOrNull( getInternalIdentifier(), s, getEntityName() );
+ return ( entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ) );
+ }
+
+ /**
+ * Getter for property 'target'.
+ *
+ * Same as {@link #getImplementation()} except that this method will not force initialization.
+ *
+ * @return Value for property 'target'.
+ */
+ protected final Object getTarget() {
+ return target;
+ }
+
+ @Override
+ public final boolean isReadOnlySettingAvailable() {
+ return (session != null && !session.isClosed());
+ }
+
+ private void errorIfReadOnlySettingNotAvailable() {
+ if ( session == null ) {
+ throw new TransientObjectException(
+ "Proxy [" + entityName + "#" + id + "] is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
+ );
+ }
+ if ( !session.isOpenOrWaitingForAutoClose() ) {
+ throw new SessionException(
+ "Session is closed. The read-only/modifiable setting is only accessible when the proxy [" + entityName + "#" + id + "] is associated with an open session."
+ );
+ }
+ }
+
+ @Override
+ public final boolean isReadOnly() {
+ errorIfReadOnlySettingNotAvailable();
+ return readOnly;
+ }
+
+ @Override
+ public final void setReadOnly(boolean readOnly) {
+ errorIfReadOnlySettingNotAvailable();
+ // only update if readOnly is different from current setting
+ if ( this.readOnly != readOnly ) {
+ final EntityPersister persister = session.getFactory().getEntityPersister( entityName );
+ if ( !persister.isMutable() && !readOnly ) {
+ throw new IllegalStateException( "cannot make proxies [" + entityName + "#" + id + "] for immutable entities modifiable" );
+ }
+ this.readOnly = readOnly;
+ if ( initialized ) {
+ EntityKey key = generateEntityKeyOrNull( getInternalIdentifier(), session, getEntityName() );
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ if ( key != null && persistenceContext.containsEntity( key ) ) {
+ persistenceContext.setReadOnly( target, readOnly );
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the read-only/modifiable setting that should be put in affect when it is
+ * attached to a session.
+ *
+ * This method should only be called during serialization when read-only/modifiable setting
+ * is not available (i.e., isReadOnlySettingAvailable() == false)
+ *
+ * @return null, if the default setting should be used;
+ * true, for read-only;
+ * false, for modifiable
+ *
+ * @throws IllegalStateException if isReadOnlySettingAvailable() == true
+ */
+ public final Boolean isReadOnlyBeforeAttachedToSession() {
+ if ( isReadOnlySettingAvailable() ) {
+ throw new IllegalStateException(
+ "Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true [" + entityName + "#" + id + "]"
+ );
+ }
+ return readOnlyBeforeAttachedToSession;
+ }
+
+ /**
+ * Get whether the proxy can load data even
+ * if it's not attached to a session with an ongoing transaction.
+ *
+ * This method should only be called during serialization,
+ * and only makes sense after a call to {@link #prepareForPossibleLoadingOutsideTransaction()}.
+ *
+ * @return {@code true} if out-of-transaction loads are allowed, {@code false} otherwise.
+ */
+ protected boolean isAllowLoadOutsideTransaction() {
+ return allowLoadOutsideTransaction;
+ }
+
+ /**
+ * Get the session factory UUID.
+ *
+ * This method should only be called during serialization,
+ * and only makes sense after a call to {@link #prepareForPossibleLoadingOutsideTransaction()}.
+ *
+ * @return the session factory UUID.
+ */
+ protected String getSessionFactoryUuid() {
+ return sessionFactoryUuid;
+ }
+
+ /**
+ * Restore settings that are not passed to the constructor,
+ * but are still preserved during serialization.
+ *
+ * This method should only be called during deserialization, before associating
+ * the proxy with a session.
+ *
+ * @param readOnlyBeforeAttachedToSession the read-only/modifiable setting to use when
+ * associated with a session; null indicates that the default should be used.
+ * @param sessionFactoryUuid the session factory uuid, to be used if {@code allowLoadOutsideTransaction} is {@code true}.
+ * @param allowLoadOutsideTransaction whether the proxy can load data even
+ * if it's not attached to a session with an ongoing transaction.
+ *
+ * @throws IllegalStateException if isReadOnlySettingAvailable() == true
+ */
+ /* package-private */
+ final void afterDeserialization(Boolean readOnlyBeforeAttachedToSession,
+ String sessionFactoryUuid, boolean allowLoadOutsideTransaction) {
+ if ( isReadOnlySettingAvailable() ) {
+ throw new IllegalStateException(
+ "Cannot call afterDeserialization when isReadOnlySettingAvailable == true [" + entityName + "#" + id + "]"
+ );
+ }
+ this.readOnlyBeforeAttachedToSession = readOnlyBeforeAttachedToSession;
+
+ this.sessionFactoryUuid = sessionFactoryUuid;
+ this.allowLoadOutsideTransaction = allowLoadOutsideTransaction;
+ }
+
+ @Override
+ public boolean isUnwrap() {
+ return unwrap;
+ }
+
+ @Override
+ public void setUnwrap(boolean unwrap) {
+ this.unwrap = unwrap;
+ }
+}