我想要一些关于jdbc的并发问题的建议,我基本上需要更新一个值,然后使用更新然后选择检索该值,我假设通过关闭自动提交关闭没有其他事务可以访问此表,因此,在提交之前,其他事务将无法执行更新和选择查询。
下面是一些示例代码。你认为这会有效吗?其他人是否有更好的解决方案来实现这个目标?
int newVal=-1;
con.setAutoCommit(false);
PreparedStatement statement = con.prepareStatement("UPDATE atable SET val=val+1 WHERE id=?");
statement.setInt(1, id);
int result = statement.executeUpdate();
if (result != 1) {
throw new SQLException("Nothing updated");
} else {
statement = con.prepareStatement("SELECT val FROM atable WHERE id=?");
statement.setInt(1, id);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
newVal = resultSet.getInt("val");
}
}
statement.close();
con.commit();
con.setAutoCommit(true);
感谢。
答案 0 :(得分:10)
假设您使用某种形式的数据源,您可以在那里配置是否需要事务性和隔离级别。但要明确:
try(Connection con = ds.getConnection()){
con.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
con.setAutoCommit(false);
//...
} catch(SQLException sqle) {
throw new MyModelException(e)
}
现在,您可以通过更新表中的版本(或时间戳)字段来触发pesimistic锁定。这将触发数据库中的锁定(很可能是在记录级别):
try(PreparedStatement pStm = con.prepareStatement("update atable set version=version+1")){
pStm.executeUpdate();
}
此时,如果另一个用户正在尝试同时更新同一记录,则此连接将等待或超时,因此您必须为两件事做好准备。在事务结束(提交或回滚)之前,记录不会被解锁。
然后,您可以安全地选择和更新您想要的任何内容,并确保在处理您的数据时没有其他人接触您的记录。如果有其他人尝试,他们将等待你完成(或者他们将根据连接配置超时)。
或者您可以使用乐观锁定。在这种情况下,您会读取您的记录,对其进行修改,但在更新中,您确保没有其他人更改过它,因为您通过检查版本/时间戳字段与您原来读取的字段相同来读取它。在这种情况下,如果您发现过时/过时数据,则必须准备重试事务(或全部中止)。
即。 update atable set afield=? where id=? and version=1
如果受影响的行数为0,则表示您的读取和更新之间的记录可能已更新且记录不再是版本1。
答案 1 :(得分:3)
在您的连接上设置autocommit=false
将不阻止其他连接/线程更改数据库中的行!它只会在该特定连接上的每个JDBC操作之后禁用自动提交。
您需要锁定行,例如。使用select ... for update
来阻止针对该行的其他事务,并且您还需要在单个事务中进行选择和更新。
干杯,