java - 跨 JPA 和 JDBC 的 Spring @Transactional
问题描述
设置:
spring-boot-starter-parent:2.5.3
spring-boot-starter-data-jpa
(省略版本,由父级定义)- PostgreSQL(最新版本)
- 通过
spring.datasource
属性配置的一个数据源
描述:
我有一个使用JdbcOperations
( JdbcTemplate
) 将值插入表的方法。该表对另一个由 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
并将其返回给相关方法。
当有问题的方法尝试使用 插入自己的值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
立即写出插入,而不是稍后批量写入。
推荐阅读
- c# - C# Winform - 如何将我的对象的枚举属性绑定到 DataGridView 的组合框?
- android - 在MarkerClickListener 上显示警报对话框
- azure - ADF 管道计划信息
- r - 使用二次项和交互项的边距包错误
- swift - 节点的几何(来自 DAE 文件)仅在生产版本中为零
- elasticsearch - Kubernetes Kibana 操作员故障和 Nginx 入口超时
- python - 如何在 Python 中为 boucle 做随机数?
- kotlin - 从 kotlin-android-extensions 迁移到更新的视图绑定
- django - Django 到 Heroku 错误:ModuleNotFoundError
- c# - C# - 无法从 MSBuild 脚本更改 ApplicationVersion