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.engine.spi.EntityKey;
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;
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;
protected AbstractLazyInitializer() {
}
protected AbstractLazyInitializer(String entityName, Serializable id, SharedSessionContractImplementor session) {
this.entityName = entityName;
this.id = id;
if ( session == null ) {
unsetSession();
}
else {
setSession( session );
}
}
@Override
public final String getEntityName() {
return entityName;
}
@Override
public final Serializable getIdentifier() {
if ( isUninitialized() && isInitializeProxyWhenAccessingIdentifier() ) {
initialize();
}
return id;
}
private boolean isInitializeProxyWhenAccessingIdentifier() {
return session != null && session.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 ) {
if ( s == null ) {
unsetSession();
}
else if ( isConnectedToSession() ) {
LOG.attemptToAssociateProxyWithTwoOpenSessions(
entityName,
id
);
throw new HibernateException( "illegally attempted to associate proxy [" + entityName + "#" + id + "] with two open Sessions" );
}
else {
session = s;
if ( readOnlyBeforeAttachedToSession == null ) {
final EntityPersister persister = s.getFactory().getEntityPersister( entityName );
setReadOnly( s.getPersistenceContext().isDefaultReadOnly() || !persister.isMutable() );
}
else {
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.isOpen() ) {
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 ) {
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 ) {
session.beginTransaction();
}
try {
target = session.immediateLoad( entityName, id );
initialized = true;
checkTargetState(session);
}
finally {
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.isOpen() && 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" );
}
}
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 );
}
}
}
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(SharedSessionContractImplementor s) throws HibernateException {
final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), s, getEntityName() );
return (entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ));
}
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.isClosed() ) {
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();
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( getIdentifier(), session, getEntityName() );
if ( key != null && session.getPersistenceContext().containsEntity( key ) ) {
session.getPersistenceContext().setReadOnly( target, readOnly );
}
}
}
}
protected final Boolean isReadOnlyBeforeAttachedToSession() {
if ( isReadOnlySettingAvailable() ) {
throw new IllegalStateException(
"Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true [" + entityName + "#" + id + "]"
);
}
return readOnlyBeforeAttachedToSession;
}
final void setReadOnlyBeforeAttachedToSession(Boolean readOnlyBeforeAttachedToSession) {
if ( isReadOnlySettingAvailable() ) {
throw new IllegalStateException(
"Cannot call setReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true [" + entityName + "#" + id + "]"
);
}
this.readOnlyBeforeAttachedToSession = readOnlyBeforeAttachedToSession;
}
@Override
public boolean isUnwrap() {
return unwrap;
}
@Override
public void setUnwrap(boolean unwrap) {
this.unwrap = unwrap;
}
}