【问题标题】:Would transactions/spring Transaction propagation solve this concurrency issue?事务/弹簧事务传播会解决这个并发问题吗?
【发布时间】:2012-05-02 03:56:18
【问题描述】:

如果可以的话,我有几个关于 Spring 事务的问题。 假设我有这个 DAO 类:

public class MyDAO {

    /**
    * verifies if a certain record in DB contains 'True' in a certain Column named publishFlag
    */
    @Transactional
    public bloolean isBeingPublished(Long recordID){
    ...
    }

    /**
    * sets the record's publishFlag column to true indicating that it's being published
    */
    @Transactional
    public boolean setBeingPublished(Long recordID){
    ...
    }

}

下面的类使用它:

public class MyClass {

    @Autowired
    MyDAO dao;

    public void publishRecords(List<Long> ids){

        for(Long id : ids){
            if(!dao.isBeingPublished(id)){
                dao.setBeingPublished(id);
                //do something to publish the record
            }
        }

    }
}

我的问题是:

  • 首先,!dao.isBeingPublished(id)dao.setBeingPublished(id)是在同一个事务中执行还是在不同事务中执行?

  • 第二个问题是关于并发的,可以创建多个MyClass 实例并且可以并发调用publishRecord 方法,所以两个并发调用!dao.isBeingPublished(id) 可能都给出相同的结果,从而记录发布了两次! 我会考虑使publishRecords 同步,但应用程序可能部署在多个服务器上,这使得同步声明无用,因此我对事务提出疑问,因为数据库是部署在这些服务器上的应用程序之间的唯一共享资源。

我的问题的解决方案究竟是什么?我阅读了 Spring 的事务传播,发现 REQUIRES_NEW 会创建一个新事务,即使当前正在执行一个事务,但我仍然看不出这将如何解决我的问题。

提前感谢您的帮助。

【问题讨论】:

    标签: java spring concurrency transactions


    【解决方案1】:

    首先,!dao.isBeingPublished(id) 和 dao.setBeingPublished(id) 在同一个事务中执行或在 分开的?

    除非有一个用@Transactional 注释的方法在堆栈的更上层,否则它们将在单独的事务中发生,所以是的,您可能会出现竞争条件。

    如果我是你,我会抛弃 isBeingPublishedsetBeingPublished 来支持单个 @Transactional publishIfPossible 方法,该方法返回一个布尔值,它是否能够获取数据库行锁并执行发布操作。

    【讨论】:

    • 你的建议很有趣,但是如果我要创建这样一个方法,首先调用它会开始一个事务,这个方法会尝试发布记录,然后提交更改到数据库以便它们是可见的;在第一个事务提交更改之前对创建另一个事务的方法的另一个调用呢?那也会使唱片出版两次不?
    • 如果您配置了正确的事务隔离(并且您的数据库支持它...这不适用于 MySQL MyISM 表),第一个事务将持有一个行级锁,并且第二个事务将阻塞,直到该行级锁被释放。总的来说,DB确实很擅长管理锁和并发,所以最好让它自己处理细节,而不是自己动手!
    • 数据库管理系统是oracle,所以我认为支持不会成为问题;我会看看事务隔离谢谢。
    【解决方案2】:

    需要考虑的事情很少,DAO 是专注于对单个实体的操作,而 service 是专注于对一个或多个实体的操作,所以事务应该放在服务层,这样你就可以在没有任何事务的情况下重用 DAO 的操作,但是让服务决定何时开始和结束事务

    1. 不是单笔交易,而是两笔单独交易。
    2. 这是您当前设计的并发问题,请参阅以下建议。

    界面

    public interface MyClass {
        public void publishRecords(List<Long> ids);
    }
    

    实施

    @Service
    @Transactional(readOnly = false)
    class DefaultMyClass implements MyClass  {
    
        @Autowired
        MyDAO dao;
    
        // single transaction
        @Override
        public void publishRecords(List<Long> ids) {
            for(Long id : ids){
                if(!dao.isBeingPublished(id)){
                    dao.setBeingPublished(id);
                    //do something to publish the record
                }
            }
        }
    }
    

    DAO

    class MyDAO {
    
        public bloolean isBeingPublished(Long recordID){
            // bigbang
        }
    
        public boolean setBeingPublished(Long recordID){
            // bigbang
        }
    }
    

    使用上面的设计,这两个问题都解决了。

    【讨论】:

    • 感谢您的回复,该建议实际上会使它们在同一个事务中运行,但是,运行publishRecords单个事务 将是分开的,不是吗?在事务 A 提交其对 DB 的更改之前,另一个事务 B 可以启动并且并发问题仍然存在
    • @K-SaMa 你可以在JPA中使用@Version注解实现乐观锁,更多见here
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-27
    • 2015-06-30
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    • 2020-10-20
    相关资源
    最近更新 更多