mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 08:24:13 +00:00
Consistent JPA/Hibernate transaction interoperability
Closes gh-37273
This commit is contained in:
@@ -54,6 +54,13 @@ public class EntityManagerHolder extends ResourceHolderSupport {
|
|||||||
this.entityAgent = entityAgent;
|
this.entityAgent = entityAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected EntityManagerHolder(EntityManagerHolder original) {
|
||||||
|
this.entityManager = original.entityManager;
|
||||||
|
this.entityAgent = original.entityAgent;
|
||||||
|
this.transactionActive = original.transactionActive;
|
||||||
|
this.savepointManager = original.savepointManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void setEntityManager(EntityManager entityManager) {
|
void setEntityManager(EntityManager entityManager) {
|
||||||
this.entityManager = entityManager;
|
this.entityManager = entityManager;
|
||||||
|
|||||||
+12
-3
@@ -44,6 +44,7 @@ import org.springframework.jdbc.datasource.DataSourceUtils;
|
|||||||
import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
|
import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
|
||||||
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
|
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
|
||||||
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
|
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
|
||||||
|
import org.springframework.orm.jpa.EntityManagerHolder;
|
||||||
import org.springframework.transaction.CannotCreateTransactionException;
|
import org.springframework.transaction.CannotCreateTransactionException;
|
||||||
import org.springframework.transaction.IllegalTransactionStateException;
|
import org.springframework.transaction.IllegalTransactionStateException;
|
||||||
import org.springframework.transaction.InvalidIsolationLevelException;
|
import org.springframework.transaction.InvalidIsolationLevelException;
|
||||||
@@ -391,9 +392,17 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
|
|||||||
txObject.setSavepointAllowed(isNestedTransactionAllowed());
|
txObject.setSavepointAllowed(isNestedTransactionAllowed());
|
||||||
|
|
||||||
SessionFactory sessionFactory = obtainSessionFactory();
|
SessionFactory sessionFactory = obtainSessionFactory();
|
||||||
SessionHolder sessionHolder =
|
Object resource = TransactionSynchronizationManager.getResource(sessionFactory);
|
||||||
(SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
|
if (resource instanceof EntityManagerHolder emHolder) {
|
||||||
if (sessionHolder != null) {
|
SessionHolder sessionHolder;
|
||||||
|
if (emHolder instanceof SessionHolder originalSessionHolder) {
|
||||||
|
sessionHolder = originalSessionHolder;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sessionHolder = new SessionHolder(emHolder);
|
||||||
|
TransactionSynchronizationManager.unbindResource(sessionFactory);
|
||||||
|
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
|
||||||
|
}
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Found thread-bound Session [" + sessionHolder.getSession() + "] for Hibernate transaction");
|
logger.debug("Found thread-bound Session [" + sessionHolder.getSession() + "] for Hibernate transaction");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.orm.jpa.hibernate;
|
package org.springframework.orm.jpa.hibernate;
|
||||||
|
|
||||||
|
import jakarta.persistence.EntityManager;
|
||||||
import org.hibernate.FlushMode;
|
import org.hibernate.FlushMode;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.StatelessSession;
|
import org.hibernate.StatelessSession;
|
||||||
@@ -49,10 +50,14 @@ public class SessionHolder extends EntityManagerHolder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SessionHolder(StatelessSession session) {
|
SessionHolder(StatelessSession session) {
|
||||||
super(null);
|
super((EntityManager) null);
|
||||||
setStatelessSession(session);
|
setStatelessSession(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SessionHolder(EntityManagerHolder emHolder) {
|
||||||
|
super(emHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void setSession(Session session) {
|
void setSession(Session session) {
|
||||||
this.entityManager = session;
|
this.entityManager = session;
|
||||||
|
|||||||
+11
-2
@@ -187,7 +187,16 @@ public class SpringSessionContext implements CurrentSessionContext {
|
|||||||
if (value instanceof StatelessSession statelessSession) {
|
if (value instanceof StatelessSession statelessSession) {
|
||||||
return statelessSession;
|
return statelessSession;
|
||||||
}
|
}
|
||||||
if (value instanceof SessionHolder sessionHolder) {
|
else if (value instanceof EntityManagerHolder emHolder) {
|
||||||
|
SessionHolder sessionHolder;
|
||||||
|
if (emHolder instanceof SessionHolder originalSessionHolder) {
|
||||||
|
sessionHolder = originalSessionHolder;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sessionHolder = new SessionHolder(emHolder);
|
||||||
|
TransactionSynchronizationManager.unbindResource(sessionFactory);
|
||||||
|
TransactionSynchronizationManager.bindResource(sessionFactory, sessionHolder);
|
||||||
|
}
|
||||||
if (sessionHolder.hasStatelessSession()) {
|
if (sessionHolder.hasStatelessSession()) {
|
||||||
return sessionHolder.getStatelessSession();
|
return sessionHolder.getStatelessSession();
|
||||||
}
|
}
|
||||||
@@ -216,7 +225,7 @@ public class SpringSessionContext implements CurrentSessionContext {
|
|||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new HibernateException("Could not obtain transaction-synchronized Session for current thread");
|
throw new HibernateException("Could not obtain transaction-synchronized StatelessSession for current thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void bindSessionHolder(SessionFactory sessionFactory, SessionHolder holder) {
|
private static void bindSessionHolder(SessionFactory sessionFactory, SessionHolder holder) {
|
||||||
|
|||||||
+58
@@ -32,7 +32,10 @@ import org.springframework.context.ApplicationContext;
|
|||||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||||
import org.springframework.orm.jpa.AbstractContainerEntityManagerFactoryIntegrationTests;
|
import org.springframework.orm.jpa.AbstractContainerEntityManagerFactoryIntegrationTests;
|
||||||
import org.springframework.orm.jpa.EntityManagerFactoryInfo;
|
import org.springframework.orm.jpa.EntityManagerFactoryInfo;
|
||||||
|
import org.springframework.orm.jpa.EntityManagerHolder;
|
||||||
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
import org.springframework.orm.jpa.domain.Person;
|
import org.springframework.orm.jpa.domain.Person;
|
||||||
|
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@@ -150,6 +153,61 @@ class HibernateNativeEntityManagerFactoryIntegrationTests extends AbstractContai
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void withJpaTransactionManager() {
|
||||||
|
String firstName = "Tony";
|
||||||
|
insertPerson(firstName);
|
||||||
|
|
||||||
|
Query<Person> q = statelessSession.createQuery("select p from Person as p", Person.class);
|
||||||
|
assertThat(q.getResultList()).hasSize(1);
|
||||||
|
assertThat(q.getResultList().get(0).getFirstName()).isEqualTo(firstName);
|
||||||
|
|
||||||
|
endTransaction();
|
||||||
|
|
||||||
|
JpaTransactionManager dstm = new JpaTransactionManager(sessionFactory);
|
||||||
|
new TransactionTemplate(dstm).execute(status -> {
|
||||||
|
insertPerson(firstName);
|
||||||
|
Query<Person> q2 = statelessSession.createQuery("select p from Person as p", Person.class);
|
||||||
|
assertThat(q2.getResultList()).hasSize(1);
|
||||||
|
assertThat(q2.getResultList().get(0).getFirstName()).isEqualTo(firstName);
|
||||||
|
Query<Person> q3 = sharedSession.createQuery("select p from Person as p", Person.class);
|
||||||
|
assertThat(q3.getResultList()).hasSize(1);
|
||||||
|
assertThat(q3.getResultList().get(0).getFirstName()).isEqualTo(firstName);
|
||||||
|
status.setRollbackOnly();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void withPreBoundEntityManager() {
|
||||||
|
String firstName = "Tony";
|
||||||
|
insertPerson(firstName);
|
||||||
|
|
||||||
|
Query<Person> q = statelessSession.createQuery("select p from Person as p", Person.class);
|
||||||
|
assertThat(q.getResultList()).hasSize(1);
|
||||||
|
assertThat(q.getResultList().get(0).getFirstName()).isEqualTo(firstName);
|
||||||
|
|
||||||
|
endTransaction();
|
||||||
|
|
||||||
|
Session session = sessionFactory.openSession();
|
||||||
|
TransactionSynchronizationManager.bindResource(sessionFactory, new EntityManagerHolder(session));
|
||||||
|
|
||||||
|
startNewTransaction();
|
||||||
|
|
||||||
|
insertPerson(firstName);
|
||||||
|
Query<Person> q2 = statelessSession.createQuery("select p from Person as p", Person.class);
|
||||||
|
assertThat(q2.getResultList()).hasSize(1);
|
||||||
|
assertThat(q2.getResultList().get(0).getFirstName()).isEqualTo(firstName);
|
||||||
|
Query<Person> q3 = sharedSession.createQuery("select p from Person as p", Person.class);
|
||||||
|
assertThat(q3.getResultList()).hasSize(1);
|
||||||
|
assertThat(q3.getResultList().get(0).getFirstName()).isEqualTo(firstName);
|
||||||
|
|
||||||
|
endTransaction();
|
||||||
|
|
||||||
|
session.close();
|
||||||
|
TransactionSynchronizationManager.unbindResource(sessionFactory);
|
||||||
|
}
|
||||||
|
|
||||||
@Test // SPR-16956
|
@Test // SPR-16956
|
||||||
void readOnly() {
|
void readOnly() {
|
||||||
assertThat(sessionFactory.getCurrentSession().getHibernateFlushMode()).isSameAs(FlushMode.AUTO);
|
assertThat(sessionFactory.getCurrentSession().getHibernateFlushMode()).isSameAs(FlushMode.AUTO);
|
||||||
|
|||||||
Reference in New Issue
Block a user