微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

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

我有以下代码


@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 举报,一经查实,本站将立刻删除。