Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -389,9 +389,13 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
throw new IllegalTransactionStateException(
"Pre-bound JDBC Connection found! JpaTransactionManager does not support " +
"running within DataSourceTransactionManager if told to manage the DataSource itself. " +
"It is recommended to use a single JpaTransactionManager for all transactions " +
"on a single DataSource, no matter whether JPA or JDBC access.");
"running within another transaction manager that exposes a JDBC Connection " +
"for the same DataSource (for example, a JpaTransactionManager or " +
"HibernateTransactionManager for a different persistence unit) if told to " +
"manage the DataSource itself. Alternatively, a JDBC Connection may have been " +
"left bound to the current thread. It is recommended to use a single " +
"JpaTransactionManager for all transactions on a single DataSource, " +
"no matter whether JPA or JDBC access.");
}

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -445,9 +445,13 @@ protected void doBegin(Object transaction, TransactionDefinition definition) {
if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
throw new IllegalTransactionStateException(
"Pre-bound JDBC Connection found! HibernateTransactionManager does not support " +
"running within DataSourceTransactionManager if told to manage the DataSource itself. " +
"It is recommended to use a single HibernateTransactionManager for all transactions " +
"on a single DataSource, no matter whether Hibernate or JDBC access.");
"running within another transaction manager that exposes a JDBC Connection " +
"for the same DataSource (for example, a HibernateTransactionManager or " +
"JpaTransactionManager for a different SessionFactory) if told to manage " +
"the DataSource itself. Alternatively, a JDBC Connection may have been " +
"left bound to the current thread. It is recommended to use a single " +
"HibernateTransactionManager for all transactions on a single DataSource, " +
"no matter whether Hibernate or JDBC access.");
}

SessionImplementor session = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@

package org.springframework.orm.jpa;

import java.sql.Connection;
import java.util.List;

import javax.sql.DataSource;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
Expand All @@ -26,6 +29,9 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.springframework.jdbc.datasource.ConnectionHandle;
import org.springframework.jdbc.datasource.SimpleConnectionHandle;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionSystemException;
Expand Down Expand Up @@ -613,4 +619,37 @@ void transactionFlush() {
verify(manager).close();
}

@Test // gh-14130
void transactionWithinOtherTransactionManagerForSameDataSource() {
DataSource dataSource = mock();
Connection connection = mock();
JpaDialect jpaDialect = new DefaultJpaDialect() {
@Override
public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly) {
return new SimpleConnectionHandle(connection);
}
};

EntityManagerFactory otherFactory = mock();
EntityManager otherManager = mock();
given(otherFactory.createEntityManager()).willReturn(otherManager);
given(otherManager.getTransaction()).willReturn(mock());
given(otherManager.isOpen()).willReturn(true);

JpaTransactionManager otherTm = new JpaTransactionManager(otherFactory);
otherTm.setDataSource(dataSource);
otherTm.setJpaDialect(jpaDialect);
tm.setDataSource(dataSource);
tm.setJpaDialect(jpaDialect);

assertThatExceptionOfType(IllegalTransactionStateException.class)
.isThrownBy(() -> new TransactionTemplate(otherTm).executeWithoutResult(status ->
tt.executeWithoutResult(innerStatus -> {})))
.withMessageStartingWith("Pre-bound JDBC Connection found!")
.withMessageContaining("another transaction manager")
.withMessageNotContaining("DataSourceTransactionManager");

verify(otherManager).close();
}

}