package org.hibernate.loader.plan.exec.process.internal;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.StaleObjectStateException;
import org.hibernate.WrongClassException;
import org.hibernate.engine.internal.TwoPhaseLoad;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.EntityUniqueKey;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.EntityAliases;
import org.hibernate.loader.plan.exec.process.spi.EntityReferenceInitializer;
import org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext;
import org.hibernate.loader.plan.exec.spi.EntityReferenceAliases;
import org.hibernate.loader.plan.spi.EntityFetch;
import org.hibernate.loader.plan.spi.EntityReference;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Loadable;
import org.hibernate.persister.entity.UniqueKeyLoadable;
import org.hibernate.pretty.MessageHelper;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.VersionType;
import org.jboss.logging.Logger;
import static org.hibernate.loader.plan.exec.process.spi.ResultSetProcessingContext.EntityReferenceProcessingState;
public class EntityReferenceInitializerImpl implements EntityReferenceInitializer {
private static final Logger log = CoreLogging.logger( EntityReferenceInitializerImpl.class );
private final EntityReference entityReference;
private final EntityReferenceAliases entityReferenceAliases;
private final boolean isReturn;
public EntityReferenceInitializerImpl(
EntityReference entityReference,
EntityReferenceAliases entityReferenceAliases) {
this( entityReference, entityReferenceAliases, false );
}
public EntityReferenceInitializerImpl(
EntityReference entityReference,
EntityReferenceAliases entityReferenceAliases,
boolean isRoot) {
this.entityReference = entityReference;
this.entityReferenceAliases = entityReferenceAliases;
isReturn = isRoot;
}
@Override
public EntityReference getEntityReference() {
return entityReference;
}
@Override
public void hydrateIdentifier(ResultSet resultSet, ResultSetProcessingContextImpl context) throws SQLException {
final EntityReferenceProcessingState processingState = context.getProcessingState( entityReference );
Object identifierHydratedForm = processingState.getIdentifierHydratedForm();
if ( identifierHydratedForm == null ) {
identifierHydratedForm = readIdentifierHydratedState( resultSet, context );
processingState.registerIdentifierHydratedForm( identifierHydratedForm );
}
}
private Object readIdentifierHydratedState(ResultSet resultSet, ResultSetProcessingContext context)
throws SQLException {
try {
return entityReference.getEntityPersister().getIdentifierType().hydrate(
resultSet,
entityReferenceAliases.getColumnAliases().getSuffixedKeyAliases(),
context.getSession(),
null
);
}
catch (Exception e) {
throw new HibernateException(
"Encountered problem trying to hydrate identifier for entity ["
+ entityReference.getEntityPersister() + "]",
e
);
}
}
@Override
public void resolveEntityKey(ResultSet resultSet, ResultSetProcessingContextImpl context) {
final EntityReferenceProcessingState processingState = context.getProcessingState( entityReference );
final EntityKey entityKey = processingState.getEntityKey();
if ( entityKey != null ) {
log.debugf(
"On call to EntityIdentifierReaderImpl#resolve, EntityKey was already known; " +
"should only happen on root returns with an optional identifier specified"
);
return;
}
final Object identifierHydratedForm = processingState.getIdentifierHydratedForm();
if ( identifierHydratedForm == null ) {
return;
}
final Type identifierType = entityReference.getEntityPersister().getIdentifierType();
final Serializable resolvedId = (Serializable) identifierType.resolve(
identifierHydratedForm,
context.getSession(),
null
);
if ( resolvedId != null ) {
processingState.registerEntityKey(
context.getSession().generateEntityKey( resolvedId, entityReference.getEntityPersister() )
);
}
}
@Override
public void hydrateEntityState(ResultSet resultSet, ResultSetProcessingContextImpl context) {
final EntityReferenceProcessingState processingState = context.getProcessingState( entityReference );
if ( processingState.isMissingIdentifier() ) {
handleMissingIdentifier( context );
return;
}
final EntityKey entityKey = processingState.getEntityKey();
if ( entityKey == null ) {
handleMissingIdentifier( context );
return;
}
if ( processingState.getEntityInstance() != null ) {
return;
}
final Object existing = context.getSession().getEntityUsingInterceptor( entityKey );
if ( existing != null ) {
if ( ! entityReference.getEntityPersister().isInstance( existing ) ) {
throw new WrongClassException(
"loaded object was of wrong class " + existing.getClass(),
entityKey.getIdentifier(),
entityReference.getEntityPersister().getEntityName()
);
}
checkVersion( resultSet, context, entityKey, existing );
processingState.registerEntityInstance( existing );
return;
}
Object entityInstance = null;
if ( isReturn &&
context.shouldUseOptionalEntityInformation() &&
context.getQueryParameters().getOptionalObject() != null ) {
final EntityKey optionalEntityKey = ResultSetProcessorHelper.getOptionalObjectKey(
context.getQueryParameters(),
context.getSession()
);
if ( optionalEntityKey != null && optionalEntityKey.equals( entityKey ) ) {
entityInstance = context.getQueryParameters().getOptionalObject();
}
}
final String concreteEntityTypeName = getConcreteEntityTypeName( resultSet, context, entityKey );
if ( entityInstance == null ) {
entityInstance = context.getSession().instantiate( concreteEntityTypeName, entityKey.getIdentifier() );
}
processingState.registerEntityInstance( entityInstance );
log.trace( "hydrating entity state" );
final LockMode requestedLockMode = context.resolveLockMode( entityReference );
final LockMode lockModeToAcquire = requestedLockMode == LockMode.NONE
? LockMode.READ
: requestedLockMode;
loadFromResultSet(
resultSet,
context,
entityInstance,
concreteEntityTypeName,
entityKey,
lockModeToAcquire
);
}
private void handleMissingIdentifier(ResultSetProcessingContext context) {
if ( EntityFetch.class.isInstance( entityReference ) ) {
final EntityFetch fetch = (EntityFetch) entityReference;
final EntityType fetchedType = fetch.getFetchedType();
if ( ! fetchedType.isOneToOne() ) {
return;
}
final EntityReferenceProcessingState fetchOwnerState = context.getOwnerProcessingState( fetch );
if ( fetchOwnerState == null ) {
throw new IllegalStateException( "Could not locate fetch owner state" );
}
final EntityKey ownerEntityKey = fetchOwnerState.getEntityKey();
if ( ownerEntityKey != null ) {
context.getSession().getPersistenceContext().addNullProperty(
ownerEntityKey,
fetchedType.getPropertyName()
);
}
}
}
private void loadFromResultSet(
ResultSet resultSet,
ResultSetProcessingContext context,
Object entityInstance,
String concreteEntityTypeName,
EntityKey entityKey,
LockMode lockModeToAcquire) {
final Serializable id = entityKey.getIdentifier();
final Loadable concreteEntityPersister = (Loadable) context.getSession().getFactory().getMetamodel().entityPersister( concreteEntityTypeName );
if ( log.isTraceEnabled() ) {
log.tracev(
"Initializing object from ResultSet: {0}",
MessageHelper.infoString(
concreteEntityPersister,
id,
context.getSession().getFactory()
)
);
}
TwoPhaseLoad.addUninitializedEntity(
entityKey,
entityInstance,
concreteEntityPersister,
lockModeToAcquire,
context.getSession()
);
final EntityPersister rootEntityPersister = context.getSession().getFactory().getMetamodel().entityPersister(
concreteEntityPersister.getRootEntityName()
);
final Object[] values;
try {
values = concreteEntityPersister.hydrate(
resultSet,
id,
entityInstance,
(Loadable) entityReference.getEntityPersister(),
concreteEntityPersister == rootEntityPersister
? entityReferenceAliases.getColumnAliases().getSuffixedPropertyAliases()
: entityReferenceAliases.getColumnAliases().getSuffixedPropertyAliases( concreteEntityPersister ),
context.getLoadPlan().areLazyAttributesForceFetched(),
context.getSession()
);
context.getProcessingState( entityReference ).registerHydratedState( values );
}
catch (SQLException e) {
throw context.getSession().getFactory().getServiceRegistry().getService( JdbcServices.class ).getSqlExceptionHelper().convert(
e,
"Could not read entity state from ResultSet : " + entityKey
);
}
final Object rowId;
try {
rowId = concreteEntityPersister.hasRowId()
? resultSet.getObject( entityReferenceAliases.getColumnAliases().getRowIdAlias() )
: null;
if ( rowId != null && log.isTraceEnabled() ) {
log.tracev(
"extracted ROWID value: {0}",
rowId
);
}
}
catch (SQLException e) {
throw context.getSession().getFactory().getServiceRegistry().getService( JdbcServices.class ).getSqlExceptionHelper().convert(
e,
"Could not read entity row-id from ResultSet : " + entityKey
);
}
final EntityType entityType = EntityFetch.class.isInstance( entityReference )
? ( (EntityFetch) entityReference ).getFetchedType()
: entityReference.getEntityPersister().getEntityMetamodel().getEntityType();
if ( entityType != null ) {
String ukName = entityType.getRHSUniqueKeyPropertyName();
if ( ukName != null ) {
final int index = ( (UniqueKeyLoadable) concreteEntityPersister ).getPropertyIndex( ukName );
final Type type = concreteEntityPersister.getPropertyTypes()[index];
EntityUniqueKey euk = new EntityUniqueKey(
entityReference.getEntityPersister().getEntityName(),
ukName,
type.semiResolve( values[index], context.getSession(), entityInstance ),
type,
concreteEntityPersister.getEntityMode(),
context.getSession().getFactory()
);
context.getSession().getPersistenceContext().addEntity( euk, entityInstance );
}
}
TwoPhaseLoad.postHydrate(
concreteEntityPersister,
id,
values,
rowId,
entityInstance,
lockModeToAcquire,
context.getSession()
);
context.registerHydratedEntity( entityReference, entityKey, entityInstance );
}
private String getConcreteEntityTypeName(
ResultSet resultSet,
ResultSetProcessingContext context,
EntityKey entityKey) {
final Loadable loadable = (Loadable) entityReference.getEntityPersister();
if ( ! loadable.hasSubclasses() ) {
return entityReference.getEntityPersister().getEntityName();
}
final Object discriminatorValue;
try {
discriminatorValue = loadable.getDiscriminatorType().nullSafeGet(
resultSet,
entityReferenceAliases.getColumnAliases().getSuffixedDiscriminatorAlias(),
context.getSession(),
null
);
}
catch (SQLException e) {
throw context.getSession().getFactory().getServiceRegistry().getService( JdbcServices.class ).getSqlExceptionHelper().convert(
e,
"Could not read discriminator value from ResultSet"
);
}
final String result = loadable.getSubclassForDiscriminatorValue( discriminatorValue );
if ( result == null ) {
throw new WrongClassException(
"Discriminator: " + discriminatorValue,
entityKey.getIdentifier(),
entityReference.getEntityPersister().getEntityName()
);
}
return result;
}
private void checkVersion(
ResultSet resultSet,
ResultSetProcessingContext context,
EntityKey entityKey,
Object existing) {
final LockMode requestedLockMode = context.resolveLockMode( entityReference );
if ( requestedLockMode != LockMode.NONE ) {
final LockMode currentLockMode = context.getSession().getPersistenceContext().getEntry( existing ).getLockMode();
final boolean isVersionCheckNeeded = entityReference.getEntityPersister().isVersioned()
&& currentLockMode.lessThan( requestedLockMode );
if ( isVersionCheckNeeded ) {
checkVersion(
context.getSession(),
resultSet,
entityReference.getEntityPersister(),
entityReferenceAliases.getColumnAliases(),
entityKey,
existing
);
context.getSession().getPersistenceContext().getEntry( existing ).setLockMode( requestedLockMode );
}
}
}
private void checkVersion(
SharedSessionContractImplementor session,
ResultSet resultSet,
EntityPersister persister,
EntityAliases entityAliases,
EntityKey entityKey,
Object entityInstance) {
final Object version = session.getPersistenceContext().getEntry( entityInstance ).getVersion();
if ( version != null ) {
VersionType versionType = persister.getVersionType();
final Object currentVersion;
try {
currentVersion = versionType.nullSafeGet(
resultSet,
entityAliases.getSuffixedVersionAliases(),
session,
null
);
}
catch (SQLException e) {
throw session.getFactory().getServiceRegistry().getService( JdbcServices.class ).getSqlExceptionHelper().convert(
e,
"Could not read version value from result set"
);
}
if ( !versionType.isEqual( version, currentVersion ) ) {
if ( session.getFactory().getStatistics().isStatisticsEnabled() ) {
session.getFactory().getStatistics().optimisticFailure( persister.getEntityName() );
}
throw new StaleObjectStateException( persister.getEntityName(), entityKey.getIdentifier() );
}
}
}
@Override
public void finishUpRow(ResultSet resultSet, ResultSetProcessingContextImpl context) {
}
}