-
Notifications
You must be signed in to change notification settings - Fork 3.5k
/
EnhancementHelper.java
214 lines (192 loc) · 6.61 KB
/
EnhancementHelper.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.bytecode.enhance.spi.interceptor;
import java.util.Locale;
import java.util.function.BiFunction;
import org.hibernate.FlushMode;
import org.hibernate.LazyInitializationException;
import org.hibernate.bytecode.BytecodeLogger;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.SessionFactoryRegistry;
import org.hibernate.mapping.OneToOne;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
/**
* @author Steve Ebersole
*/
public class EnhancementHelper {
/**
* Should the given property be included in the owner's base fetch group?
*/
public static boolean includeInBaseFetchGroup(
Property bootMapping,
boolean isEnhanced,
boolean allowEnhancementAsProxy) {
final Value value = bootMapping.getValue();
if ( ! isEnhanced ) {
if ( value instanceof ToOne ) {
if ( ( (ToOne) value ).isUnwrapProxy() ) {
BytecodeLogger.LOGGER.debugf(
"To-one property `%s#%s` was mapped with LAZY + NO_PROXY but the class was not enhanced",
bootMapping.getPersistentClass().getEntityName(),
bootMapping.getName()
);
}
}
return true;
}
if ( value instanceof ToOne ) {
final ToOne toOne = (ToOne) value;
if ( toOne.isLazy() ) {
if ( toOne.isUnwrapProxy() ) {
if ( toOne instanceof OneToOne ) {
return false;
}
// include it in the base fetch group so long as the config allows
// using the FK to create an "enhancement proxy"
return allowEnhancementAsProxy;
}
}
return true;
}
return ! bootMapping.isLazy();
}
public static <T> T performWork(
BytecodeLazyAttributeInterceptor interceptor,
BiFunction<SharedSessionContractImplementor, Boolean, T> work,
String entityName,
String attributeName) {
SharedSessionContractImplementor session = interceptor.getLinkedSession();
boolean isTempSession = false;
boolean isJta = false;
// first figure out which Session to use
if ( session == null ) {
if ( interceptor.allowLoadOutsideTransaction() ) {
session = openTemporarySessionForLoading( interceptor, entityName, attributeName );
isTempSession = true;
}
else {
throwLazyInitializationException( Cause.NO_SESSION, entityName, attributeName );
}
}
else if ( !session.isOpen() ) {
if ( interceptor.allowLoadOutsideTransaction() ) {
session = openTemporarySessionForLoading( interceptor, entityName, attributeName );
isTempSession = true;
}
else {
throwLazyInitializationException( Cause.CLOSED_SESSION, entityName, attributeName );
}
}
else if ( !session.isConnected() ) {
if ( interceptor.allowLoadOutsideTransaction() ) {
session = openTemporarySessionForLoading( interceptor, entityName, attributeName );
isTempSession = true;
}
else {
throwLazyInitializationException( Cause.DISCONNECTED_SESSION, entityName, attributeName);
}
}
// If we are using a temporary Session, begin a transaction if necessary
if ( isTempSession ) {
BytecodeLogger.LOGGER.debug( "Enhancement interception Helper#performWork started temporary Session" );
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.
BytecodeLogger.LOGGER.debug( "Enhancement interception Helper#performWork starting transaction on temporary Session" );
session.beginTransaction();
}
}
try {
// do the actual work
return work.apply( session, isTempSession );
}
finally {
if ( isTempSession ) {
try {
// Commit the JDBC transaction is we started one.
if ( !isJta ) {
BytecodeLogger.LOGGER.debug( "Enhancement interception Helper#performWork committing transaction on temporary Session" );
session.getTransaction().commit();
}
}
catch (Exception e) {
BytecodeLogger.LOGGER.warn(
"Unable to commit JDBC transaction on temporary session used to load lazy " +
"collection associated to no session"
);
}
// Close the just opened temp Session
try {
BytecodeLogger.LOGGER.debug( "Enhancement interception Helper#performWork closing temporary Session" );
session.close();
}
catch (Exception e) {
BytecodeLogger.LOGGER.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
}
}
}
}
enum Cause {
NO_SESSION,
CLOSED_SESSION,
DISCONNECTED_SESSION,
NO_SF_UUID
}
private static void throwLazyInitializationException(Cause cause, String entityName, String attributeName) {
final String reason;
switch ( cause ) {
case NO_SESSION: {
reason = "no session and settings disallow loading outside the Session";
break;
}
case CLOSED_SESSION: {
reason = "session is closed and settings disallow loading outside the Session";
break;
}
case DISCONNECTED_SESSION: {
reason = "session is disconnected and settings disallow loading outside the Session";
break;
}
case NO_SF_UUID: {
reason = "could not determine SessionFactory UUId to create temporary Session for loading";
break;
}
default: {
reason = "<should never get here>";
}
}
final String message = String.format(
Locale.ROOT,
"Unable to perform requested lazy initialization [%s.%s] - %s",
entityName,
attributeName,
reason
);
throw new LazyInitializationException( message );
}
private static SharedSessionContractImplementor openTemporarySessionForLoading(
BytecodeLazyAttributeInterceptor interceptor,
String entityName,
String attributeName) {
if ( interceptor.getSessionFactoryUuid() == null ) {
throwLazyInitializationException( Cause.NO_SF_UUID, entityName, attributeName );
}
final SessionFactoryImplementor sf = (SessionFactoryImplementor)
SessionFactoryRegistry.INSTANCE.getSessionFactory( interceptor.getSessionFactoryUuid() );
final SharedSessionContractImplementor session = (SharedSessionContractImplementor) sf.openSession();
session.getPersistenceContext().setDefaultReadOnly( true );
session.setHibernateFlushMode( FlushMode.MANUAL );
return session;
}
}