Java JPA:解析读取后写入的竞争条件

Java JPA:解析读取后写入的竞争条件,java,hibernate,jpa,jta,java-persistence-api,Java,Hibernate,Jpa,Jta,Java Persistence Api,我想用散列存储唯一的实体。实体由生成的id和必须唯一的哈希值组成。 我使用以下代码执行此操作: @Stateless @LocalBean public class srvcEntity { @PersistenceContext(unitName = "mine") private EntityManager em; private Long save(byte[hash]) { List<Entity> list = Entity.fi

我想用散列存储唯一的实体。实体由生成的id和必须唯一的哈希值组成。 我使用以下代码执行此操作:

@Stateless
@LocalBean
public class srvcEntity {

    @PersistenceContext(unitName = "mine")
    private EntityManager em;

    private Long save(byte[hash]) {
        List<Entity> list = Entity.findByHash(hash, em); // using TypedQuery.getResultList()
        if (list != null && list.size() > 0) {
            entityId = list.get(0).getId();
        } else {
            Entity e = Entity.save(hash, em); // using em.persist()
            if (e != null) {
                entityId = e.getId();
            }
        }
        return entityId;
    }
}
@无状态
@本地豆
公共类srvcEntity{
@PersistenceContext(unitName=“我的”)
私人实体管理者;
专用长保存(字节[哈希]){
List List=Entity.findByHash(hash,em);//使用TypedQuery.getResultList()
if(list!=null&&list.size()>0){
entityId=list.get(0.getId();
}否则{
实体e=Entity.save(散列,em);//使用em.persist()
如果(e!=null){
entityId=e.getId();
}
}
返回entityId;
}
}
在我同时存储了很多实体之前,这种方法非常有效。然后,竞争条件可能导致这样一种情况:在读取时,找不到具有给定哈希的实体,但在保存时,我得到了重复哈希的ConstraintViolationException

因为我的代码存在于具有基于容器的事务的bean中,所以在保存失败后,我无法再次读取


如何解决此争用条件?

直接的方法是捕获异常,然后再次执行读取


您还可以使用一个可防止竞争状况的。这在您不想执行某些操作(可能两次)的情况下非常有用,但它也涉及到锁定(尽管Guava
Striped
类允许您配置锁的数量,因此吞吐量非常好)。

直接的方法是捕获异常,然后再次执行读取


您还可以使用一个可防止竞争状况的。这在您不想执行某些操作(可能两次)的情况下非常有用,但它也涉及到锁定(尽管Guava
Striped
类允许您配置锁的数量,因此吞吐量非常好)。

EJB方法是同步的。如果您只运行应用程序的一个实例(它不是集群的!),那么

@独生子女


可以解决此问题,因为此代码应该只有一个入口点,这将消除竞争条件的可能性

EJB方法是同步的。如果您只运行应用程序的一个实例(它不是集群的!),那么

@独生子女


可以解决此问题,因为此代码应该只有一个入口点,这将消除竞争条件的可能性

正如我已经说过的,由于事务已经标记为回滚,所以在失败的写入之后不可能进行读取。但是是的,那是最简单的方法,我已经尝试过了,但失败了;-)您可以围绕这一点进行设计,但使用奇怪的
Entity.save(hash,em)
样式代码可能会比较困难。您为什么要在
EntityManager
周围创建这样一个奇怪的包装器?在任何情况下,第二个选项甚至可以防止尝试插入重复的值。除了与其余实体“追溯拟合”之外,没有其他特殊原因。如果没有em.persist()的包装器,您将如何操作取决于您现有的代码。例如,对于服务或dao类和
REQUIRES\u NEW
事务,您可以让事务回滚,而不会影响外部事务。当然,这与您当前的体系结构不太兼容。因此,基本上我需要另一个围绕save的bean来获得一个允许失败但不会终止“父”事务的额外事务?正如我已经说过的,在失败的写入之后不可能进行读取,因为事务已标记为回滚。但是是的,那是最简单的方法,我已经尝试过了,但失败了;-)您可以围绕这一点进行设计,但使用奇怪的
Entity.save(hash,em)
样式代码可能会比较困难。您为什么要在
EntityManager
周围创建这样一个奇怪的包装器?在任何情况下,第二个选项甚至可以防止尝试插入重复的值。除了与其余实体“追溯拟合”之外,没有其他特殊原因。如果没有em.persist()的包装器,您将如何操作取决于您现有的代码。例如,对于服务或dao类和
REQUIRES\u NEW
事务,您可以让事务回滚,而不会影响外部事务。当然,这与您当前的体系结构不太兼容。因此,基本上我需要另一个围绕save的bean来获得一个允许失败但不会杀死“父级”的额外事务事务?不幸的是,我运行了多个应用程序服务器来接收这些实体。不幸的是,我运行了多个应用程序服务器来接收这些实体。