JPA:在阻止不同线程中的Read时读取并保留一个事务

时间:2013-12-25 16:51:11

标签: java multithreading java-ee jpa transactions

我的用例是1..N关系中的两个实体,让我们说Box中的Box和Items。 Box有一个属性“capacity”,用于指定Box中可以拥有的项目数。

我在做什么。我选择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;
        }
    }
}

我正在同时调用方法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

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

2 个答案:

答案 0 :(得分:3)

使用JPA,您可以添加锁定机制,因为版本2.0支持乐观和悲观锁定,在乐观版本中,您可以添加一个版本列,允许检查实体的正确版本是否正在发生变化,在另一个版本中手悲观锁使用数据库锁定句子。在您的情况下,您可以使用Query.setLockMode在查询中设置块。

在此article中,您可以获得更多信息

答案 1 :(得分:2)

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