Skip to content

Commit 14aefb3

Browse files
committed
HHH-19536 Review and rationalize memory layout of AbstractInterceptor
1 parent eaf01a3 commit 14aefb3

File tree

6 files changed

+140
-14
lines changed

6 files changed

+140
-14
lines changed

hibernate-core/src/main/java/org/hibernate/bytecode/enhance/spi/interceptor/AbstractInterceptor.java

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@
1010
* @author Steve Ebersole
1111
*/
1212
public abstract class AbstractInterceptor implements SessionAssociableInterceptor {
13-
private final String entityName;
1413

15-
private transient SharedSessionContractImplementor session;
16-
private boolean allowLoadOutsideTransaction;
17-
private String sessionFactoryUuid;
14+
private final String entityName;
15+
private SessionAssociationMarkers sessionAssociation;
1816

1917
public AbstractInterceptor(String entityName) {
2018
this.entityName = entityName;
@@ -26,33 +24,46 @@ public String getEntityName() {
2624

2725
@Override
2826
public SharedSessionContractImplementor getLinkedSession() {
29-
return session;
27+
return this.sessionAssociation != null ? this.sessionAssociation.session : null;
3028
}
3129

3230
@Override
3331
public void setSession(SharedSessionContractImplementor session) {
34-
this.session = session;
35-
if ( session != null && !allowLoadOutsideTransaction ) {
36-
this.allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions().isInitializeLazyStateOutsideTransactionsEnabled();
37-
if ( this.allowLoadOutsideTransaction ) {
38-
this.sessionFactoryUuid = session.getFactory().getUuid();
39-
}
32+
if ( session != null ) {
33+
this.sessionAssociation = session.getSessionAssociationMarkers();
34+
}
35+
else {
36+
unsetSession();
4037
}
4138
}
4239

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

4849
@Override
4950
public boolean allowLoadOutsideTransaction() {
50-
return allowLoadOutsideTransaction;
51+
if ( this.sessionAssociation != null ) {
52+
return this.sessionAssociation.allowLoadOutsideTransaction;
53+
}
54+
else {
55+
return false;
56+
}
5157
}
5258

5359
@Override
5460
public String getSessionFactoryUuid() {
55-
return sessionFactoryUuid;
61+
if ( this.sessionAssociation != null ) {
62+
return this.sessionAssociation.sessionFactoryUuid;
63+
}
64+
else {
65+
return null;
66+
}
5667
}
5768

5869
/**
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.bytecode.enhance.spi.interceptor;
6+
7+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
8+
9+
/**
10+
* Contains the state necessary to properly implement SessionAssociableInterceptor;
11+
* this allows reuse of this particular object across multiple interceptor instances
12+
* and keeps them from getting too allocation-intensive.
13+
* It also allows for a very elegant, simple way for the Session to clear references
14+
* to itself on close.
15+
*/
16+
public final class SessionAssociationMarkers {
17+
18+
private static final SessionAssociationMarkers NON_ASSOCIATED = new SessionAssociationMarkers();
19+
20+
final boolean allowLoadOutsideTransaction;
21+
final String sessionFactoryUuid;
22+
transient SharedSessionContractImplementor session; //TODO by resetting this one field on Session#close we might be able to avoid iterating on all managed instances to de-associate them? Only if we guarantee all managed types use this.
23+
24+
public SessionAssociationMarkers(final SharedSessionContractImplementor session) {
25+
this.allowLoadOutsideTransaction = session.getFactory().getSessionFactoryOptions()
26+
.isInitializeLazyStateOutsideTransactionsEnabled();
27+
if ( this.allowLoadOutsideTransaction ) {
28+
this.sessionFactoryUuid = session.getFactory().getUuid();
29+
}
30+
else {
31+
this.sessionFactoryUuid = null;
32+
}
33+
this.session = session;
34+
}
35+
36+
/**
37+
* Constructor for the singleton representing non-associated
38+
* state.
39+
*/
40+
private SessionAssociationMarkers() {
41+
this.allowLoadOutsideTransaction = false;
42+
this.sessionFactoryUuid = null;
43+
this.session = null;
44+
}
45+
46+
/**
47+
* Copying constructor for when we're allowed to load outside of transactions
48+
* and need to transparently reassociated to the SessionFactory having the
49+
* specified UUID.
50+
* @param sessionFactoryUuid
51+
*/
52+
private SessionAssociationMarkers(String sessionFactoryUuid) {
53+
this.allowLoadOutsideTransaction = true;
54+
this.sessionFactoryUuid = sessionFactoryUuid;
55+
this.session = null;
56+
}
57+
58+
public SessionAssociationMarkers deAssociatedCopy() {
59+
if ( allowLoadOutsideTransaction ) {
60+
return new SessionAssociationMarkers( sessionFactoryUuid );
61+
}
62+
else {
63+
return NON_ASSOCIATED;
64+
}
65+
}
66+
67+
/**
68+
* Careful as this mutates the state of this instance, which is possibly
69+
* used by multiple managed entities.
70+
* Removes the reference to the session; useful on Session close.
71+
*/
72+
public void sessionClosed() {
73+
this.session = null;
74+
}
75+
76+
}

hibernate-core/src/main/java/org/hibernate/engine/spi/SessionDelegatorBaseImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.hibernate.Transaction;
4141
import org.hibernate.UnknownProfileException;
4242
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
43+
import org.hibernate.bytecode.enhance.spi.interceptor.SessionAssociationMarkers;
4344
import org.hibernate.cache.spi.CacheTransactionSynchronization;
4445
import org.hibernate.collection.spi.PersistentCollection;
4546
import org.hibernate.engine.jdbc.LobCreator;
@@ -1237,6 +1238,11 @@ public Object loadFromSecondLevelCache(EntityPersister persister, EntityKey enti
12371238
return delegate.loadFromSecondLevelCache( persister, entityKey, instanceToLoad, lockMode );
12381239
}
12391240

1241+
@Override
1242+
public SessionAssociationMarkers getSessionAssociationMarkers() {
1243+
return delegate.getSessionAssociationMarkers();
1244+
}
1245+
12401246
@Override
12411247
public boolean isIdentifierRollbackEnabled() {
12421248
return delegate.isIdentifierRollbackEnabled();

hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionContractImplementor.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import org.hibernate.LockOptions;
1818
import org.hibernate.StatelessSession;
1919
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
20+
import org.hibernate.bytecode.enhance.spi.interceptor.SessionAssociationMarkers;
2021
import org.hibernate.dialect.Dialect;
2122
import org.hibernate.event.spi.EventSource;
2223
import org.hibernate.query.Query;
@@ -572,4 +573,15 @@ default boolean isStatelessSession() {
572573
*/
573574
@Incubating
574575
Object loadFromSecondLevelCache(EntityPersister persister, EntityKey entityKey, Object instanceToLoad, LockMode lockMode);
576+
577+
/**
578+
* Wrap all state that lazy loading interceptors might need to
579+
* manage association with this session, or to handle lazy loading
580+
* after detachment via the UUID of the SessionFactory.
581+
* N.B. this captures the current Session, however it can get
582+
* updated to a null session (for detached entities) or updated to
583+
* a different Session.
584+
*/
585+
@Incubating
586+
SessionAssociationMarkers getSessionAssociationMarkers();
575587
}

hibernate-core/src/main/java/org/hibernate/engine/spi/SharedSessionDelegatorBaseImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.hibernate.SharedSessionContract;
2121
import org.hibernate.Transaction;
2222
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
23+
import org.hibernate.bytecode.enhance.spi.interceptor.SessionAssociationMarkers;
2324
import org.hibernate.cache.spi.CacheTransactionSynchronization;
2425
import org.hibernate.collection.spi.PersistentCollection;
2526
import org.hibernate.engine.jdbc.LobCreator;
@@ -693,6 +694,11 @@ public Object loadFromSecondLevelCache(EntityPersister persister, EntityKey enti
693694
return delegate.loadFromSecondLevelCache( persister, entityKey, instanceToLoad, lockMode );
694695
}
695696

697+
@Override
698+
public SessionAssociationMarkers getSessionAssociationMarkers() {
699+
return delegate.getSessionAssociationMarkers();
700+
}
701+
696702
@Override
697703
public boolean isIdentifierRollbackEnabled() {
698704
return delegate.isIdentifierRollbackEnabled();

hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.hibernate.UnknownEntityTypeException;
2727
import org.hibernate.binder.internal.TenantIdBinder;
2828
import org.hibernate.boot.spi.SessionFactoryOptions;
29+
import org.hibernate.bytecode.enhance.spi.interceptor.SessionAssociationMarkers;
2930
import org.hibernate.cache.spi.CacheTransactionSynchronization;
3031
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
3132
import org.hibernate.dialect.Dialect;
@@ -179,6 +180,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
179180

180181
//Lazily initialized
181182
private transient ExceptionConverter exceptionConverter;
183+
private transient SessionAssociationMarkers sessionAssociationMarkers;
182184

183185
public AbstractSharedSessionContract(SessionFactoryImpl factory, SessionCreationOptions options) {
184186
this.factory = factory;
@@ -476,6 +478,11 @@ public void close() {
476478
removeSharedSessionTransactionObserver( transactionCoordinator );
477479
}
478480

481+
if ( sessionAssociationMarkers != null ) {
482+
sessionAssociationMarkers.sessionClosed();
483+
sessionAssociationMarkers = null;
484+
}
485+
479486
try {
480487
if ( shouldCloseJdbcCoordinatorOnClose( isTransactionCoordinatorShared ) ) {
481488
jdbcCoordinator.close();
@@ -1586,6 +1593,14 @@ public FormatMapper getJsonFormatMapper() {
15861593
return factoryOptions.getJsonFormatMapper();
15871594
}
15881595

1596+
@Override
1597+
public SessionAssociationMarkers getSessionAssociationMarkers() {
1598+
if ( this.sessionAssociationMarkers == null ) {
1599+
this.sessionAssociationMarkers = new SessionAssociationMarkers( this );
1600+
}
1601+
return sessionAssociationMarkers;
1602+
}
1603+
15891604
@Serial
15901605
private void writeObject(ObjectOutputStream oos) throws IOException {
15911606
if ( log.isTraceEnabled() ) {

0 commit comments

Comments
 (0)