我想从数据库表中递增并返回一个计数器。
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作为新行时导致Unique Constraint验证在此期间已经创建了。
解决此问题的最佳方法是什么?首先使用SELECT FOR UPDATE
然后根据执行UPDATE或INSERT的结果会更好吗?
答案 0 :(得分:1)
答案 1 :(得分:0)
由于Merge可以在并发执行中抛出Unique Constraint异常,因此最好的解决方案是在执行插入时捕获异常,然后行必须在那里,然后继续更新。
在容器管理事务的情况下提交此事务是下一个问题,因为例外导致isRollBackOnly == true
。可行的方法是使用新的bean调用在新事务中尝试插入,请参阅Commit transaction after exception - undo setRollbackOnly