不同的线程具有相同的实体,彼此看不到变化

时间:2019-07-04 09:48:15

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

我有一张桌子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();

我的问题-我可以在不增加数据库约束的情况下解决此问题吗? 上面的检查没用吗?

2 个答案:

答案 0 :(得分:1)

您的操作包含2个操作:

  1. 从数据库获取实体

  2. 保存不存在的新实体

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

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

解决问题的最幼稚的选择是在执行操作之前获取一个锁。可以使用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);
}

答案 1 :(得分:1)

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

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

但是您写道:

  

我可以在不增加数据库约束的情况下解决此问题吗?

不,不可能为所有客户提供严格的保证。

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