Spring JPA 和 JDBC 中的@Transactional

Spring @Transactional across JPA and JDBC

设置:

描述:

我有一个使用 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 插入自己的值时,我得到一个 DataIntegrityViolationExceptionviolates 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

我尝试在两侧使用不同的 PropagationIsolation 设置,但没有成功。

您 运行 遇到的问题是 EntityManager 在事务关闭之前一直在内存中缓存未保存的更改,因此当您尝试通过 JDBC直接数据库还没看到

要解决这个问题,您可以使用更具体的接口 JpaRespository,它有一个 saveAndFlush 方法指示 EntityManager 立即写出插入而不是批处理稍后再写。