diff --git a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/EclipseLinkJpaDialect.java b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/EclipseLinkJpaDialect.java index 7f6af0152e..c26b9bb27b 100644 --- a/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/EclipseLinkJpaDialect.java +++ b/spring-orm/src/main/java/org/springframework/orm/jpa/vendor/EclipseLinkJpaDialect.java @@ -34,14 +34,20 @@ import org.springframework.transaction.TransactionException; * {@link org.springframework.orm.jpa.JpaDialect} implementation for Eclipse * Persistence Services (EclipseLink). Compatible with EclipseLink 3.0/4.0. * - *

By default, this class acquires an early EclipseLink transaction with an early - * JDBC Connection for non-read-only transactions. This allows for mixing JDBC and - * JPA/EclipseLink operations in the same transaction, with cross visibility of + *

By default, this dialect acquires an early EclipseLink transaction with an + * early JDBC Connection for non-read-only transactions. This allows for mixing + * 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. * + *

NOTE: This dialect supports custom isolation levels with limitations. + * 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. See the + * {@link #setLazyDatabaseTransaction "lazyDatabaseTransaction" javadoc} for details. + * * @author Juergen Hoeller * @since 2.5.2 * @see #setLazyDatabaseTransaction @@ -65,7 +71,16 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect { * even for non-read-only transactions, allowing access to EclipseLink's * shared cache and following EclipseLink's connection mode configuration, * assuming that isolation and visibility at the JDBC level are less important. + *

NOTE: Lazy database transactions are not guaranteed to work reliably + * in combination with custom isolation levels. Use read-only as well as this + * lazy flag with care. If other transactions use custom isolation levels, + * it is not recommended to use read-only and lazy transactions at all. + * Otherwise, you may see non-default isolation levels used during read-only + * or lazy access. If this is not acceptable, don't use read-only and lazy + * next to custom isolation levels in potentially concurrent transactions. * @see org.eclipse.persistence.sessions.UnitOfWork#beginEarlyTransaction() + * @see TransactionDefinition#isReadOnly() + * @see TransactionDefinition#getIsolationLevel() */ public void setLazyDatabaseTransaction(boolean lazyDatabaseTransaction) { this.lazyDatabaseTransaction = lazyDatabaseTransaction; @@ -83,7 +98,8 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect { // (since Spring 4.1.2 / revised in 5.3.28) UnitOfWork uow = entityManager.unwrap(UnitOfWork.class); DatabaseLogin databaseLogin = uow.getLogin(); - // Synchronize on shared DatabaseLogin instance (-> concurrent transactions) + // Synchronize on shared DatabaseLogin instance for consistent isolation level + // set and reset in case of concurrent transactions with different isolation. synchronized (databaseLogin) { int originalIsolationLevel = databaseLogin.getTransactionIsolation(); // Apply current isolation level value, if necessary. @@ -101,13 +117,23 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect { } } } + 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 + // the default isolation level even in case of concurrent transactions with + // a custom isolation level (see above), as of 6.0.10 + synchronized (databaseLogin) { + entityManager.getTransaction().begin(); + uow.beginEarlyTransaction(); + entityManager.unwrap(Connection.class); + } + } else { + // Regular transaction begin with lazy database transaction. entityManager.getTransaction().begin(); - 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. - entityManager.unwrap(UnitOfWork.class).beginEarlyTransaction(); - } } return null;