Spring JPA 和 JDBC 中的@Transactional
Spring @Transactional across JPA and JDBC
设置:
spring-boot-starter-parent:2.5.3
spring-boot-starter-data-jpa
(省略版本,由父定义)
- PostgreSQL(最新版本)
- 通过
spring.datasource
属性配置的一个数据源
描述:
我有一个使用 JdbcOperations
(JdbcTemplate
) 将值插入 table 的方法。
此 table 对另一个 table 具有外键约束,后者由 JPA CrudRepository
.
管理
有问题的方法如下所示:
private static final String SAVE_SQL = "INSERT INTO table (entity_id, value) VALUES (?, ?) ON CONFLICT (entity_id, value) DO UPDATE SET value = EXCLUDED.value";
@Transactional
public void save(long id, String value) {
this.otherService.getOrCreateEntity(id);
this.jdbcOperations.update(SAVE_SQL, (ps) -> {
ps.setLong(id);// this is a foreign key to the JPA managed table
ps.setString(value);
});
}
otherService
的方法是这样的:
@Transactional
public Entity getOrCreateEntity(long id) {
final Optional<Entity> optionalEntity = this.crudRepository.findById(id);
Entity entity;
if (optionalEntity.isEmpty()) {
entity = new Entity();
entity.setId(id);
entity = this.crudRepository.save(entity);
} else {
entity = optionalEntity.get();
}
return entity;
}
我添加了以下日志记录设置以查看正在使用哪些事务:
logging:
level:
org.springframework.orm.jpa: DEBUG
org.springframework.transaction: DEBUG
调用save(long, String)
方法时,我可以看到创建了一个新事务。 otherService.getOrCreateEntity(long)
-Method 将新的 Entity
和 returns 插入到相关方法中。
当有问题的方法尝试使用 JdbcOperations
插入自己的值时,我得到一个 DataIntegrityViolationException
:violates foreign key constraint
.
这是事务日志:
2021-09-09 21:42:29.704 DEBUG 16077 --- [nio-9000-exec-7] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [TheJdbcOperationsUsingService.save]: PROPAGATION_REQUIRED,ISOLATION_READ_UNCOMMITTED
2021-09-09 21:42:29.704 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Opened new EntityManager [SessionImpl(2113410426<open>)] for JPA transaction
2021-09-09 21:42:29.704 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@1e809ae2]
-- Call to otherService.getOrCreateEntity(long) start
2021-09-09 21:43:17.710 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(2113410426<open>)] for JPA transaction
2021-09-09 21:43:17.711 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Participating in existing transaction
2021-09-09 21:43:17.712 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(2113410426<open>)] for JPA transaction
2021-09-09 21:43:17.713 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Participating in existing transaction
2021-09-09 21:43:17.719 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(2113410426<open>)] for JPA transaction
2021-09-09 21:43:17.720 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Participating in existing transaction
-- Call to otherService.getOrCreateEntity(long) finished
2021-09-09 21:43:51.374 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Initiating transaction rollback
2021-09-09 21:43:51.374 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Rolling back JPA transaction on EntityManager [SessionImpl(2113410426<open>)]
2021-09-09 21:43:51.376 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Closing JPA EntityManager [SessionImpl(2113410426<open>)] after transaction
我尝试在两侧使用不同的 Propagation
和 Isolation
设置,但没有成功。
您 运行 遇到的问题是 EntityManager
在事务关闭之前一直在内存中缓存未保存的更改,因此当您尝试通过 JDBC直接数据库还没看到
要解决这个问题,您可以使用更具体的接口 JpaRespository
,它有一个 saveAndFlush
方法指示 EntityManager
立即写出插入而不是批处理稍后再写。
设置:
spring-boot-starter-parent:2.5.3
spring-boot-starter-data-jpa
(省略版本,由父定义)- PostgreSQL(最新版本)
- 通过
spring.datasource
属性配置的一个数据源
描述:
我有一个使用 JdbcOperations
(JdbcTemplate
) 将值插入 table 的方法。
此 table 对另一个 table 具有外键约束,后者由 JPA CrudRepository
.
有问题的方法如下所示:
private static final String SAVE_SQL = "INSERT INTO table (entity_id, value) VALUES (?, ?) ON CONFLICT (entity_id, value) DO UPDATE SET value = EXCLUDED.value";
@Transactional
public void save(long id, String value) {
this.otherService.getOrCreateEntity(id);
this.jdbcOperations.update(SAVE_SQL, (ps) -> {
ps.setLong(id);// this is a foreign key to the JPA managed table
ps.setString(value);
});
}
otherService
的方法是这样的:
@Transactional
public Entity getOrCreateEntity(long id) {
final Optional<Entity> optionalEntity = this.crudRepository.findById(id);
Entity entity;
if (optionalEntity.isEmpty()) {
entity = new Entity();
entity.setId(id);
entity = this.crudRepository.save(entity);
} else {
entity = optionalEntity.get();
}
return entity;
}
我添加了以下日志记录设置以查看正在使用哪些事务:
logging:
level:
org.springframework.orm.jpa: DEBUG
org.springframework.transaction: DEBUG
调用save(long, String)
方法时,我可以看到创建了一个新事务。 otherService.getOrCreateEntity(long)
-Method 将新的 Entity
和 returns 插入到相关方法中。
当有问题的方法尝试使用 JdbcOperations
插入自己的值时,我得到一个 DataIntegrityViolationException
:violates foreign key constraint
.
这是事务日志:
2021-09-09 21:42:29.704 DEBUG 16077 --- [nio-9000-exec-7] o.s.orm.jpa.JpaTransactionManager : Creating new transaction with name [TheJdbcOperationsUsingService.save]: PROPAGATION_REQUIRED,ISOLATION_READ_UNCOMMITTED
2021-09-09 21:42:29.704 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Opened new EntityManager [SessionImpl(2113410426<open>)] for JPA transaction
2021-09-09 21:42:29.704 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@1e809ae2]
-- Call to otherService.getOrCreateEntity(long) start
2021-09-09 21:43:17.710 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(2113410426<open>)] for JPA transaction
2021-09-09 21:43:17.711 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Participating in existing transaction
2021-09-09 21:43:17.712 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(2113410426<open>)] for JPA transaction
2021-09-09 21:43:17.713 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Participating in existing transaction
2021-09-09 21:43:17.719 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Found thread-bound EntityManager [SessionImpl(2113410426<open>)] for JPA transaction
2021-09-09 21:43:17.720 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Participating in existing transaction
-- Call to otherService.getOrCreateEntity(long) finished
2021-09-09 21:43:51.374 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Initiating transaction rollback
2021-09-09 21:43:51.374 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Rolling back JPA transaction on EntityManager [SessionImpl(2113410426<open>)]
2021-09-09 21:43:51.376 DEBUG 16327 --- [nio-9000-exec-6] o.s.orm.jpa.JpaTransactionManager : Closing JPA EntityManager [SessionImpl(2113410426<open>)] after transaction
我尝试在两侧使用不同的 Propagation
和 Isolation
设置,但没有成功。
您 运行 遇到的问题是 EntityManager
在事务关闭之前一直在内存中缓存未保存的更改,因此当您尝试通过 JDBC直接数据库还没看到
要解决这个问题,您可以使用更具体的接口 JpaRespository
,它有一个 saveAndFlush
方法指示 EntityManager
立即写出插入而不是批处理稍后再写。