2013-04-02 27 views
27

Tôi gặp sự cố với các tài nguyên thử và tôi chỉ yêu cầu chắc chắn. Tôi có thể sử dụng nó, nếu tôi cần phản ứng ngoại lệ, và tôi vẫn cần tài nguyên trong khối catch? Ví dụ đưa ra là thế này:Khôi phục giao dịch trên SQLException bằng cách sử dụng khối try-with-resources mới

try (java.sql.Connection con = createConnection()) 
{ 
    con.setAutoCommit(false); 
    Statement stm = con.createStatement(); 
    stm.execute(someQuery); // causes SQLException 
} 
catch(SQLException ex) 
{ 
    con.rollback(); 
    // do other stuff 
} 

Tôi sợ rằng tôi vẫn cam chịu để sử dụng cũ try-catch-cuối cùng trong trường hợp này, thậm chí theo tài liệu oracle - "catch khối và cuối cùng trong một thử-với-nguồn lực tuyên bố, bất kỳ bắt hoặc cuối cùng khối được chạy sau khi các nguồn lực khai báo đã được đóng lại. "

+2

Trong trường hợp này nếu bản thân kết nối không thành công, không có điểm nào được đưa trở lại. Phạm vi 'con' được giới hạn chỉ để thử chặn. – learningloop

+0

Câu hỏi này cũng có thể hữu ích. http://stackoverflow.com/questions/9260159/java-7-automatic-resource-management-jdbc –

+0

Trong số tất cả các tùy chọn thú vị nhất định, tôi vẫn thích giải pháp tốt nhất "try-catch-finally" – Adam

Trả lời

33

Theo thông số ngôn ngữ, kết nối sẽ bị đóng trước khi điều khoản bắt được thực hiện (http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.20.3.2).

Một giải pháp khả thi là để tổ try-với-nguồn báo cáo:

try (java.sql.Connection con = createConnection()) 
{ 
    con.setAutoCommit(false); 
    try (Statement stm = con.createStatement()) 
    { 
     stm.execute(someQuery); // causes SQLException 
    } 
    catch(SQLException ex) 
    { 
     con.rollback(); 
     con.setAutoCommit(true); 
     throw ex; 
    } 
    con.commit(); 
    con.setAutoCommit(true); 
} 

Hy vọng rằng minh họa điểm. Điều này cần được cải thiện khá nhiều nếu bạn có kế hoạch sử dụng nó trong mã sản xuất.

Ví dụ: nếu bạn đang sử dụng hồ bơi kết nối, thì bạn phải trả lại kết nối khi bạn nhận được kết nối, do đó, con.setAutoCommit (true); nên được thực hiện trong một điều khoản cuối cùng. Điều này có nghĩa là các tài nguyên cố gắng bên ngoài phải là một cố gắng truyền thống cuối cùng.

+0

ban đầu, thx –

+1

