diff --git a/ebean-spring-txn/src/test/java/org/example/EbeanSpringModuleTest.java b/ebean-spring-txn/src/test/java/org/example/EbeanSpringModuleTest.java index 82cbffb4ea..3b35a302fe 100644 --- a/ebean-spring-txn/src/test/java/org/example/EbeanSpringModuleTest.java +++ b/ebean-spring-txn/src/test/java/org/example/EbeanSpringModuleTest.java @@ -2,15 +2,19 @@ import io.ebean.DB; import io.ebean.Transaction; +import io.ebean.test.PersistenceContextAccess; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.annotation.Transactional; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.in; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -42,6 +46,24 @@ public EbeanSpringModuleTest() { super(); } + @Transactional + @Rollback + @Test + public void testWithRollback() { + // setup + User user = new User(); + user.setName("rollback1"); + DB.save(user); + + // this loads the user into the [transaction scoped] persistence context + User found = DB.find(User.class, user.getOid()); + found.setName("mutated"); + + PersistenceContextAccess.clear(); + + userService.insideTestRollback(user.getOid()); + } + /** * Test app. */ diff --git a/ebean-spring-txn/src/test/java/org/example/UserService.java b/ebean-spring-txn/src/test/java/org/example/UserService.java index 6370601bab..cecf059b4b 100644 --- a/ebean-spring-txn/src/test/java/org/example/UserService.java +++ b/ebean-spring-txn/src/test/java/org/example/UserService.java @@ -16,4 +16,6 @@ public interface UserService { void batchInsert(); void requiresNew(); + + void insideTestRollback(long oid); } diff --git a/ebean-spring-txn/src/test/java/org/example/UserServiceImpl.java b/ebean-spring-txn/src/test/java/org/example/UserServiceImpl.java index 2a49f43f7b..0923a306e5 100644 --- a/ebean-spring-txn/src/test/java/org/example/UserServiceImpl.java +++ b/ebean-spring-txn/src/test/java/org/example/UserServiceImpl.java @@ -12,6 +12,8 @@ import java.util.ArrayList; import java.util.List; +import static org.assertj.core.api.Assertions.assertThat; + /** * The Class UserServiceImpl. * @@ -28,6 +30,13 @@ public class UserServiceImpl implements UserService, ApplicationContextAware { @Autowired private Database ebeanServer; + @Override + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class) + public void insideTestRollback(long oid) { + User found = ebeanServer.find(User.class, oid); + assertThat(found.getName()).isEqualTo("rollback1"); + } + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Throwable.class) public void save(User user) { ebeanServer.save(user); diff --git a/ebean-test/src/main/java/io/ebean/test/PersistenceContextAccess.java b/ebean-test/src/main/java/io/ebean/test/PersistenceContextAccess.java new file mode 100644 index 0000000000..b7586145dc --- /dev/null +++ b/ebean-test/src/main/java/io/ebean/test/PersistenceContextAccess.java @@ -0,0 +1,59 @@ +package io.ebean.test; + +import io.ebean.Transaction; +import io.ebean.bean.PersistenceContext; +import io.ebeaninternal.api.SpiTransaction; + +/** + * Provides tests access to the persistence context. + *

+ * Expected to be used with tests that use Spring test {@code @Rollback} {@code @Transactional}. + * These tests have an outer transaction that will rollback. The issue is that test setup code + * is now running in the SAME TRANSACTION as the code under test (main code we are testing) and + * that the Ebean Persistence Context is transaction scoped - so the test setup code can load + * entities into the ebean persistence context during the test setup phase - we SHOULD clear the + * persistence context AFTER the setup phase of the test and BEFORE we run the code under test. + * + *

{@code
+ *
+ * @Test
+ * @Transactional   // spring transactional
+ * @Rollback        // spring test rollback
+ * void myTestWithSpringRollback() {
+ *
+ *   // Test Setup: this MIGHT load entities into the persistence context
+ *   performTestSetup();
+ *
+ *   // clear out the persistence context
+ *   PersistenceContextAccess.clear();
+ *
+ *   // Act
+ *   performActionsWeAreTestingHere();
+ *
+ *   // Assert
+ *   assertThat(...)
+ *
+ * }
+ *
+ * }
+ */ +public class PersistenceContextAccess { + + /** + * Clear the persistence context of the current transaction. + *

+ * This is expected to be called after test setup phase and before + * the test executes the code we are looking to test - so + * AFTER "setup" and BEFORE "act". + */ + public static void clear() { + Transaction current = Transaction.current(); + if (current != null) { + SpiTransaction spiTransaction = (SpiTransaction) current; + PersistenceContext pc = spiTransaction.persistenceContext(); + if (pc != null) { + pc.clear(); + } + } + } +}