如何在Spring Boot测试中提交变更并在测试后回滚?
解决方案:通过
flush触发数据库约束,同时保留事务回滚 你的核心矛盾在于:默认事务下JPA延迟写入导致唯一约束不生效,而取消事务后数据无法回滚。解决思路是保留@DataJpaTest的默认事务行为(测试后自动回滚),同时强制同步持久化上下文到数据库,触发约束检查。
具体步骤
- 移除类级别上的
@Transactional(propagation = Propagation.NOT_SUPPORTED)注解,恢复每个测试方法的独立事务(默认测试结束后自动回滚)。 - 在需要验证唯一约束的测试中,使用
saveAndFlush()替代save(),或在save()后调用flush(),强制将变更同步到数据库,触发唯一约束校验。
修改后的测试类代码
package com.example.springdata.repositories; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.Optional; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.dao.DataIntegrityViolationException; import com.example.springdata.entities.UserEntity; import io.zonky.test.db.AutoConfigureEmbeddedDatabase; import io.zonky.test.db.AutoConfigureEmbeddedDatabase.DatabaseProvider; @DataJpaTest @AutoConfigureEmbeddedDatabase(provider = DatabaseProvider.ZONKY) class UserRepositoryIntegrationTests { @Autowired private UserRepository userRepository; @Test void userIsCreatedAndRead() { UserEntity user = createEntity1(); userRepository.save(user); assertThat(user.getId()).isNotNull(); Optional<UserEntity> optionalUser = userRepository.findById(user.getId()); assertThat(optionalUser).isPresent(); optionalUser.ifPresent(foundUser -> assertThat(foundUser).usingRecursiveComparison().isEqualTo(user)); } @Test void emailIsUnique() { UserEntity user1 = createEntity1(); // 保存并立即同步到数据库,触发约束检查 userRepository.saveAndFlush(user1); UserEntity user2 = createEntity2(); user2.setEmail(user1.getEmail()); // 再次保存并同步,触发重复邮箱的约束异常 assertThrows(DataIntegrityViolationException.class, () -> userRepository.saveAndFlush(user2)); } private UserEntity createEntity1() { UserEntity user = new UserEntity(); user.setLogin("john"); user.setFirstName("John"); user.setLastName("Doe"); user.setEmail("john.doe@example.com"); return user; } private UserEntity createEntity2() { UserEntity user = new UserEntity(); user.setLogin("jane"); user.setFirstName("Jane"); user.setLastName("Smith"); user.setEmail("jane.smith@example.com"); return user; } }
原理说明
- @DataJpaTest默认会给每个测试方法创建独立事务,测试结束后自动回滚,保证测试间数据隔离。
- JPA默认延迟写入:
save()仅将实体放入持久化上下文,不会立即执行SQL。saveAndFlush()会强制将待处理的SQL发送到数据库,触发唯一约束校验。 - 这样既保证了
emailIsUnique测试能触发数据库异常,又让每个测试结束后数据自动回滚,不会影响其他测试用例。
内容的提问来源于stack exchange,提问作者Denis
相关产品推荐
相关产品推荐

