在同一资源上具有悲观锁的并行事务

问题描述

我有以下代码


@Entity
public class Foo {
  @Id
  private long id;
  private int status;

  // getters and setters
}

@ApplicationScoped
public class FooRepository {
  @PersistenceContext(unitName="fooPU")
  private EntityManager em;

  public Foo get(long id) {
    return em.find(Foo.class,id);
  }
  
  // added by me
  public Foo getAndLock(long id) {
    Map<String,Object> props = new HashMap<>();
    props.put("javax.persistence.query.timeout",60000);
    return em.find(Foo.class,id,LockModeType.pessimistic_WRITE,props);
  }

  public Foo update(Foo f) {
    return em.merge(f);
  }
}

@Stateless
public class FooService {
  @Inject
  private FooRepository fooRepository;

  public void doStuff(long id) {
    System.out.console("attempting to lock");
    Foo f = fooRepository.getAndLock(id);
    System.out.println("got lock,status is = " + f.getStatus());
    if (f.getStatus() == 200) {
      doStuffwithFoo(f);
    }
  }

  private void doStuffWithFoo(Foo f) {
    // slow code,500-5000 ms
    System.out.println("doing heavy work");
    f.setStatus(400);
    fooRepository.update(f);
    System.out.println("done,new status is = " + f.getStatus());
  }
}

@WebServlet(name="...",urlPatterns={"..."})
public class FooServlet extends AbstractServlet {
  @Inject
  private FooService fooService;

  @Override
  public void doGet(HttpServletRequest req,HttpServletResponse resp){
    System.out.println("in servlet");
    fooService.doStuff(Long.parseLong(req.getParameter("fooId")));
  }

}

其他信息:

  • persistence.xml将事务类型定义为JTA(因此,由容器管理)
  • 将数据源(在JBoss中设置)上的事务隔离级别设置为认值(在Web界面中显示为空,但我相信是REPEATABLE_READ

这个想法是,使用特定的fooId参数调用servlet将从数据库获取Foo的匹配实例,并且只有当它具有status == 200时,它才应执行{{ 1}}。此方法通常会花费相对较长的时间,但最终应将该Foo实例的状态更新为doStuffWithFoo(在快乐的路径中)。

当两个请求几乎同时调用servlet时存在一个问题:两个请求都获得正确的400实例,但是因为它们都看到状态Foo,所以它们都执行200。这不应该发生。

我试图使用doStuffWithFoo锁来解决此问题(在名为pessimistic_WRITE的服务之前,我在存储库中添加getAndLock方法)。据我了解,该锁定应防止第二个请求只要被锁定就读取数据,并且(由于超时)要等到锁定释放后才能继续(或者如果需要等待太久则抛出超时) )。

不过,在测试时,我注意到日志中打印了以下内容

get

因此,锁似乎在顺序访问in servlet in servlet attempting to lock attempting to lock got lock,status is = 200 doing heavy work done,new status is = 400 got lock,status is = **200** doing heavy work done,new status is = 400 方面起作用,但是我不明白的是为什么当我期望第二次调用数据库读取状态为doStuffWithFoo时它应该是400。我认为锁定的全部目的是确保不会发生这种情况。

我觉得JEE应该可以实现,但是我的配置中缺少一些东西。我的问题是:有什么我可以做的,以确保第二个请求读取更新的状态(400),最好不要:

到目前为止,我已经尝试过:

  • 200方法添加em.flush,但这没有效果
  • 将事务隔离级别设置为updateREAD_UNCOMMITTED,但是在两种情况下,这都会破坏数据源(无法再连接到数据库,我怀疑jdbc驱动程序不支持它)

注意:这不是我的代码,这是一个遗留项目,我被要求对以下事实做一些事情:两个快速连续的请求导致冲突。我发现有关代码的一些问题值得怀疑,但是进行重构或重写是不现实的选择。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...