mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +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;
|
||||
}
|
||||
|
||||
protected EntityManagerHolder(EntityManagerHolder original) {
|
||||
this.entityManager = original.entityManager;
|
||||
this.entityAgent = original.entityAgent;
|
||||
this.transactionActive = original.transactionActive;
|
||||
this.savepointManager = original.savepointManager;
|
||||
}
|
||||
|
||||
|
||||
void setEntityManager(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.TransactionAwareDataSourceProxy;
|
||||
import org.springframework.orm.jpa.EntityManagerFactoryUtils;
|
||||
import org.springframework.orm.jpa.EntityManagerHolder;
|
||||
import org.springframework.transaction.CannotCreateTransactionException;
|
||||
import org.springframework.transaction.IllegalTransactionStateException;
|
||||
import org.springframework.transaction.InvalidIsolationLevelException;
|
||||
@@ -391,9 +392,17 @@ public class HibernateTransactionManager extends AbstractPlatformTransactionMana
|
||||
txObject.setSavepointAllowed(isNestedTransactionAllowed());
|
||||
|
||||
SessionFactory sessionFactory = obtainSessionFactory();
|
||||
SessionHolder sessionHolder =
|
||||
(SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
|
||||
if (sessionHolder != null) {
|
||||
Object resource = TransactionSynchronizationManager.getResource(sessionFactory);
|
||||
if (resource 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 (logger.isDebugEnabled()) {
|
||||
logger.debug("Found thread-bound Session [" + sessionHolder.getSession() + "] for Hibernate transaction");
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.orm.jpa.hibernate;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.StatelessSession;
|
||||
@@ -49,10 +50,14 @@ public class SessionHolder extends EntityManagerHolder {
|
||||
}
|
||||
|
||||
SessionHolder(StatelessSession session) {
|
||||
super(null);
|
||||
super((EntityManager) null);
|
||||
setStatelessSession(session);
|
||||
}
|
||||
|
||||
SessionHolder(EntityManagerHolder emHolder) {
|
||||
super(emHolder);
|
||||
}
|
||||
|
||||
|
||||
void setSession(Session session) {
|
||||
this.entityManager = session;
|
||||
|
||||
+11
-2
@@ -187,7 +187,16 @@ public class SpringSessionContext implements CurrentSessionContext {
|
||||
if (value instanceof StatelessSession 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()) {
|
||||
return sessionHolder.getStatelessSession();
|
||||
}
|
||||
@@ -216,7 +225,7 @@ public class SpringSessionContext implements CurrentSessionContext {
|
||||
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) {
|
||||
|
||||
+58
@@ -32,7 +32,10 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.orm.jpa.AbstractContainerEntityManagerFactoryIntegrationTests;
|
||||
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.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
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
|
||||
void readOnly() {
|
||||
assertThat(sessionFactory.getCurrentSession().getHibernateFlushMode()).isSameAs(FlushMode.AUTO);
|
||||
|
||||
Reference in New Issue
Block a user