What happens to the database when you annotate a Spring integration test with @Transactional, and why?
answer
- Rolls back by default, pass or fail
- TransactionalTestExecutionListener does it
- @DataJpaTest transactional; @SpringBootTest not
- Isolation + no teardown
- Rollback != exception-driven
basics
~20 sSpring starts a transaction before the test method and automatically rolls it back afterward, even if the test passes. So any data written during the test is undone, keeping the database clean and each test isolated.
solid answer
~40 sPutting org.springframework.transaction.annotation.@Transactional on a test class or method makes Spring's TestContext framework wrap each test method in a transaction that is rolled back by default when the method finishes — pass or fail. This is done by the TransactionalTestExecutionListener. The point is isolation and cleanup: writes made during the test never commit, so tests don't leak state into one another and you avoid manual teardown. Note it's the *test's* @Transactional (from spring-tx), not the web @Transactional on your service — though it's the same annotation. Slices like @DataJpaTest are meta-annotated with it, so they roll back automatically; @SpringBootTest is not transactional unless you add it. The rollback is intentional and independent of whether an exception was thrown.
code
java · 25 lines@DataJpaTest // meta-annotated with @Transactional -> rolls back automatically
class UserRepositoryTest {
@Autowired UserRepository users;
@Test
void savesUser() {
users.save(new User("[email protected]"));
assertThat(users.count()).isEqualTo(1);
// No @AfterEach cleanup: the transaction is rolled back after this method,
// so the next test starts with an empty table.
}
}
@SpringBootTest
@Transactional // NOT transactional by default -> add this to get rollback
class OrderServiceIntegrationTest {
@Autowired OrderService service;
@Test
void placingOrderWritesRow() {
service.place(new Order(...));
// rolled back at end of method
}
}go deeper
Must know: @Transactional test = automatic rollback, keeps DB clean, no manual cleanup.
Should know rollback is default regardless of pass/fail, and which slices are transactional vs @SpringBootTest.
Explains the listener, single-transaction-per-method semantics, and the disambiguation of multiple tx managers.
Frames rollback tests as an isolation strategy with trade-offs (cache masking, no real commit) and decides when NOT to use them.
## The mechanism When you place `@Transactional` (from `org.springframework.transaction.annotation`) on a test class or test method, the Spring TestContext Framework's **`TransactionalTestExecutionListener`** does the following around each test method: 1. Before the method: starts a new transaction using the configured `PlatformTransactionManager`. 2. Runs the test method inside that transaction. 3. After the method: **rolls the transaction back by default** — regardless of whether the test passed or threw. So every INSERT/UPDATE/DELETE performed during the test is undone. The database is left exactly as it was before the test ran. ## Why this is the default - **Isolation** — tests don't see each other's writes; order-independence. - **No teardown code** — you don't have to write `@AfterEach` cleanup to delete rows. - **Speed** — rollback is generally cheaper than commit + cleanup. ## Key clarifications - The rollback is **not** triggered by an exception. A perfectly green test still rolls back. This surprises beginners who expect a passing test to persist data. - It is the **same annotation** you use on services (`@Transactional`), but here it is interpreted by the test framework, not by the AOP proxy around a bean. - **Test slices**: `@DataJpaTest`, `@DataJdbcTest`, `@JdbcTest` are meta-annotated with `@Transactional`, so they roll back automatically. `@SpringBootTest` is **not** transactional — if you want rollback there, add `@Transactional` yourself. - Only one transaction manager is used. If the context has more than one `PlatformTransactionManager`, you must disambiguate (e.g. `@Transactional("txManagerBeanName")` or a `TransactionManagementConfigurer`). ## Gotcha to be aware of Because the whole test runs in **one** transaction, a `save` followed by a `find` may be served from the JPA first-level cache without a real SQL round-trip, and constraint violations may not surface until a flush. This can mask bugs — covered in more advanced questions on this topic. ## When to override Use `@Commit` / `@Rollback(false)` when you deliberately want the data to persist (rare — e.g. debugging, or a multi-step workflow across transactions).
- Does the transaction roll back only when the test fails?No. The default is to roll back on every transactional test method, whether it passes or throws. Rollback is not tied to test success or to exceptions.
- Is @SpringBootTest transactional out of the box?No. Full-context @SpringBootTest is not transactional; you must add @Transactional to get automatic rollback. Slice tests like @DataJpaTest already include it.
saying these in an interview costs you the question
- Claiming the transaction only rolls back when the test throws an exception
- Thinking data written in a @DataJpaTest is permanently persisted
- Assuming @SpringBootTest rolls back automatically like @DataJpaTest