1
1
/*
2
- * Copyright 2002-2023 the original author or authors.
2
+ * Copyright 2002-2024 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
18
18
19
19
import java .sql .Connection ;
20
20
import java .sql .SQLException ;
21
+ import java .util .concurrent .locks .Lock ;
22
+ import java .util .concurrent .locks .ReentrantLock ;
21
23
22
24
import jakarta .persistence .EntityManager ;
23
25
import jakarta .persistence .PersistenceException ;
39
41
* JDBC and JPA operations in the same transaction, with cross visibility of
40
42
* their impact. If this is not needed, set the "lazyDatabaseTransaction" flag to
41
43
* {@code true} or consistently declare all affected transactions as read-only.
42
- * As of Spring 4.1.2, this will reliably avoid early JDBC Connection retrieval
43
- * and therefore keep EclipseLink in shared cache mode.
44
+ * This will reliably avoid early JDBC Connection retrieval and therefore keep
45
+ * EclipseLink in shared cache mode.
44
46
*
45
47
* <p><b>NOTE: This dialect supports custom isolation levels with limitations.</b>
46
- * Consistent isolation level handling is only guaranteed when all Spring transaction
47
- * definitions specify a concrete isolation level, and as of 6.0.10 also when using
48
- * the default isolation level with non-readOnly and non-lazy transactions. See the
48
+ * Consistent isolation level handling is only guaranteed when all Spring
49
+ * transaction definitions specify a concrete isolation level and when using the
50
+ * default isolation level with non-readOnly and non-lazy transactions; see the
49
51
* {@link #setLazyDatabaseTransaction "lazyDatabaseTransaction" javadoc} for details.
52
+ * Internal locking happens for transaction isolation management in EclipseLink's
53
+ * DatabaseLogin, at the granularity of the {@code EclipseLinkJpaDialect} instance;
54
+ * for independent persistence units with different target databases, use distinct
55
+ * {@code EclipseLinkJpaDialect} instances in order to minimize the locking impact.
50
56
*
51
57
* @author Juergen Hoeller
52
58
* @since 2.5.2
@@ -58,6 +64,8 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
58
64
59
65
private boolean lazyDatabaseTransaction = false ;
60
66
67
+ private final Lock transactionIsolationLock = new ReentrantLock ();
68
+
61
69
62
70
/**
63
71
* Set whether to lazily start a database resource transaction within a
@@ -94,13 +102,13 @@ public Object beginTransaction(EntityManager entityManager, TransactionDefinitio
94
102
95
103
int currentIsolationLevel = definition .getIsolationLevel ();
96
104
if (currentIsolationLevel != TransactionDefinition .ISOLATION_DEFAULT ) {
97
- // Pass custom isolation level on to EclipseLink's DatabaseLogin configuration
98
- // (since Spring 4.1.2 / revised in 5.3.28)
105
+ // Pass custom isolation level on to EclipseLink's DatabaseLogin configuration.
99
106
UnitOfWork uow = entityManager .unwrap (UnitOfWork .class );
100
107
DatabaseLogin databaseLogin = uow .getLogin ();
101
- // Synchronize on shared DatabaseLogin instance for consistent isolation level
108
+ // Lock around shared DatabaseLogin instance for consistent isolation level
102
109
// set and reset in case of concurrent transactions with different isolation.
103
- synchronized (databaseLogin ) {
110
+ this .transactionIsolationLock .lock ();
111
+ try {
104
112
int originalIsolationLevel = databaseLogin .getTransactionIsolation ();
105
113
// Apply current isolation level value, if necessary.
106
114
if (currentIsolationLevel != originalIsolationLevel ) {
@@ -116,20 +124,26 @@ public Object beginTransaction(EntityManager entityManager, TransactionDefinitio
116
124
databaseLogin .setTransactionIsolation (originalIsolationLevel );
117
125
}
118
126
}
127
+ finally {
128
+ this .transactionIsolationLock .unlock ();
129
+ }
119
130
}
120
131
else if (!definition .isReadOnly () && !this .lazyDatabaseTransaction ) {
121
132
// Begin an early transaction to force EclipseLink to get a JDBC Connection
122
133
// so that Spring can manage transactions with JDBC as well as EclipseLink.
123
134
UnitOfWork uow = entityManager .unwrap (UnitOfWork .class );
124
- DatabaseLogin databaseLogin = uow .getLogin ();
125
- // Synchronize on shared DatabaseLogin instance for consistently picking up
135
+ // Lock around shared DatabaseLogin instance for consistently picking up
126
136
// the default isolation level even in case of concurrent transactions with
127
- // a custom isolation level (see above), as of 6.0.10
128
- synchronized (databaseLogin ) {
137
+ // a custom isolation level (see above).
138
+ this .transactionIsolationLock .lock ();
139
+ try {
129
140
entityManager .getTransaction ().begin ();
130
141
uow .beginEarlyTransaction ();
131
142
entityManager .unwrap (Connection .class );
132
143
}
144
+ finally {
145
+ this .transactionIsolationLock .unlock ();
146
+ }
133
147
}
134
148
else {
135
149
// Regular transaction begin with lazy database transaction.
@@ -143,9 +157,6 @@ else if (!definition.isReadOnly() && !this.lazyDatabaseTransaction) {
143
157
public ConnectionHandle getJdbcConnection (EntityManager entityManager , boolean readOnly )
144
158
throws PersistenceException , SQLException {
145
159
146
- // As of Spring 4.1.2, we're using a custom ConnectionHandle for lazy retrieval
147
- // of the underlying Connection (allowing for deferred internal transaction begin
148
- // within the EclipseLink EntityManager)
149
160
return new EclipseLinkConnectionHandle (entityManager );
150
161
}
151
162
0 commit comments