Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,54 @@
* @author Steve Ebersole
*/
public abstract class AbstractInterceptor implements SessionAssociableInterceptor {
private final String entityName;

private transient SharedSessionContractImplementor session;
private boolean allowLoadOutsideTransaction;
private String sessionFactoryUuid;
private SessionAssociationMarkers sessionAssociation;

public AbstractInterceptor(String entityName) {
this.entityName = entityName;
}

public String getEntityName() {
return entityName;
protected AbstractInterceptor() {
}

@Override
public SharedSessionContractImplementor getLinkedSession() {
return session;
return this.sessionAssociation != null ? this.sessionAssociation.session : null;
}

@Override
public void setSession(SharedSessionContractImplementor session) {
this.session = session;
if ( session != null && !allowLoadOutsideTransaction ) {
this.allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
if ( this.allowLoadOutsideTransaction ) {
this.sessionFactoryUuid = session.getFactory().getUuid();
}
if ( session != null ) {
this.sessionAssociation = session.getSessionAssociationMarkers();
}
else {
unsetSession();
}
}

@Override
public void unsetSession() {
this.session = null;
if ( this.sessionAssociation != null ) {
//We shouldn't mutate the original instance as it's shared across multiple entities,
//but we can get a version of it which represents the same state except it doesn't have the session set:
this.sessionAssociation = this.sessionAssociation.deAssociatedCopy();
}
}

@Override
public boolean allowLoadOutsideTransaction() {
return allowLoadOutsideTransaction;
if ( this.sessionAssociation != null ) {
return this.sessionAssociation.allowLoadOutsideTransaction;
}
else {
return false;
}
}

@Override
public String getSessionFactoryUuid() {
return sessionFactoryUuid;
if ( this.sessionAssociation != null ) {
return this.sessionAssociation.sessionFactoryUuid;
}
else {
return null;
}
}

/**
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -18,68 +18,35 @@
import org.hibernate.type.CompositeType;
import org.hibernate.type.Type;

import static java.util.Collections.unmodifiableSet;
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
import static org.hibernate.engine.internal.ManagedTypeHelper.asSelfDirtinessTracker;
import static org.hibernate.engine.internal.ManagedTypeHelper.isSelfDirtinessTrackerType;
import static org.hibernate.internal.util.collections.CollectionHelper.toSmallSet;

/**
* @author Steve Ebersole
*/
public class EnhancementAsProxyLazinessInterceptor extends AbstractLazyLoadInterceptor {
private final Set<String> identifierAttributeNames;
private final CompositeType nonAggregatedCidMapper;
public class EnhancementAsProxyLazinessInterceptor extends AbstractInterceptor implements BytecodeLazyAttributeInterceptor {

private final EntityKey entityKey;

private final boolean inLineDirtyChecking;
private final EntityRelatedState meta;
private Set<String> writtenFieldNames;
private Set<String> collectionAttributeNames;

private Status status;

private final boolean initializeBeforeWrite;

public EnhancementAsProxyLazinessInterceptor(
String entityName,
Set<String> identifierAttributeNames,
CompositeType nonAggregatedCidMapper,
EntityRelatedState meta,
EntityKey entityKey,
SharedSessionContractImplementor session) {
super( entityName, session );

this.identifierAttributeNames = identifierAttributeNames;
assert identifierAttributeNames != null;

this.nonAggregatedCidMapper = nonAggregatedCidMapper;
assert nonAggregatedCidMapper != null || identifierAttributeNames.size() == 1;

this.entityKey = entityKey;

final EntityPersister entityPersister =
session.getFactory().getMappingMetamodel()
.getEntityDescriptor( entityName );
if ( entityPersister.hasCollections() ) {
final Type[] propertyTypes = entityPersister.getPropertyTypes();
final String[] propertyNames = entityPersister.getPropertyNames();
collectionAttributeNames = new HashSet<>();
for ( int i = 0; i < propertyTypes.length; i++ ) {
Type propertyType = propertyTypes[i];
if ( propertyType instanceof CollectionType ) {
collectionAttributeNames.add( propertyNames[i] );
}
}
}

this.inLineDirtyChecking = isSelfDirtinessTrackerType( entityPersister.getMappedClass() );
// if self-dirty tracking is enabled but DynamicUpdate is not enabled then we need to
// initialize the entity because the precomputed update statement contains even not
// dirty properties. And so we need all the values we have to initialize. Or, if it's
// versioned, we need to fetch the current version.
initializeBeforeWrite =
!inLineDirtyChecking
|| !entityPersister.getEntityMetamodel().isDynamicUpdate()
|| entityPersister.isVersioned();
this.meta = meta;
status = Status.UNINITIALIZED;
setSession( session );
}

@Override
public String getEntityName() {
return meta.entityName;
}

public EntityKey getEntityKey() {
Expand All @@ -95,7 +62,7 @@ protected Object handleRead(Object target, String attributeName, Object value) {

// the attribute being read is an entity-id attribute
// - we already know the id, return that
if ( identifierAttributeNames.contains( attributeName ) ) {
if ( meta.identifierAttributeNames.contains( attributeName ) ) {
return extractIdValue( target, attributeName );
}

Expand All @@ -113,14 +80,10 @@ private Object read(
final Object[] writtenAttributeValues;
final AttributeMapping[] writtenAttributeMappings;

final EntityPersister entityPersister =
session.getFactory().getMappingMetamodel()
.getEntityDescriptor( getEntityName() );

if ( writtenFieldNames != null && !writtenFieldNames.isEmpty() ) {

// enhancement has dirty-tracking available and at least one attribute was explicitly set

final EntityPersister entityPersister = meta.persister;
if ( writtenFieldNames.contains( attributeName ) ) {
// the requested attribute was one of the attributes explicitly set,
// we can just return the explicitly-set value
Expand Down Expand Up @@ -155,7 +118,7 @@ private Object read(
for ( int i = 0; i < writtenAttributeMappings.length; i++ ) {
final AttributeMapping attribute = writtenAttributeMappings[i];
attribute.setValue(target, writtenAttributeValues[i] );
if ( inLineDirtyChecking ) {
if ( meta.inLineDirtyChecking ) {
asSelfDirtinessTracker(target).$$_hibernate_trackChange( attribute.getAttributeName() );
}
}
Expand All @@ -167,6 +130,7 @@ private Object read(

private Object extractIdValue(Object target, String attributeName) {
// access to the id or part of it for non-aggregated cid
final CompositeType nonAggregatedCidMapper = meta.nonAggregatedCidMapper;
if ( nonAggregatedCidMapper == null ) {
return getIdentifier();
}
Expand Down Expand Up @@ -211,17 +175,13 @@ public Object forceInitialize(
);
}

final EntityPersister persister =
session.getFactory().getMappingMetamodel()
.getEntityDescriptor( getEntityName() );

if ( isTemporarySession ) {
// Add an entry for this entity in the PC of the temp Session
session.getPersistenceContext()
.addEnhancedProxy( entityKey, asPersistentAttributeInterceptable( target ) );
}

return persister.initializeEnhancedEntityUsedAsProxy( target, attributeName, session );
return meta.persister.initializeEnhancedEntityUsedAsProxy( target, attributeName, session );
}

@Override
Expand All @@ -230,19 +190,19 @@ protected Object handleWrite(Object target, String attributeName, Object oldValu
throw new IllegalStateException( "EnhancementAsProxyLazinessInterceptor interception on an initialized instance" );
}

if ( identifierAttributeNames.contains( attributeName ) ) {
if ( meta.identifierAttributeNames.contains( attributeName ) ) {
// it is illegal for the identifier value to be changed. Normally Hibernate
// validates this during flush. However, here it's dangerous to just allow the
// new value to be set and continue on waiting for the flush for validation
// because this interceptor manages the entity's entry in the PC itself. So
// just do the check here up-front
final boolean changed;
if ( nonAggregatedCidMapper == null ) {
if ( meta.nonAggregatedCidMapper == null ) {
changed = ! entityKey.getPersister().getIdentifierType().isEqual( oldValue, newValue );
}
else {
final int subAttrIndex = nonAggregatedCidMapper.getPropertyIndex( attributeName );
final Type subAttrType = nonAggregatedCidMapper.getSubtypes()[subAttrIndex];
final int subAttrIndex = meta.nonAggregatedCidMapper.getPropertyIndex( attributeName );
final Type subAttrType = meta.nonAggregatedCidMapper.getSubtypes()[subAttrIndex];
changed = ! subAttrType.isEqual( oldValue, newValue );
}

Expand All @@ -255,8 +215,8 @@ protected Object handleWrite(Object target, String attributeName, Object oldValu
return newValue;
}

if ( initializeBeforeWrite
|| collectionAttributeNames != null && collectionAttributeNames.contains( attributeName ) ) {
if ( meta.initializeBeforeWrite
|| meta.collectionAttributeNames != null && meta.collectionAttributeNames.contains( attributeName ) ) {
// we need to force-initialize the proxy - the fetch group to which the `attributeName` belongs
try {
forceInitialize( target, attributeName );
Expand All @@ -265,7 +225,7 @@ protected Object handleWrite(Object target, String attributeName, Object oldValu
setInitialized();
}

if ( inLineDirtyChecking ) {
if ( meta.inLineDirtyChecking ) {
asSelfDirtinessTracker( target ).$$_hibernate_trackChange( attributeName );
}
}
Expand Down Expand Up @@ -303,7 +263,7 @@ public boolean isAttributeLoaded(String fieldName) {
throw new UnsupportedOperationException( "Call to EnhancementAsProxyLazinessInterceptor#isAttributeLoaded on an interceptor which is marked as initialized" );
}
// Only fields from the identifier are loaded (until it's initialized)
return identifierAttributeNames.contains( fieldName );
return meta.identifierAttributeNames.contains( fieldName );
}

@Override
Expand Down Expand Up @@ -345,4 +305,58 @@ private enum Status {
INITIALIZING,
INITIALIZED
}

/**
* This is an helper object to group all state which relates to a particular entity type,
* and which is needed for this interceptor.
* Grouping such state allows for upfront construction as a per-entity singleton:
* this reduces processing work on creation of an interceptor instance and is more
* efficient from a point of view of memory usage and memory layout.
*/
public static class EntityRelatedState {

private final String entityName;
private final CompositeType nonAggregatedCidMapper;
private final Set<String> identifierAttributeNames;
private final Set<String> collectionAttributeNames;
private final boolean initializeBeforeWrite;
private final boolean inLineDirtyChecking;
private final EntityPersister persister;

public EntityRelatedState(EntityPersister persister,
CompositeType nonAggregatedCidMapper,
Set<String> identifierAttributeNames) {
this.identifierAttributeNames = identifierAttributeNames;
this.entityName = persister.getEntityName();
this.nonAggregatedCidMapper = nonAggregatedCidMapper;
this.persister = persister;
assert nonAggregatedCidMapper != null || identifierAttributeNames.size() == 1;

if ( persister.hasCollections() ) {
final Set<String> tmpCollectionAttributeNames = new HashSet<>();
final Type[] propertyTypes = persister.getPropertyTypes();
final String[] propertyNames = persister.getPropertyNames();
for ( int i = 0; i < propertyTypes.length; i++ ) {
Type propertyType = propertyTypes[i];
if ( propertyType instanceof CollectionType ) {
tmpCollectionAttributeNames.add( propertyNames[i] );
}
}
this.collectionAttributeNames = toSmallSet( unmodifiableSet( tmpCollectionAttributeNames ) );
}
else {
this.collectionAttributeNames = Collections.emptySet();
}

this.inLineDirtyChecking = isSelfDirtinessTrackerType( persister.getMappedClass() );
// if self-dirty tracking is enabled but DynamicUpdate is not enabled then we need to
// initialize the entity because the precomputed update statement contains even not
// dirty properties. And so we need all the values we have to initialize. Or, if it's
// versioned, we need to fetch the current version.
this.initializeBeforeWrite =
!inLineDirtyChecking
|| !persister.getEntityMetamodel().isDynamicUpdate()
|| persister.isVersioned();
}
}
}
Loading