Spring数据和@Transactional在多次保存时不回滚
Spring Data and @Transactional does not rollback when there are multiple saves
我使用:
- Spring 数据 (4.x)
- HikariCP
- Hibernate(我使用 EntityManager)
考虑以下存储库:
public interface TestModelRepository extends JpaRepository<TestModel, Long> {
}
public interface TestModelRepository2 extends JpaRepository<TestModel2, Long> {
}
和以下服务:
@Service
static class Svc {
@Autowired
private TestModelRepository modelRepository;
@Autowired
private TestModelRepository2 modelRepository2;
@Transactional
public void insertWithException() {
assertThat(TransactionAspectSupport.currentTransactionStatus()).isNotNull();
modelRepository.save(new TestModel("any"));
modelRepository2.save(new TestModel2("unique"));
modelRepository2.save(new TestModel2("unique"));
}
}
存储库 2 中的第二次保存抛出 DataIntegrityViolationException,因为提供的值不是唯一的。事务应该回滚此方法中的所有内容,因为它带有 @Transactional 注释,但它不会。
TestModel 和 TestModel2 之一被保留。实际上,它们会在每次调用 save() 之后保存到数据库中,因此即使 @Transactional 方法尚未完成,这些值也会被插入到数据库中(我通过放置断点并登录数据库来验证它)。在我看来,autocommit 设置为 true,但我将其设置为 false(在 HikariCP 配置中)。
这是我基于 java 的配置(片段):
@Bean
PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
@Bean
JpaVendorAdapter vendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.MYSQL);
adapter.setDatabasePlatform(MySQL5Dialect.class.getName());
return adapter;
}
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource);
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
entityManagerFactory.setPackagesToScan(packagesToScan);
entityManagerFactory.setJpaProperties(properties);
主要问题:为什么事务不回滚所有内容?
其他问题:
- 什么时候应该将数据提交到数据库?事务何时提交或何时提交?
- 是在整个交易过程中保持连接,还是可以在交易中间将其返回到池中,然后在需要时请求另一个连接?
- Spring 数据在存储库上不需要@Transactional,那么事务参数是什么(传播和隔离)?
您可能在连接上或在数据库配置中有自动提交。我还注意到您正在使用 mysql。确保您的架构和表是 InnoDB 而不是 MyISAM
我使用:
- Spring 数据 (4.x)
- HikariCP
- Hibernate(我使用 EntityManager)
考虑以下存储库:
public interface TestModelRepository extends JpaRepository<TestModel, Long> {
}
public interface TestModelRepository2 extends JpaRepository<TestModel2, Long> {
}
和以下服务:
@Service
static class Svc {
@Autowired
private TestModelRepository modelRepository;
@Autowired
private TestModelRepository2 modelRepository2;
@Transactional
public void insertWithException() {
assertThat(TransactionAspectSupport.currentTransactionStatus()).isNotNull();
modelRepository.save(new TestModel("any"));
modelRepository2.save(new TestModel2("unique"));
modelRepository2.save(new TestModel2("unique"));
}
}
存储库 2 中的第二次保存抛出 DataIntegrityViolationException,因为提供的值不是唯一的。事务应该回滚此方法中的所有内容,因为它带有 @Transactional 注释,但它不会。
TestModel 和 TestModel2 之一被保留。实际上,它们会在每次调用 save() 之后保存到数据库中,因此即使 @Transactional 方法尚未完成,这些值也会被插入到数据库中(我通过放置断点并登录数据库来验证它)。在我看来,autocommit 设置为 true,但我将其设置为 false(在 HikariCP 配置中)。
这是我基于 java 的配置(片段):
@Bean
PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
@Bean
JpaVendorAdapter vendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabase(Database.MYSQL);
adapter.setDatabasePlatform(MySQL5Dialect.class.getName());
return adapter;
}
LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
entityManagerFactory.setDataSource(dataSource);
entityManagerFactory.setJpaVendorAdapter(vendorAdapter);
entityManagerFactory.setPackagesToScan(packagesToScan);
entityManagerFactory.setJpaProperties(properties);
主要问题:为什么事务不回滚所有内容?
其他问题:
- 什么时候应该将数据提交到数据库?事务何时提交或何时提交?
- 是在整个交易过程中保持连接,还是可以在交易中间将其返回到池中,然后在需要时请求另一个连接?
- Spring 数据在存储库上不需要@Transactional,那么事务参数是什么(传播和隔离)?
您可能在连接上或在数据库配置中有自动提交。我还注意到您正在使用 mysql。确保您的架构和表是 InnoDB 而不是 MyISAM