我正在尝试在JDBC中实现一个计数器,该方法实际上增加了一个列值并返回了新的列值,下面是我的代码的片段我首先执行select ... for update来获取旧值(也得到结果)在行锁中)然后在该行上执行更新。我是否正确地假设没有其他事务可以干扰select .. for update和update之间的列val的值。
//get connection here
con.setAutoCommit(false);
PreparedStatement statement = con.prepareStatement("SELECT val FROM atable WHERE id=? FOR UPDATE");
statement.setInt(1, id);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
oldVal = resultSet.getInt("val");
statement = con.prepareStatement("UPDATE atable SET val=" + oldVal + "+1 WHERE id=?");
statement.setInt(1, id);
int result = statement.executeUpdate();
if (result != 1) {
throw new SQLException("error message");
} else {
newVal = oldVal + 1;//could do another select statement to get the new value
statement.close();
}
} else {
throw new SQLException("error message");
}
resultSet.close();
con.commit();
con.setAutoCommit(true);
//close connection here
感谢。
答案 0 :(得分:0)
我会做类似下面的代码。 (我拿出了可读性的检查和例外;随意添加它们。)
con.setAutoCommit(false);
PreparedStatement incrementVal =
con.prepareStatement("UPDATE atable SET val = val + 1 WHERE id=?");
incrementVal.setInt(1, id);
statement.executeUpdate();
PreparedStatement selectIncrementedVal =
con.prepareStatement("SELECT val FROM atable WHERE id=?");
selectIncrementedVal.setInt(1, id);
ResultSet resultSet = selectIncrementedVal.executeQuery();
if (resultSet.next()) {
newVal = resultSet.getInt("val");
}
resultSet.close();
con.commit();
con.setAutoCommit(true);
请参阅下面有关交易隔离的信息。
http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_serializable
答案 1 :(得分:0)
我是否有权假设没有其他交易可以干扰 select ... for update之间的列val值,然后更新
是的,你可以。否则可能导致数据库中的“不一致”。
答案 2 :(得分:0)
我打算添加一些关于数据库当前隔离级别的内容,但是在我的不确定性中,我搜索了一些其他有趣的文章: SQLServer Question和 Oracle-centric answer
最终,我不确定(对不起)。我可能会建议一个存储过程来提供您想要的显式原子锁,并在需要时让它将新值返回给您的代码。但是,存储过程语法可能是数据库供应商特定的。
(顺便说一句,你没有展示你如何获得连接参考。如果有多个线程同时使用相同的连接,他们将能够修改你背后的“自动提交”属性。您正在使用连接池,因此在任何给定时间只有一个线程具有此引用。)
另请注意,数据库隔离级别(如果证明是相关的)也是在连接级别设置的,因此如果您从池中获取连接,则前一个用户可能已将连接保留在不同于你所需要的状态。您谨慎恢复setAutoCommit()
属性;您可能需要对setTransactionIsolation()
属性执行相同操作。
答案 3 :(得分:0)
使用Oracle,您可以执行以下操作:
CallableStatement cs = con.prepareCall(
"UPDATE atable SET val = val + 1 WHERE id=? RETURNING val INTO ?");
cs.setInt(1, id);
cs.registerOutParameter(2, Types.NUMERIC);
cs.execute();
int newId = cs.getInt(2);