Java JPA:在一个事务中读取并持久化,同时阻止在不同线程中读取

Java JPA:在一个事务中读取并持久化,同时阻止在不同线程中读取,java,multithreading,jakarta-ee,jpa,transactions,Java,Multithreading,Jakarta Ee,Jpa,Transactions,我的用例是1..N关系中的两个实体,比如Box和Box中的项。Box有一个属性“capacity”,它指定我可以在Box中拥有多少项 我实际上在做什么。我选择count of all Items in Box,当项目数小于Box.capacity时,我将保留该项目 问题是当我同时访问我的方法时。同时调用方法两次将导致两次读取都可以并发运行,当进入写入阶段时,两个线程都会将该项写入数据库 我的代码: @Entity public class Box { private int capaci

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

我实际上在做什么。我选择count of all Items in Box,当项目数小于Box.capacity时,我将保留该项目

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

我的代码:

@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;
        }
    }
}

或者有没有更好的方法来处理这个问题?如何检查实体的计数并在没有并发问题的情况下保持它?

使用JPA,您可以添加锁定机制,因为版本2.0支持乐观锁定和悲观锁定,在乐观版本中,您可以添加一个版本列,允许检查实体的正确版本是否正在更改,另一方面,悲观锁使用数据库锁语句。在本例中,您可以使用
query.setLockMode
在查询中设置块


在这里,您可以获得更多信息

使用JPA,您可以添加锁定机制,因为版本2.0支持乐观和悲观锁定,在乐观版本中,您可以添加一个版本列,用于检查实体的正确版本是否正在更改,另一方面,悲观锁使用数据库锁语句。在本例中,您可以使用
query.setLockMode
在查询中设置块


在这方面,您可以获得更多信息

我相信ORM框架将不支持您自己的业务逻辑。我更喜欢使用java锁机制,无论是新的锁规范,还是旧的使用同步块的好方法,通过在块中覆盖读写操作。

我相信ORM框架将不支持您自己的业务逻辑。我更喜欢使用java锁机制,无论是新的锁规范还是旧的使用同步块的好方法,通过在块中覆盖读写操作。

谢谢,我忘记了它可能是那么简单,并且正在考虑复杂的数据库锁。它可以工作,但您必须检查一些问题,例如,您锁定的对象是什么??,如果您锁定或同步没有并发性的方法,该方法将同时执行一次,另一种情况是,如果select为两个不同的调用返回不同的值,但锁定了块语句而不是查询返回的实体,则无论值如何,始终锁定对象。这就是为什么我不建议你使用这种方法,我建议你检查JPA的锁定机制,它是为某个东西创建的。谢谢,我忘了它可能很简单,并且考虑了复杂的数据库锁定。它可以工作,但你必须检查一些问题,例如,你锁定了什么对象??,如果锁定或同步没有并发性的方法,该方法将同时执行一次,另一种情况是,如果select为两个不同的调用返回不同的值,但锁定了块语句,而不是查询返回的实体,则始终锁定对象,而不考虑值。这就是为什么我不建议你使用这种方法,我建议你检查JPA的锁定机制,它是为一些事情创建的。我决定用最简单的方法,使用同步块。但非常感谢你们的文章,我一定会从中学到一些东西。我决定用最简单的方法,使用同步块。但非常感谢你们的文章,我一定会从中学到一些东西。
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