@Transactional 注释未按预期工作

Posted

技术标签:

【中文标题】@Transactional 注释未按预期工作【英文标题】:@Transactional annotation not working as expected 【发布时间】:2017-09-06 16:50:05 【问题描述】:

我需要一些帮助来尝试调试我的 Spring Boot 应用程序的事务管理为什么不起作用。

基本思想是我有 2 个表,我想在其中写一些东西。当 2 个表中的一个出现问题时,应该回滚事务并且不应该将任何内容写入数据库。

下面是简化版的代码:

@Transactional
public void archiveTask(String taskId) 

    OffloadedRun run = new OffloadedRun();
    run.setStartDateTime(LocalDateTime.now());
    calculationRunRepository.save(run);

    List<SingleContractCalculationResults> activeResults = contractCalculationResultAccessService.get(taskId);

    for (SingleContractCalculationResults result : example) 
        for (Map.Entry<String, ContractResults> entry : result.getResultsPerScenario().entrySet()) 
            String scenario = entry.getKey();
            ContractResults results = entry.getValue();

            OffloadedCalculationResult offloadedCalculationResult = new OffloadedCalculationResult();

//                offloadedCalculationResult.setOffloadedRun(run);
            offloadedCalculationResult.setContractId(result.getContractId());
            calculationResultRepository.save(offloadedCalculationResult);
        
    

我执行保存方法的类是 Spring Data JPA 存储库,其定义如下:

public interface CalculationRunRepository extends JpaRepository<OffloadedRun, String> 

我注释掉的行是必填列。我这样做是为了强制执行 ConstraintViolationException 来测试在第二个表中保存某些内容时发生的异常情况。

发生的情况是第一个实体已成功保存,这是不应该发生的。我正试图弄清楚这是为什么。

我的 Spring Boot 应用程序配置了 @EnableTransactionManagement 以在我自己的服务中启用 @Transactional 注释(比如这个)。

我将 org.springframework.transaction.interceptor 的日志记录级别更改为 TRACE 以查看发生了什么:

o.s.t.i.TransactionInterceptor           : Getting transaction for [be.sodemo.calculator.offloading.TaskArchiverImpl.archiveTask]
o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
o.s.t.i.TransactionInterceptor           : Completing transaction for [be.sodemo.calculator.offloading.TaskArchiverImpl.archiveTask]

o.h.e.j.s.SqlExceptionHelper             : SQL Error: 1048, SQLState: 23000
o.h.e.j.s.SqlExceptionHelper             : Column 'run_id' cannot be null
o.h.e.j.b.i.AbstractBatchImpl            : HHH000010: On release of batch it still contained JDBC statements
o.a.c.c.C.[.[.[.[dispatcherServlet]      : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause

com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 'run_id' cannot be null
    at sun.reflect.GeneratedConstructorAccessor2599.newInstance(Unknown Source) ~[?:?]
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java) ~[?:1.8.0_102]
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[?:1.8.0_102]
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:425) ~[mysql-connector-java-5.1.40.jar:5.1.40]

之后

如果事务管理正常工作,我不确定日志记录会是什么样子,但它似乎正在完成每个事务。

有没有人知道我接下来可以尝试什么来看看出了什么问题?

编辑:到目前为止,我一直在使用 MySQL 数据库。我注意到,当我在 H2 数据库上测试完全相同的代码时,回滚似乎按预期工作。我看到的唯一区别是它引发了另一个特定于供应商的异常。

我已经尝试在 @Transactional 注释上显式设置 rollbackFor 属性,如下所示:

@Transactional(rollbackFor = Exception.class, MySQLIntegrityConstraintViolationException.class)

但即使这样也不会导致回滚。

编辑:

这些是我与 JPA/Hibernate 相关的 Spring Boot 设置:

spring:
  jpa:
    hibernate:
      ddl-auto: none
      dialect: org.hibernate.dialect.MySQL5Dialect
    database: mysql
    properties:
      hibernate:
        order_inserts: true
        jdbc:
          batch_size: 50
  datasource:
    url: jdbc:mysql://localhost/local-test-db
    driver-class-name: com.mysql.jdbc.Driver

【问题讨论】:

您能否检查@Transactional 注释的导入?确保它不是从 javax 包中导入的,而是从 spring 包中导入的。 这是注解的spring版本:org.springframework.transaction.annotation.Transactional 能否请您包含calculationRunRepository.save(run) 的方法?可能是你这里有不同的事务传播。 我将 spring 数据用于我的存储库。我添加了一个小例子:) 显示您的休眠配置...此外,在使用 MySQL 时,请确保您实际使用支持事务的表类型...如果您使用非 InnoDB 方言并使用休眠来创建模式事务是无用的因为 MyISAM 不支持事务。 【参考方案1】:

你必须使用 org.springframework.orm.hibernate4.HibernateTransactionManager。

您可能正在使用 org.springframework.orm.jpa.JpaTransactionManager。

【讨论】:

【参考方案2】:

我不确定您为什么在存储库 中使用 String 作为 id 类型。您的 OffloadedRun 域具有 String 类型的 id?它应该与您在 OffloadedRun 域中的 id 字段的类型匹配。确保案件。确认, 您能否也发布您的 OffloadedRun 域代码?

【讨论】:

以上是关于@Transactional 注释未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

k8s nginx 入口重写目标注释未按预期工作

带有 Kotlin 的 Spring Boot - @Value 注释未按预期工作

HTML 表上的“querySelectorAll”未按预期工作

带有一个 goto 标签的 C 代码未按预期工作

实体未按预期提交(Hibernate ORM with Panache)

Spring 忽略 @Transactional 注释