【问题标题】:Different threads get same entity and don't see changes each other不同的线程获得相同的实体并且看不到彼此的变化
【发布时间】:2019-07-04 09:48:15
【问题描述】:

我有一张桌子products。在这张表中,我需要带有约束的is_active - only one row with the same type can be true.

我有通过检查保存新的Product 的服务:

@Service
public class ProductServiceImpl implements ProductService {

    private final ProductRepository productRepository;

    public ProductServiceImpl(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @Override
    public void save(Product product) {

        Product productInDb = productRepository.findOneByTypeAndIsActive(product.getType());

        if (productInDb != null)
            throw new AlreadyActiveException();

        product.setActive(true);
        productRepository.saveAndFlush(product);
    }
}

当我在几个线程中调用 save 方法并尝试检查活动产品时 - 在两个线程中 findOneByTypeAndIsActive 方法返回 productInDb is null 因为我在表中没有活动产品。 在每个线程中我设置product.setActive(true); 并尝试保存在数据库中。 如果我在 DB 中没有约束 - 我将两个产品都保存在 is_active = true 状态并且未执行此检查:

     if (productInDb != null)
        throw new AlreadyActiveException();

我的问题 - 我可以在不在数据库中添加约束的情况下解决这个问题吗? 而且上面的检查也没用?

【问题讨论】:

    标签: java multithreading hibernate jpa spring-data-jpa


    【解决方案1】:

    从我的角度来看,这不是最好的数据库表设计,在记录结构中具有is_active 标志和限制,表中只有一条记录可以同时是is_active

    您必须使用数据库架构约束,否则您必须用所有记录锁定整个表。如何锁定整个表以进行修改是特定于数据库的。我不认为 JPA 本身就支持这种锁。

    但你写道:

    我可以在不在 DB 中添加约束的情况下解决这个问题吗?

    不,对所有客户都严格保证是不可能的。

    但如果您只有一个应用程序使用此表 - 您可以使用本地的、应用程序特定的锁,例如,您可以在 @Service 级别创建 Read/Write java 锁。

    【讨论】:

      【解决方案2】:

      您的操作包含 2 个操作:

      1. 从数据库中获取实体

      2. 如果新实体不存在,则保存它

      你的问题是很少的线程可以同时启动这个操作并且看不到彼此的变化。这绝对不是你需要的。由几个动作组成的操作必须是原子的。

      如果我理解正确,您的规则是在数据存储区中仅保留同一 type 的 1 个 active 产品。这听起来像是一个数据一致性要求,应该在应用程序级别解决。

      解决问题的最简单的方法是在执行操作之前获取锁。可以通过synchronised 或显式锁定来解决:

      @Override
      public synchronised void save(Product product) {
      
          Product productInDb = productRepository.findOneByTypeAndIsActive(product.getType());
      
          if (productInDb != null)
              throw new AlreadyActiveException();
      
          product.setActive(true);
          productRepository.saveAndFlush(product);
      }
      

      【讨论】:

      • 我换个问题——把id换成Type(一点都不重要)
      • 您是否只需要 db 中相同 type 的 1 个 active 产品?
      • 如果我理解正确 - 我需要在数据库中添加约束。它比应用程序级同步更好吗?
      • "您是否只需要 db 中相同类型的 1 个有效产品?" - 是的
      猜你喜欢
      • 2021-06-07
      • 2021-12-19
      • 2020-03-11
      • 2014-03-14
      • 1970-01-01
      • 2015-01-23
      • 2013-07-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多