我正在努力减少死锁,并且向我指出我不应该对一个连接使用多个查询,因为事务可能未提交和打开,从而导致死锁。所以在伪代码中,像这样:
try(Connection con = datasource.getConnection())
{
PreparedStatement stm1 = con.prepareStatement(getSQL());
stm1.setString(1, owner);
stm1.setTimestamp(2, Timestamp.valueOf(LocalDateTime.now()));
stm1.setInt(3, count);
int updateCount = stm1.executeUpdate();
stm1.close();
PreparedStatement stm2 = con.prepareStatement(getSQL2());
stm2.setString(1, owner);
ResultSet rs = stm2.executeQuery();
List<Object> results = new ArrayList<>();
while(rs.next()) {
results.add(create(rs));
}
return results;
} catch (SQLException e) {
throw new RuntimeException("Failed to claim message",e);
}
当auto-commit设置为true时,stm1何时提交事务?
重用这样的连接或两个语句是否都使用单独的连接是一种好习惯吗?
答案 0 :(得分:5)
通常可以通过阅读JDBC specification来回答这些问题。 JDBC 4.2第10.1节事务边界和自动提交表示:
何时开始新交易是由隐含的决定 JDBC驱动程序或底层数据源。一些 数据源实现显式的“开始事务”语句, 没有JDBC API可以这样做。通常,启动新事务 当前的SQL语句需要一个而没有 交易已经到位。是否给定的SQL语句 要求事务也由SQL:2003指定。
Connection
属性自动提交指定何时结束 交易。启用自动提交会导致事务提交 该语句完成后,每个单独的SQL语句。 声明被认为是“完整”的点取决于 关于SQL语句的类型以及应用程序的作用 执行后:
- 对于数据操作语言(DML)语句(如插入,更新,删除和DDL语句),语句即将完成 因为它已经完成了执行。
- 对于
Select
语句,当关联的结果集关闭时,语句完成。- 对于
CallableStatement
个对象或返回多个结果的语句,该语句在完成所有关联后完成 结果集已关闭,所有更新计数和输出 参数已被检索。
在您的代码中,事务作为stm1.executeUpdate()
的一部分提交(此事务可能已在准备或执行时启动)。在准备或执行stmt2
时启动新事务,但由于您未关闭stmt2
或rs
,连接关闭将触发提交。
关于是否应该重用连接和语句:它取决于上下文和代码。对于特定的工作单元,您使用单个连接。如果要进一步重用连接,则应使用连接池。只有在有意义的情况下才能重用语句(否则你的代码可能会因为资源泄漏而变得复杂),而且还有连接池提供内置的语句池,可以降低这种复杂性。
像“[..]这样的语句不应该使用多个查询与一个连接,因为事务可能没有提交和打开,导致死锁。”通常是不正确的,如果导致应用程序运行不正常应用。它可能适用于不正确遵循上述自动提交规则的行为不当的驱动程序,或者可能在连接寿命较长且您没有正确完成语句的情况下(如stmt2
的情况) )。也就是说,通常最好在完成后禁用自动提交并显式提交或回滚。
通过对语句和结果集使用 try-with-resources 可以改进代码,因为这样可以确保结果集和语句尽快关闭,即使发生异常时也是如此:
try (Connection con = datasource.getConnection()) {
try (PreparedStatement stm1 = con.prepareStatement(getSQL())) {
stm1.setString(1, owner);
stm1.setTimestamp(2, Timestamp.valueOf(LocalDateTime.now()));
stm1.setInt(3, count);
int updateCount = stm1.executeUpdate();
}
try (PreparedStatement stm2 = con.prepareStatement(getSQL2())) {
stm2.setString(1, owner);
try (ResultSet rs = stm2.executeQuery()) {
List<Object> results = new ArrayList<>();
while(rs.next()) {
results.add(create(rs));
}
return results;
}
}
} catch (SQLException e) {
throw new RuntimeException("Failed to claim message", e);
}
答案 1 :(得分:1)
禁用自动提交模式时,在您明确调用方法提交之前,不会提交任何SQL语句。在上一次调用方法提交之后执行的所有语句都包含在当前事务中,并作为一个单元一起提交。
当你设置auto-commit为true时,它会立即在数据库中提交。
重用连接是一种很好的做法。只要两个线程同时使用相同的连接,这是绝对安全的