Skip to content

Commit c23522e

Browse files
committed
SharedEntityManagerCreator is oblivious to the presence of multiple TransactionManager
1 parent f083962 commit c23522e

File tree

4 files changed

+57
-4
lines changed

4 files changed

+57
-4
lines changed

spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
* <p>Mainly intended for internal use within the framework.
6464
*
6565
* @author Juergen Hoeller
66+
* @author Réda Housni Alaoui
6667
* @since 2.0
6768
*/
6869
public abstract class EntityManagerFactoryUtils {
@@ -196,6 +197,28 @@ public static EntityManager doGetTransactionalEntityManager(EntityManagerFactory
196197
public static EntityManager doGetTransactionalEntityManager(
197198
EntityManagerFactory emf, @Nullable Map<?, ?> properties, boolean synchronizedWithTransaction)
198199
throws PersistenceException {
200+
return doGetTransactionalEntityManager(emf, properties, synchronizedWithTransaction, true);
201+
}
202+
203+
/**
204+
* Obtain a JPA EntityManager from the given factory. Is aware of a corresponding
205+
* EntityManager bound to the current thread, e.g. when using JpaTransactionManager.
206+
* <p>Same as {@code getEntityManager}, but throwing the original PersistenceException.
207+
* @param emf the EntityManagerFactory to create the EntityManager with
208+
* @param properties the properties to be passed into the {@code createEntityManager}
209+
* call (may be {@code null})
210+
* @param synchronizedWithTransaction whether to automatically join ongoing
211+
* transactions (according to the JPA 2.1 SynchronizationType rules)
212+
* @param createIfNeeded whether to create an {@link EntityManager} if no existing transactional one is found
213+
* @return the EntityManager, or {@code null} if none found
214+
* @throws jakarta.persistence.PersistenceException if the EntityManager couldn't be created
215+
* @see #getTransactionalEntityManager(jakarta.persistence.EntityManagerFactory)
216+
* @see JpaTransactionManager
217+
*/
218+
@Nullable
219+
public static EntityManager doGetTransactionalEntityManager(
220+
EntityManagerFactory emf, @Nullable Map<?, ?> properties, boolean synchronizedWithTransaction, boolean createIfNeeded)
221+
throws PersistenceException {
199222

200223
Assert.notNull(emf, "No EntityManagerFactory specified");
201224

@@ -249,6 +272,9 @@ else if (!TransactionSynchronizationManager.isSynchronizationActive()) {
249272
// Indicate that we can't obtain a transactional EntityManager.
250273
return null;
251274
}
275+
else if (!createIfNeeded) {
276+
return null;
277+
}
252278

253279
// Create a new EntityManager for use within the current transaction.
254280
logger.debug("Opening JPA EntityManager");

spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
* @author Oliver Gierke
6262
* @author Mark Paluch
6363
* @author Sam Brannen
64+
* @author Réda Housni Alaoui
6465
* @since 2.0
6566
* @see jakarta.persistence.PersistenceContext
6667
* @see jakarta.persistence.PersistenceContextType#TRANSACTION
@@ -268,10 +269,9 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
268269
}
269270
}
270271

271-
// Determine current EntityManager: either the transactional one
272-
// managed by the factory or a temporary one for the given invocation.
272+
// Determine current EntityManager
273273
EntityManager target = EntityManagerFactoryUtils.doGetTransactionalEntityManager(
274-
this.targetFactory, this.properties, this.synchronizedWithTransaction);
274+
this.targetFactory, this.properties, this.synchronizedWithTransaction, false);
275275

276276
switch (method.getName()) {
277277
case "getTargetEntityManager" -> {

spring-orm/src/test/java/org/springframework/orm/jpa/hibernate/HibernateMultiEntityManagerFactoryIntegrationTests.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,24 +23,32 @@
2323
import org.springframework.beans.factory.annotation.Autowired;
2424
import org.springframework.orm.jpa.AbstractContainerEntityManagerFactoryIntegrationTests;
2525
import org.springframework.orm.jpa.EntityManagerFactoryInfo;
26+
import org.springframework.orm.jpa.domain.Person;
27+
import org.springframework.transaction.PlatformTransactionManager;
28+
import org.springframework.transaction.TransactionStatus;
2629

2730
import static org.assertj.core.api.Assertions.assertThat;
2831
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
32+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2933

3034
/**
3135
* Hibernate-specific JPA tests with multiple EntityManagerFactory instances.
3236
*
3337
* @author Juergen Hoeller
38+
* @author Réda Housni Alaoui
3439
*/
3540
class HibernateMultiEntityManagerFactoryIntegrationTests extends AbstractContainerEntityManagerFactoryIntegrationTests {
3641

3742
@Autowired
3843
private EntityManagerFactory entityManagerFactory2;
3944

45+
@Autowired
46+
private PlatformTransactionManager transactionManager2;
47+
4048

4149
@Override
4250
protected String[] getConfigLocations() {
43-
return new String[] {"/org/springframework/orm/jpa/hibernate/hibernate-manager-multi.xml",
51+
return new String[]{"/org/springframework/orm/jpa/hibernate/hibernate-manager-multi.xml",
4452
"/org/springframework/orm/jpa/memdb.xml"};
4553
}
4654

@@ -68,4 +76,19 @@ void testEntityManagerFactory2() {
6876
}
6977
}
7078

79+
@Test
80+
public void testInstantiateAndSaveWithSharedEmProxyUnderTheWrongTransaction() {
81+
endTransaction();
82+
TransactionStatus transaction = this.transactionManager2.getTransaction(this.transactionDefinition);
83+
84+
assertThat(countRowsInTable("person")).as("Should be no people from previous transactions").isEqualTo(0);
85+
Person person = new Person();
86+
person.setFirstName("Tony");
87+
person.setLastName("Blair");
88+
assertThatThrownBy(() -> sharedEntityManager.persist(person))
89+
.hasMessageContaining("No EntityManager with actual transaction available for current thread");
90+
91+
transactionManager2.rollback(transaction);
92+
}
93+
7194
}

spring-orm/src/test/resources/org/springframework/orm/jpa/hibernate/hibernate-manager-multi.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,8 @@
2727
<property name="entityManagerFactory" ref="entityManagerFactory"/>
2828
</bean>
2929

30+
<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager">
31+
<property name="entityManagerFactory" ref="entityManagerFactory2"/>
32+
</bean>
33+
3034
</beans>

0 commit comments

Comments
 (0)