@ -1,5 +1,5 @@
@@ -1,5 +1,5 @@
/ *
* Copyright 2002 - 2023 the original author or authors .
* Copyright 2002 - 2024 the original author or authors .
*
* Licensed under the Apache License , Version 2 . 0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
@ -18,6 +18,8 @@ package org.springframework.orm.jpa.vendor;
@@ -18,6 +18,8 @@ package org.springframework.orm.jpa.vendor;
import java.sql.Connection ;
import java.sql.SQLException ;
import java.util.concurrent.locks.Lock ;
import java.util.concurrent.locks.ReentrantLock ;
import jakarta.persistence.EntityManager ;
import jakarta.persistence.PersistenceException ;
@ -39,14 +41,18 @@ import org.springframework.transaction.TransactionException;
@@ -39,14 +41,18 @@ import org.springframework.transaction.TransactionException;
* JDBC and JPA operations in the same transaction , with cross visibility of
* their impact . If this is not needed , set the "lazyDatabaseTransaction" flag to
* { @code true } or consistently declare all affected transactions as read - only .
* As of Spring 4 . 1 . 2 , this will reliably avoid early JDBC Connection retrieval
* and therefore keep EclipseLink in shared cache mode .
* This will reliably avoid early JDBC Connection retrieval and therefore keep
* EclipseLink in shared cache mode .
*
* < p > < b > NOTE : This dialect supports custom isolation levels with limitations . < / b >
* Consistent isolation level handling is only guaranteed when all Spring transaction
* definitions specify a concrete isolation level , and as of 6 . 0 . 10 also when using
* the default isolation level with non - readOnly and non - lazy transactions . S ee the
* Consistent isolation level handling is only guaranteed when all Spring
* transaction definitions specify a concrete isolation level and when using the
* default isolation level with non - readOnly and non - lazy transactions ; s ee the
* { @link # setLazyDatabaseTransaction "lazyDatabaseTransaction" javadoc } for details .
* Internal locking happens for transaction isolation management in EclipseLink ' s
* DatabaseLogin , at the granularity of the { @code EclipseLinkJpaDialect } instance ;
* for independent persistence units with different target databases , use distinct
* { @code EclipseLinkJpaDialect } instances in order to minimize the locking impact .
*
* @author Juergen Hoeller
* @since 2 . 5 . 2
@ -58,6 +64,8 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
@@ -58,6 +64,8 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
private boolean lazyDatabaseTransaction = false ;
private final Lock transactionIsolationLock = new ReentrantLock ( ) ;
/ * *
* Set whether to lazily start a database resource transaction within a
@ -94,13 +102,13 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
@@ -94,13 +102,13 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
int currentIsolationLevel = definition . getIsolationLevel ( ) ;
if ( currentIsolationLevel ! = TransactionDefinition . ISOLATION_DEFAULT ) {
// Pass custom isolation level on to EclipseLink's DatabaseLogin configuration
// (since Spring 4.1.2 / revised in 5.3.28)
// Pass custom isolation level on to EclipseLink's DatabaseLogin configuration.
UnitOfWork uow = entityManager . unwrap ( UnitOfWork . class ) ;
DatabaseLogin databaseLogin = uow . getLogin ( ) ;
// Synchronize on shared DatabaseLogin instance for consistent isolation level
// Lock around shared DatabaseLogin instance for consistent isolation level
// set and reset in case of concurrent transactions with different isolation.
synchronized ( databaseLogin ) {
this . transactionIsolationLock . lock ( ) ;
try {
int originalIsolationLevel = databaseLogin . getTransactionIsolation ( ) ;
// Apply current isolation level value, if necessary.
if ( currentIsolationLevel ! = originalIsolationLevel ) {
@ -116,20 +124,26 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
@@ -116,20 +124,26 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
databaseLogin . setTransactionIsolation ( originalIsolationLevel ) ;
}
}
finally {
this . transactionIsolationLock . unlock ( ) ;
}
}
else if ( ! definition . isReadOnly ( ) & & ! this . lazyDatabaseTransaction ) {
// Begin an early transaction to force EclipseLink to get a JDBC Connection
// so that Spring can manage transactions with JDBC as well as EclipseLink.
UnitOfWork uow = entityManager . unwrap ( UnitOfWork . class ) ;
DatabaseLogin databaseLogin = uow . getLogin ( ) ;
// Synchronize on shared DatabaseLogin instance for consistently picking up
// Lock around shared DatabaseLogin instance for consistently picking up
// the default isolation level even in case of concurrent transactions with
// a custom isolation level (see above), as of 6.0.10
synchronized ( databaseLogin ) {
// a custom isolation level (see above).
this . transactionIsolationLock . lock ( ) ;
try {
entityManager . getTransaction ( ) . begin ( ) ;
uow . beginEarlyTransaction ( ) ;
entityManager . unwrap ( Connection . class ) ;
}
finally {
this . transactionIsolationLock . unlock ( ) ;
}
}
else {
// Regular transaction begin with lazy database transaction.
@ -143,9 +157,6 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
@@ -143,9 +157,6 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
public ConnectionHandle getJdbcConnection ( EntityManager entityManager , boolean readOnly )
throws PersistenceException , SQLException {
// As of Spring 4.1.2, we're using a custom ConnectionHandle for lazy retrieval
// of the underlying Connection (allowing for deferred internal transaction begin
// within the EclipseLink EntityManager)
return new EclipseLinkConnectionHandle ( entityManager ) ;
}