Replace transaction isolation synchronization with ReentrantLock
Closes gh-33546
This commit is contained in:
parent
8680c43368
commit
594ed95f3c
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -18,6 +18,8 @@ package org.springframework.orm.jpa.vendor;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import jakarta.persistence.PersistenceException;
|
import jakarta.persistence.PersistenceException;
|
||||||
|
@ -39,14 +41,18 @@ import org.springframework.transaction.TransactionException;
|
||||||
* JDBC and JPA operations in the same transaction, with cross visibility of
|
* JDBC and JPA operations in the same transaction, with cross visibility of
|
||||||
* their impact. If this is not needed, set the "lazyDatabaseTransaction" flag to
|
* their impact. If this is not needed, set the "lazyDatabaseTransaction" flag to
|
||||||
* {@code true} or consistently declare all affected transactions as read-only.
|
* {@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
|
* This will reliably avoid early JDBC Connection retrieval and therefore keep
|
||||||
* and therefore keep EclipseLink in shared cache mode.
|
* EclipseLink in shared cache mode.
|
||||||
*
|
*
|
||||||
* <p><b>NOTE: This dialect supports custom isolation levels with limitations.</b>
|
* <p><b>NOTE: This dialect supports custom isolation levels with limitations.</b>
|
||||||
* Consistent isolation level handling is only guaranteed when all Spring transaction
|
* Consistent isolation level handling is only guaranteed when all Spring
|
||||||
* definitions specify a concrete isolation level, and as of 6.0.10 also when using
|
* transaction definitions specify a concrete isolation level and when using the
|
||||||
* the default isolation level with non-readOnly and non-lazy transactions. See the
|
* default isolation level with non-readOnly and non-lazy transactions; see the
|
||||||
* {@link #setLazyDatabaseTransaction "lazyDatabaseTransaction" javadoc} for details.
|
* {@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
|
* @author Juergen Hoeller
|
||||||
* @since 2.5.2
|
* @since 2.5.2
|
||||||
|
@ -58,6 +64,8 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
|
||||||
|
|
||||||
private boolean lazyDatabaseTransaction = false;
|
private boolean lazyDatabaseTransaction = false;
|
||||||
|
|
||||||
|
private final Lock transactionIsolationLock = new ReentrantLock();
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set whether to lazily start a database resource transaction within a
|
* Set whether to lazily start a database resource transaction within a
|
||||||
|
@ -94,13 +102,13 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
|
||||||
|
|
||||||
int currentIsolationLevel = definition.getIsolationLevel();
|
int currentIsolationLevel = definition.getIsolationLevel();
|
||||||
if (currentIsolationLevel != TransactionDefinition.ISOLATION_DEFAULT) {
|
if (currentIsolationLevel != TransactionDefinition.ISOLATION_DEFAULT) {
|
||||||
// Pass custom isolation level on to EclipseLink's DatabaseLogin configuration
|
// Pass custom isolation level on to EclipseLink's DatabaseLogin configuration.
|
||||||
// (since Spring 4.1.2 / revised in 5.3.28)
|
|
||||||
UnitOfWork uow = entityManager.unwrap(UnitOfWork.class);
|
UnitOfWork uow = entityManager.unwrap(UnitOfWork.class);
|
||||||
DatabaseLogin databaseLogin = uow.getLogin();
|
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.
|
// set and reset in case of concurrent transactions with different isolation.
|
||||||
synchronized (databaseLogin) {
|
this.transactionIsolationLock.lock();
|
||||||
|
try {
|
||||||
int originalIsolationLevel = databaseLogin.getTransactionIsolation();
|
int originalIsolationLevel = databaseLogin.getTransactionIsolation();
|
||||||
// Apply current isolation level value, if necessary.
|
// Apply current isolation level value, if necessary.
|
||||||
if (currentIsolationLevel != originalIsolationLevel) {
|
if (currentIsolationLevel != originalIsolationLevel) {
|
||||||
|
@ -116,20 +124,26 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
|
||||||
databaseLogin.setTransactionIsolation(originalIsolationLevel);
|
databaseLogin.setTransactionIsolation(originalIsolationLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
this.transactionIsolationLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (!definition.isReadOnly() && !this.lazyDatabaseTransaction) {
|
else if (!definition.isReadOnly() && !this.lazyDatabaseTransaction) {
|
||||||
// Begin an early transaction to force EclipseLink to get a JDBC Connection
|
// Begin an early transaction to force EclipseLink to get a JDBC Connection
|
||||||
// so that Spring can manage transactions with JDBC as well as EclipseLink.
|
// so that Spring can manage transactions with JDBC as well as EclipseLink.
|
||||||
UnitOfWork uow = entityManager.unwrap(UnitOfWork.class);
|
UnitOfWork uow = entityManager.unwrap(UnitOfWork.class);
|
||||||
DatabaseLogin databaseLogin = uow.getLogin();
|
// Lock around shared DatabaseLogin instance for consistently picking up
|
||||||
// Synchronize on shared DatabaseLogin instance for consistently picking up
|
|
||||||
// the default isolation level even in case of concurrent transactions with
|
// the default isolation level even in case of concurrent transactions with
|
||||||
// a custom isolation level (see above), as of 6.0.10
|
// a custom isolation level (see above).
|
||||||
synchronized (databaseLogin) {
|
this.transactionIsolationLock.lock();
|
||||||
|
try {
|
||||||
entityManager.getTransaction().begin();
|
entityManager.getTransaction().begin();
|
||||||
uow.beginEarlyTransaction();
|
uow.beginEarlyTransaction();
|
||||||
entityManager.unwrap(Connection.class);
|
entityManager.unwrap(Connection.class);
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
this.transactionIsolationLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Regular transaction begin with lazy database transaction.
|
// Regular transaction begin with lazy database transaction.
|
||||||
|
@ -143,9 +157,6 @@ public class EclipseLinkJpaDialect extends DefaultJpaDialect {
|
||||||
public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly)
|
public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly)
|
||||||
throws PersistenceException, SQLException {
|
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);
|
return new EclipseLinkConnectionHandle(entityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue