1

我想从数据库表中增加并返回一个计数器。

java代码如下:

String sqlUpdate = "UPDATE mytable SET col3 = col3 + 1 WHERE colpk1 = ? AND colpk2 = ?";
Query queryUpdate = manager.createNativeQuery(sqlUpdate);
queryUpdate.setParameter(1, ...);
queryUpdate.setParameter(2, ...);

int num = queryUpdate.executeUpdate();

if (num == 0) {
    long count = 1;
    String sqlInsert = "INSERT INTO mytable (colpk1, colpk2, col3) VALUES (?,?,?)";
    Query queryInsert = manager.createNativeQuery(sqlInsert);
    queryInsert.setParameter(1, ...);
    queryInsert.setParameter(2, ...);
    queryInsert.setParameter(3, count);
    queryInsert.executeUpdate();

    return count;
} else {
    String sqlSelect = "SELECT col3 FROM mytable WHERE colpk1 = ? AND colpk2 = ?";
    Query querySelect = manager.createNativeQuery(sqlSelect);
    querySelect.setParameter(1, ...);
    querySelect.setParameter(2, ...);

    Object result = querySelect.getSingleResult();

    return Long.parseLong(result.toString());
}

如果已经存在具有给定主键的行,这也可以同时使用(创建锁)。但是,如果该行尚不存在(num == 0),则 UPDATE 不会锁定,并且两个查询之间可能会发生并发访问,然后在将 INSERT 作为新行执行时导致唯一约束验证在此期间已经创建。

解决这个问题的最佳方法是什么?SELECT FOR UPDATE首先使用一个然后根据结果执行更新或插入会更好吗?

4

2 回答 2

1

MERGE语句将避免拆分语句。

http://en.wikipedia.org/wiki/Merge_(SQL )

或者,您可以始终在条件发生时为极少数情况捕获唯一约束异常,然后重试。

于 2012-08-21T09:11:21.640 回答
0

由于 Merge 可以在并发执行时抛出 Unique Constraint 异常,因此最好的解决方案是在执行插入时捕获异常,然后该行必须已经存在,然后继续更新。

在容器管理事务的情况下提交此事务是下一个问题,因为异常会导致isRollBackOnly == true. 有效的方法是使用新的 bean 调用来尝试在新事务中插入,请参阅异常后提交事务 - 撤消 setRollbackOnly

于 2012-08-30T13:59:42.967 回答