Skip to content

Commit

Permalink
SharedEntityManagerCreator is oblivious to the presence of multiple T…
Browse files Browse the repository at this point in the history
…ransactionManager
  • Loading branch information
reda-alaoui committed Nov 30, 2023
1 parent 379e1f2 commit c444e8f
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
* <p>Mainly intended for internal use within the framework.
*
* @author Juergen Hoeller
* @author Réda Housni Alaoui
* @since 2.0
*/
public abstract class EntityManagerFactoryUtils {
Expand Down Expand Up @@ -196,6 +197,28 @@ public static EntityManager doGetTransactionalEntityManager(EntityManagerFactory
public static EntityManager doGetTransactionalEntityManager(
EntityManagerFactory emf, @Nullable Map<?, ?> properties, boolean synchronizedWithTransaction)
throws PersistenceException {
return doGetTransactionalEntityManager(emf, properties, synchronizedWithTransaction, true);
}

/**
* Obtain a JPA EntityManager from the given factory. Is aware of a corresponding
* EntityManager bound to the current thread, e.g. when using JpaTransactionManager.
* <p>Same as {@code getEntityManager}, but throwing the original PersistenceException.
* @param emf the EntityManagerFactory to create the EntityManager with
* @param properties the properties to be passed into the {@code createEntityManager}
* call (may be {@code null})
* @param synchronizedWithTransaction whether to automatically join ongoing
* transactions (according to the JPA 2.1 SynchronizationType rules)
* @param createIfNeeded whether to create an {@link EntityManager} if no existing transactional one is found
* @return the EntityManager, or {@code null} if none found
* @throws jakarta.persistence.PersistenceException if the EntityManager couldn't be created
* @see #getTransactionalEntityManager(jakarta.persistence.EntityManagerFactory)
* @see JpaTransactionManager
*/
@Nullable
public static EntityManager doGetTransactionalEntityManager(
EntityManagerFactory emf, @Nullable Map<?, ?> properties, boolean synchronizedWithTransaction, boolean createIfNeeded)
throws PersistenceException {

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

Expand Down Expand Up @@ -249,6 +272,9 @@ else if (!TransactionSynchronizationManager.isSynchronizationActive()) {
// Indicate that we can't obtain a transactional EntityManager.
return null;
}
else if (!createIfNeeded) {
return null;
}

// Create a new EntityManager for use within the current transaction.
logger.debug("Opening JPA EntityManager");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
* @author Oliver Gierke
* @author Mark Paluch
* @author Sam Brannen
* @author Réda Housni Alaoui
* @since 2.0
* @see jakarta.persistence.PersistenceContext
* @see jakarta.persistence.PersistenceContextType#TRANSACTION
Expand Down Expand Up @@ -260,10 +261,9 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
"use Spring transactions or EJB CMT instead");
}

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

switch (method.getName()) {
case "getTargetEntityManager" -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,24 +23,32 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.jpa.AbstractContainerEntityManagerFactoryIntegrationTests;
import org.springframework.orm.jpa.EntityManagerFactoryInfo;
import org.springframework.orm.jpa.domain.Person;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

/**
* Hibernate-specific JPA tests with multiple EntityManagerFactory instances.
*
* @author Juergen Hoeller
* @author Réda Housni Alaoui
*/
public class HibernateMultiEntityManagerFactoryIntegrationTests extends AbstractContainerEntityManagerFactoryIntegrationTests {

@Autowired
private EntityManagerFactory entityManagerFactory2;

@Autowired
private PlatformTransactionManager transactionManager2;


@Override
protected String[] getConfigLocations() {
return new String[] {"/org/springframework/orm/jpa/hibernate/hibernate-manager-multi.xml",
return new String[]{"/org/springframework/orm/jpa/hibernate/hibernate-manager-multi.xml",
"/org/springframework/orm/jpa/memdb.xml"};
}

Expand Down Expand Up @@ -68,4 +76,19 @@ public void testEntityManagerFactory2() {
}
}

@Test
public void testInstantiateAndSaveWithSharedEmProxyUnderTheWrongTransaction() {
endTransaction();
TransactionStatus transaction = this.transactionManager2.getTransaction(this.transactionDefinition);

assertThat(countRowsInTable("person")).as("Should be no people from previous transactions").isEqualTo(0);
Person person = new Person();
person.setFirstName("Tony");
person.setLastName("Blair");
assertThatThrownBy(() -> sharedEntityManager.persist(person))
.hasMessageContaining("No EntityManager with actual transaction available for current thread");

transactionManager2.rollback(transaction);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean id="transactionManager2" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory2"/>
</bean>

</beans>

0 comments on commit c444e8f

Please sign in to comment.