【问题标题】:wrapping multiple calls in a transaction in spring boot application?在 Spring Boot 应用程序的事务中包装多个调用?
【发布时间】:2016-09-19 21:18:00
【问题描述】:

我正在使用 jdbctemplate 在我的 spring 应用程序中对 db 执行查询。

这里是用@Transactional注解的方法

@Transactional
public boolean doSomething(){    
    try {
        jdbcTemplate.update(sql1); //1
        jdbcTemplate.update(sql2); //2
        jdbcTemplate.update(sql3); //3
        return true; 
    } catch (Exception ex){
        return false;
    }
}

我的问题是,如果 1 和 2 成功而 3 失败,那么 1 和 2 上的事务是否会回滚?我该如何测试?

另外,将布尔值作为返回值是否是指示交易状态的好习惯?

【问题讨论】:

    标签: spring spring-boot spring-transactions


    【解决方案1】:

    不!如果你捕捉到异常,你的 sql 将不会回滚!!!

    这不会触发回滚。您需要删除 try-catch。但是是的,如果抛出运行时异常,这些将按预期回滚。请参考以下文档。

    @事务设置

    http://docs.spring.io/autorepo/docs/spring/4.2.x/spring-framework-reference/html/transaction.html#transaction-declarative-attransactional-settings

    您会在下面几行看到回滚是由任何 RuntimeException 触发的。因此,如果您捕获异常,它不会触发回滚,它只会在您返回 false 时结束事务。进一步注意,如果你抛出一个已检查的异常,它不会回滚,所以不要试图通过抛出一个异常来解决这个问题,以做任何你打算用该布尔返回值做的事情。

    我自己没有尝试过,但是如果您在 @Transactional 上设置 rollbackFor 属性,或者只是在此方法之外捕获 RuntimeException,您似乎可以回滚检查的异常?或者抛出你自己的运行时异常?我把它留给你。

    如上所述,这也适用于存储库,您可以在此处查看示例

    Spring Data(Repositories) 事务

    http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions

    【讨论】:

    • 我错过了异常咀嚼的效果,好地方。
    【解决方案2】:

    如果1和2成功,3失败,1和2上的事务会回滚还是不回滚?

    如果第三个操作失败,第一个和第二个操作将被回滚。但如果它们是同一事务或事务上下文的一部分。

    例如,如果您将事务管理器定义为 DataSourceTransactionManager,它将回滚同一连接的 JDBC 操作。在您的情况下,您在同一方法中拥有所有三个操作并使用相同的 jdbcTemplate,因此它将回滚您的其他两个事务。

    如果让一条sql指令失败,你可以测试一下,例如:

    1. 插入正确
    2. 插入正确
    3. 插入,但您可以尝试在数字列中插入字符串,或在非空列中插入空值。

    你可以在这里看到更多 http://www.journaldev.com/2603/spring-transaction-management-jdbc-example

    http://docs.spring.io/autorepo/docs/spring/4.2.x/spring-framework-reference/html/transaction.html

    关于返回boolean我觉得还是抛出异常比较好,它会自动回滚事务。

    【讨论】:

    • 如果我将 jdbcTemplate 调用打包到存储库中会怎样?例如,我的代码是 repository.callUpdate();respository.callInsert();repository.callDelete(); 但在下面,存储库使用相同的 jdbcTemplate
    • 在这种情况下最好使用 PlatformTransactionManager 并且您的应用服务器需要支持 JTA
    • 只是好奇为什么,我仍然将所有存储库调用包装在@Transactional
    • 我不太确定这些调用是否在同一个会话中进行。我认为每次调用都会创建一个新会话。因此,如果需要更广泛的事务上下文,则需要 JTA。
    • 另外,如果你捕捉到异常,则 tx 不会回滚
    【解决方案3】:

    如果 3 失败,则 1 和 2 将回滚。这是事务(工作单元)的核心点。
    基于@Zergleb 的回答的重要更正:仅当您抛出异常时。在您的代码中,您正在咀嚼异常,因此 1 和 2 将持续存在,因为事务没有“看到”异常它不知道有什么问题。

    为了测试,在sql3中放一些无效的sql,看看会发生什么。

    我通常不会返回一个布尔值来表示成功。成功是方法的成功完成,你应该让异常冒泡或将异常包装在更合适的东西中并抛出它,如果你的场景表明这样。

    例如:

    @Transactional
    public void doSomething(){
        try {
            jdbcTemplate.update(sql1); //1
            jdbcTemplate.update(sql2); //2
            jdbcTemplate.update(sql3); //3
        } catch (Exception ex){
            throw new MyCustomPersistenceException("Could not doSomething", e);
        }
    }
    

    【讨论】:

    • 所以你的意思是不要尝试/捕获@Transactional 方法并让调用者处理异常?另外如果我不返回布尔值,我应该返回什么?
    • 你可以尝试/捕捉,我会添加一个例子来回答。至于返回类型,imo这在您的情况下应该是无效的。在某些情况下,如果持久化改变状态(例如分配一个 id),插入/更新类型的方法可能会返回持久化对象。
    • 另外,@Zergleb 的回答指出了我错过的一些非常重要的事情。如果不抛出异常,事务代码将看不到问题,并提交。换句话说,它只有在遇到异常时才会回滚。
    猜你喜欢
    • 1970-01-01
    • 2019-12-18
    • 2015-11-25
    • 2020-01-19
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 2017-04-30
    相关资源
    最近更新 更多