2016-04-01 8 views
0

デッドロックの削減に取り組んでおり、デッドロックの原因となるトランザクションがコミットされて開かれない可能性があるため、1つの接続で複数のクエリを使用しないでください。だから、擬似コード、このようなもので:自動コミットがtrueに設定されている場合自動コミット時のJDBC

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); 
    } 

STM1は、トランザクションをコミットしない場合は?

このような接続を再利用するか、どちらのステートメントでも別の接続を使用することをお勧めしますか?

+0

'stm1.executeUpdate();'が呼び出されると、最初のステートメントはすぐにコミットされます。 – Kayaman

+0

私は接続を開くにはオーバーヘッドがあるので、接続を再利用しようとします。また、sql1とsql2を連結している場合は、1つの呼び出しを行うこともできます。 –

+0

Sql 1は更新プログラムですが、2はその更新プログラムによって選択されます。 Sql1は別の更新プログラムでデッドロックを発生させます。そのため、sql2が長すぎるトランザクションを開いてそのデッドロックを引き起こしているのではないかと思いました。どちらの場合でも、1つのクエリで表現することはできません – pandaadb

答えて

4

質問は通常JDBC specificationを読んで回答することができ、同時に2つのスレッドで使用していない限り、完全に安全です。 JDBC 4.2のセクション10.1 トランザクション境界と自動コミット氏は述べています:新しいトランザクションを開始する

が JDBCドライバまたは基礎となるデータソースのいずれかによって暗黙的に行われた決定です。一部の データソースで明示的な "begin transaction"文が実装されていますが、 JDBC APIはありません。通常、新しいトランザクションは、現行のSQL文で必要とされ、すでに トランザクションが存在しない場合に起動されます。 指定されたSQL文 がトランザクションを必要とするかどうかは、SQL:2003でも指定されます。 取引を終了したときに

を自動コミットConnection属性を指定します。自動コミットを有効にすると、その文が完了するとすぐに、個々のSQL文 の後にトランザクションがコミットされます。声明は「完全」であると考えられるで ポイントは、SQL文の種類に を依存だけでなく、アプリケーションがそれを実行した後 を何:

  • のようなデータ操作言語(DML)文の場合挿入、更新、削除、およびDDL文を実行すると、実行が完了するとすぐに文は完了します。
  • Selectステートメントの場合、関連する結果セットが閉じられると、ステートメントは完了します。
  • CallableStatementオブジェクトまたは複数の結果を返すステートメントの場合、関連するすべての結果セットが閉じられ、すべての更新カウントと出力 パラメータが取得されたときに、ステートメントは完了します。あなたのコードで

トランザクションが(このトランザクションは準備、または上の実行に開始されている場合があります)stm1.executeUpdate()の一環として取り組んでいます。stmt2の準備または実行時に新しいトランザクションが開始されますが、stmt2またはrsをクローズしないと、接続を閉じるとコミットがトリガーされます。

接続とステートメントを再利用するかどうかは、コンテキストとコードによって異なります。特定の作業単位では、単一の接続を使用します。接続をさらに再利用するには、接続プールを使用する必要があります。文を再利用することは意味がある場合にのみ行うべきです(さもなければ、コードがリソースリークを起こして複雑になる可能性があります)。また、この複雑さを軽減する組み込み文プールを提供する接続プールもあります。

ステートメントのように"[..]トランザクションがコミットされていない可能性があり、デッドロックの原因となるため、1つの接続で複数のクエリを使用すべきではありません。は通常間違っており、適用されたアプリケーションがひどく実行される可能性があります。上記の自動コミットルールに正しく従わない、または接続がずっと長く存続していて、ステートメントを正しく終了していない(例えば、stmt2の場合のように)、正しく動作しないドライバに適用される可能性があります。つまり、自動コミットを無効にし、完了したら明示的にコミットまたはロールバックするほうがよいでしょう。

これは例外が発生した場合でも、結果セットと文は、できるだけ早く閉じている保証してあなたのコードは、同様のステートメントおよび結果セットののtry-と資源を使用することによって改善することができます。

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

自動コミットモードが無効の場合、明示的にメソッドを呼び出すまで、SQL文はコミットされません。以前にコミットメソッドを呼び出した後に実行されたすべてのステートメントは、現在のトランザクションに含まれ、1つの単位としてまとめられます。

auto-commitをtrueに設定すると、データベースに即座にコミットされます。

connection.Thisを再利用することをお勧めは、同じ接続であるこれらのような

関連する問題