java - 检查和更新表中的记录时潜在的并发问题

标签 java spring hibernate transactional optimistic-locking

情况是这样

成员(member)必须兑换 token 才能访问(解锁)给定的项目。相关数据库表为:

表1

Table MEMBER_BALANCE: MEMBER_ID, TOKEN_BALANCE

表2

Table UNLOCKED_ITEM: MEMBER_ID, DATE_UNLOCKED, ITEM_ID

我需要执行的检查或约束是

  1. 当用户尝试解锁项目时,TOKEN_BALANCE 必须 > 0,并且
  2. 用户之前没有解锁过相同的项目。

我的直觉是在 MemberService.java 中编写一个简单的方法:

@Transactional
public void unlockItem(Member member, Item item){
    memberBalanceDAO.decrementBalance(member);
    itemDAO.unlockItem(member, item);
}

我通过在 UNLOCKED_ITEM 表上的 MEMBER_ID/ITEM_ID 对添加 unique 约束来解决第二个要求。

我认为,我唯一需要注意的是,用户尝试同时解锁许多项目,但未满足 TOKEN_BALANCE 要求。例如,TOKEN_BALANCE 为 1,但用户实际上可以同时单击解锁两个项目。

下面是我的MemberBalanceDAO.decrementBalance方法:

@Transactional
public void decrementBalance(Member member) {
    MemberBalance memberBalance = this.findMemberBalance(member);
    if (memberBalance.getTokens() >= 1) {
        memberBalance.setTokens(memberBalance.getTokens() - 1);
        this.save(memberBalance);
    } else {
        throw new SomeCustomRTException("No balance");
    }
}

我认为这并不能保护我免受 TOKEN_BALANCE = 1 用例的影响。我担心同时有多个解锁请求。如果余额为 1,我可以同时对 decrementBalance() 进行两次调用,将余额提交为 0,但同时也会对 itemDAO.unlockItem(...) 进行两次成功调用,对吗?

我应该如何实现这个?我应该将服务级别方法的事务设置为isolation = Isolation.SERIALIZABLE吗?或者有没有更干净/更好的方法来解决这个问题?

最佳答案

我宁愿建议您在 member_balance 表中引入 version 列。请参阅文档 Optimistic Locking .

正如您提到的,您无法修改架构;您可以使用无版本乐观锁,explained here .

或者您可能想要采用悲观锁定,explained here 。然后,您可以修改您的方法 decrementBalance(),以获取其中的成员(member)余额,不要使用 findMemberBalance()。例如,

@Transactional
public void decrementBalance(Member member) {
    MemberBalance memberBalance = entityManager.find(
        MemberBalance.class, member.id, LockModeType.PESSIMISTIC_WRITE,             
        Collections.singletonMap( "javax.persistence.lock.timeout", 200 ) //If not supported, the Hibernate dialect ignores this query hint.
    );
    if (memberBalance.getTokens() >= 1) {
        memberBalance.setTokens(memberBalance.getTokens() - 1);
        this.save(memberBalance);
    } else {
        throw new SomeCustomRTException("No balance");
    }
}

注意:它可能无法按原样工作;只是为了给您提供一些提示。

关于java - 检查和更新表中的记录时潜在的并发问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55584106/

相关文章:

java - 如何在 hibernate 中编写查询

java - java中调用kotlin补全

java - 如何下载并构建 Eclipse IDE for Java 代码?

java - 如何设置要在 Bluemix 上编译的代码文件(样本除外)?

java - 使用 Spring MVC 配置语言环境切换

java - 设置恶意 xml 的 Apache CXF 总线属性

java - Grails 中的 HeuristicCompletionException 是否可以简单地被忽略?

java - Spring 国际化可以通过 xml 配置进行,但不能通过 Java 配置进行

hibernate - JBoss 7 + hibernate + Log4j

hibernate - 如何使用 mysemma 记录 querydsl 在后台生成的 sql 查询