lưu ý rằng việc gọi ['setAutoCommit'] (http://docs.oracle.com/javase/8/docs/api/java/sql/Connection.html#setAutoCommit-boolean-) gọi COMMIT trên bất kỳ giao dịch đang chờ xử lý nào. Vì vậy, hãy cẩn thận trong mã phức tạp hơn. –

+1

"Điều này có nghĩa là các tài nguyên cố gắng bên ngoài phải là một cố gắng truyền thống cuối cùng". - vì vậy trên thực tế mẫu "thử với tài nguyên" hoàn toàn vô dụng đối với các kết nối JDBC với các kết nối các nhóm ...? – Itai

3

Trong ví dụ trên tôi nghĩ tốt hơn nên đặt con.commit() bên trong lồng nhau try-catch vì nó cũng có thể ném SQLException.

try (java.sql.Connection con = createConnection()) 
    { 
     con.setAutoCommit(false); 
     try (Statement stm = con.createStatement()) 
     { 
      stm.execute(someQuery); // causes SQLException 
      con.commit();   // also causes SQLException! 
     } 
     catch(SQLException ex) 
     { 
      con.rollback(); 
      throw ex; 
     }finally{ 
      con.setAutoCommit(true); 
     } 
    } 

Chúng tôi gặp sự cố như vậy trong môi trường sản xuất của chúng tôi với các phiên không được tiết lộ.

+2

Bạn nên sử dụng câu lệnh cuối cùng để đặt tự động Điều này sẽ ngăn cản bạn có tuyên bố này hai lần – AxelH

6
//try with resources 
    try(Connection conn = this.connectionProvider.getConnection()){//auto close BEFORE reach this , catch block, so we need a inner try block for statement 
     boolean oldAutoCommit=conn.getAutoCommit(); 
     conn.setAutoCommit(false);//auto commit to false 
     try(
      Statement stm = con.createStatement() 
     ){ 
      stm.execute(someQuery); // causes SQLException 
      conn.commit();//commit 
     } 
     catch (SQLException ex){ 
      conn.rollback();//error, rollback 
      throw ex;//If you need to throw the exception to the caller 
     } 
     finally { 
      conn.setAutoCommit(oldAutoCommit);//reset auto commit 
     } 
    } 
10

Trong mã của bạn, bạn đang bắt gặp "SQLException" để thực hiện đặt lại autoCommit. Bất kỳ loại ngoại lệ thời gian chạy nào (như một ngoại lệ con trỏ null) sẽ bong bóng từ mã của bạn mà không cần đặt lại tự động cam kết.

Cú pháp thử-với-tài nguyên làm cho trình biên dịch tạo ra một số mã tuyệt vời để trang trải tất cả các đường dẫn thực hiện và theo kịp với tất cả các ngoại lệ bị chặn thông qua các đóng. Với một vài lớp helper bạn có thể chèn cam/rollback và reset-autocommit vào quá trình sinh mã:

import java.sql.SQLException; 
import java.sql.Connection; 

public class AutoRollback implements AutoCloseable { 

    private Connection conn; 
    private boolean committed; 

    public AutoRollback(Connection conn) throws SQLException { 
     this.conn = conn;   
    } 

    public void commit() throws SQLException { 
     conn.commit(); 
     committed = true; 
    } 

    @Override 
    public void close() throws SQLException { 
     if(!committed) { 
      conn.rollback(); 
     } 
    } 

} 

public class AutoSetAutoCommit implements AutoCloseable { 

    private Connection conn; 
    private boolean originalAutoCommit; 

    public AutoSetAutoCommit(Connection conn, boolean autoCommit) throws SQLException { 
     this.conn = conn; 
     originalAutoCommit = conn.getAutoCommit(); 
     conn.setAutoCommit(autoCommit); 
    } 

    @Override 
    public void close() throws SQLException { 
     conn.setAutoCommit(originalAutoCommit); 
    } 

} 

Bây giờ bạn có thể kiểm soát rollback và Autocommit với "thử với tài nguyên" cú pháp như thế này:

try(Connection conn = getConnection(), 
     AutoSetAutoCommit a = new AutoSetAutoCommit(conn,false), 
     AutoRollback tm = new AutoRollback(conn)) 
    { 

     // Do stuff 

     tm.commit(); 
    } 
+1

Mã thú vị, tôi không dành thời gian để thử giao diện AutoCloseable.Đây có vẻ là một sự chấp thuận tốt để thay thế điều khoản cuối cùng. đơn giản hơn chỉ với một lần thử nghiệm với tài nguyên. – AxelH

+0

1. Tại sao bạn không khai báo "ném SQ LException "trong các phương thức close()? Tại sao bạn quấn như một ngoại lệ? 2. Tại sao bạn không tạo tất cả các tài nguyên trong một khối thử? AFAIK họ sẽ bị đóng lại theo thứ tự ngược lại, bạn đã tuyên bố họ. – Sabine

+0

Điểm rất tốt! Tôi sẽ thay đổi các đóng để ném các ngoại lệ SQL và kết hợp chúng trong một lần thử như bạn và AxelH đã đề xuất. Cảm ơn vì bạn đã phản hồi. – ChrisCantrell

Các vấn đề liên quan