diff --git a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java index 754764f890..5a2e8bd016 100644 --- a/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/SharedSessionContract.java @@ -68,17 +68,29 @@ public interface SharedSessionContract extends QueryProducer, AutoCloseable, Ser * Begin a unit of work and return the associated {@link Transaction} object. * If a new underlying transaction is required, begin the transaction. Otherwise, * continue the new work in the context of the existing underlying transaction. - *
- * The JPA-standard way to begin a new transaction is by calling - * {@link #getTransaction getTransaction().begin()}. When + * + * @apiNote + * The JPA-standard way to begin a new resource-local transaction is by calling + * {@link #getTransaction getTransaction().begin()}. But it's not always safe to + * execute this idiom. + *
+ * On the other hand, this method does not fail when JTA transaction management + * is used, not even if strict JPA transaction compliance is enabled. + *
+ * This method never fails when a transaction is already active. Instead, + * {@code beginTransaction()} simply returns the {@link Transaction} object + * representing the active transaction. + *
+ * On the other hand, when JTA transaction management is used, and when + * strict JPA transaction compliance is disabled, this method happily + * returns a {@link Transaction} representing the current JTA transaction context. + * * @return an instance of {@link Transaction} representing the transaction * associated with this session * diff --git a/hibernate-core/src/main/java/org/hibernate/Transaction.java b/hibernate-core/src/main/java/org/hibernate/Transaction.java index 0ce4b4f037..c6d9e4b2af 100644 --- a/hibernate-core/src/main/java/org/hibernate/Transaction.java +++ b/hibernate-core/src/main/java/org/hibernate/Transaction.java @@ -17,7 +17,7 @@ import org.hibernate.resource.transaction.spi.TransactionStatus; * depending on how Hibernate is configured. *
* Every resource-local transaction is associated with a {@link Session} and begins with - * an explicit call to {@link Session#beginTransaction()}, or, equivalently, with + * an explicit call to {@link Session#beginTransaction()}, or, almost equivalently, with * {@code session.getTransaction().begin()}, and ends with a call to {@link #commit()} * or {@link #rollback()}. *
@@ -31,6 +31,11 @@ import org.hibernate.resource.transaction.spi.TransactionStatus; *
* A {@code Transaction} object is not threadsafe. * + * @apiNote JPA doesn't allow an {@link EntityTransaction} to represent a JTA transaction. + * But when {@linkplain org.hibernate.jpa.spi.JpaCompliance#isJpaTransactionComplianceEnabled + * strict JPA transaction compliance} is disabled, as it is by default, Hibernate allows an + * instance of this interface to represent the current JTA transaction context. + * * @author Anton van Straaten * @author Steve Ebersole * diff --git a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java index 1e5de58aa4..a59c24dbc4 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/AbstractSharedSessionContract.java @@ -610,7 +610,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont @Override public Transaction beginTransaction() { checkOpen(); - final Transaction transaction = getTransaction(); + final Transaction transaction = accessTransaction(); // only need to begin a transaction if it was not // already active (this is the documented semantics) if ( !transaction.isActive() ) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/resource/transaction/jta/JpaComplianceAlreadyStartedTransactionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/resource/transaction/jta/JpaComplianceAlreadyStartedTransactionTest.java index f0ca7aabf9..8e7bc38d9d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/resource/transaction/jta/JpaComplianceAlreadyStartedTransactionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/resource/transaction/jta/JpaComplianceAlreadyStartedTransactionTest.java @@ -45,7 +45,8 @@ public class JpaComplianceAlreadyStartedTransactionTest extends BaseNonConfigCor Transaction tx = null; try { // A call to begin() with an active Tx should cause an IllegalStateException - tx = s.beginTransaction(); + tx = s.getTransaction(); + tx.begin(); } catch (Exception e) { if ( tx != null && tx.isActive() ) { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/resource/transaction/jta/NonJpaComplianceAlreadyStartedTransactionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/resource/transaction/jta/NonJpaComplianceAlreadyStartedTransactionTest.java index a0e51a4368..5b27adf226 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/resource/transaction/jta/NonJpaComplianceAlreadyStartedTransactionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/resource/transaction/jta/NonJpaComplianceAlreadyStartedTransactionTest.java @@ -50,7 +50,8 @@ public class NonJpaComplianceAlreadyStartedTransactionTest extends BaseNonConfig public void noIllegalStateExceptionShouldBeThrownWhenBeginTxIsCalledWithAnAlreadyActiveTx() throws Exception { tm.begin(); try (Session s = openSession()) { - Transaction tx = s.beginTransaction(); + Transaction tx = s.getTransaction(); + tx.begin(); try { s.persist( new TestEntity( "ABC" ) ); tx.commit();