JDBC何时进行自动提交提交

时间:2016-04-01 12:55:12

标签: java sql jdbc sql-server-2012

我正在努力减少死锁,并且向我指出我不应该对一个连接使用多个查询,因为事务可能未提交和打开,从而导致死锁。所以在伪代码中,像这样:

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何时提交事务?

重用这样的连接或两个语句是否都使用单独的连接是一种好习惯吗?

2 个答案:

答案 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时启动新事务,但由于您未关闭stmt2rs,连接关闭将触发提交。

关于是否应该重用连接和语句:它取决于上下文和代码。对于特定的工作单元,您使用单个连接。如果要进一步重用连接,则应使用连接池。只有在有意义的情况下才能重用语句(否则你的代码可能会因为资源泄漏而变得复杂),而且还有连接池提供内置的语句池,可以降低这种复杂性。

“[..]这样的语句不应该使用多个查询与一个连接,因为事务可能没有提交和打开,导致死锁。”通常是不正确的,如果导致应用程序运行不正常应用。它可能适用于不正确遵循上述自动提交规则的行为不当的驱动程序,或者可能在连接寿命较长且您没有正确完成语句的情况下(如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时,它会立即在数据库中提交。

重用连接是一种很好的做法。只要两个线程同时使用相同的连接,这是绝对安全的