java - JPA:读取并坚持一个事务,同时阻塞不同线程中的读取

标签 java multithreading jakarta-ee jpa transactions

我的用例是 1..N 关系中的两个实体,比方说 Box 和 Items in Box。 Box 有一个属性“capacity”,它指定了我可以在 Box 中拥有多少 Items。

我实际上在做什么。我选择了 Box 中所有 Items 的计数,当 Items 的数量小于 Box.capacity 时,我保留了 Item。

问题是当我同时访问我的方法时。同时调用方法两次将导致读取可以并发运行,当进入写入阶段时,两个线程都将 Item 写入数据库。

我的代码:

@Entity
public class Box {
    private int capacity;
}

@Entity
public class Item {
    @ManyToOne
    Box box;
}

@Stateless
public class BoxManager {
    @PersistenceContext
    EntityManager em;

    public Item persistItem(Item item) {
        Box box = item.getBox();
        Query query = em.createQuery("SELECT COUNT(i) FROM Item i WHERE i.box = :box");
        query.setParameter("box", box);

        int itemCount =  ((Number)typedQuery.getSingleResult()).intValue();

        // We can simulate concurrent problem putting Thread.sleep() here.

        if (itemCount < box.getCapacity()) {
            return em.persist(item);
        } else {
            return null;
        }
    }
}

我正在同时调用方法 persistItem

问题是如何防止另一个线程在第一个线程持久化第一个项目之前读取错误数量的项目?

有问题的场景是:

Thread 1: Reads count of items
Thread 2: Reads count of items
Thread 1: Writes Item to database
Thread 2: Also Writes Item to database

或者有更好的方法来处理这个问题吗?如何在没有并发问题的情况下检查实体的数量并将其持久化?

最佳答案

使用 JPA,您可以添加锁定机制,从 2.0 版本开始,您就支持乐观和悲观锁定,在乐观版本中,您可以添加一个版本列,以检查实体的正确版本是否正在更改,在其他版本中hand 悲观锁使用数据库锁定语句。在您的情况下,您可以使用 Query.setLockMode 在查询中设置一个 block 。

在此article你可以获得更多信息

关于java - JPA:读取并坚持一个事务,同时阻塞不同线程中的读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20774930/

相关文章:

java - 键盘没有在上方滚动 EditText

C++ 在后台读取文件

java - 我应该升级 JBoss 还是完全避开它并迁移到 Tomcat?

eclipse - 此 URL 不支持具有简单 servlet 的 HTTP 方法 GET

java - 配置 war 内部依赖工件的属性

java - 从路径中检索上一级目录名称

java - 使用camel-bindy或beanio解析键值文本文件

java - 简化正则表达式

python - 在线程中捕获键盘中断或处理信号

java - 请求-响应消息格式和算法设计