2008-09-19 42 views
49

Tôi đã làm xét mã (chủ yếu là sử dụng các công cụ như FindBugs) của một trong những dự án thú cưng của chúng tôi và FindBugs đánh dấu mã sau đây là sai lầm (giả):ResultSet không đóng khi kết nối đóng?

Connection conn = dataSource.getConnection(); 

try{ 
    PreparedStatement stmt = conn.prepareStatement(); 
    //initialize the statement 
    stmt.execute(); 
    ResultSet rs = stmt.getResultSet(); 
    //get data 
}finally{ 
    conn.close(); 
} 

Các lỗi được rằng mã này có thể không giải phóng các nguồn lực . Tôi đã tìm ra rằng ResultSet và Tuyên Bố đã không đóng cửa, vì vậy tôi đóng chúng vào cuối cùng:

finally{ 
    try{ 
     rs.close() 
    }catch(SqlException se){ 
     //log it 
    } 
    try{ 
     stmt.close(); 
    }catch(SqlException se){ 
     //log it 
    } 
    conn.close(); 
} 

Nhưng tôi gặp các mô hình trên trong nhiều dự án (từ khá một vài công ty), và không có ai ở resultsets đóng hoặc Các câu lệnh.

Bạn có gặp sự cố với ResultSets và câu lệnh không bị đóng khi đóng kết nối không?

Tôi chỉ tìm thấy this và nó đề cập đến Oracle có vấn đề với việc đóng kết quả khi đóng kết nối (chúng tôi sử dụng Oracle db, do đó sửa chữa của tôi). java.sql.api không nói gì trong Connection.close() javadoc.

+0

tôi khuyên bạn nên sử dụng Apache commons-dbutils (http://commons.apache.org/dbutils/) Đó là một thư viện JDBC nhẹ mà thực sự dọn dẹp rất nhiều mã boilerplate JDBC. –

+2

Đây là loại lỗi mà người dùng nhận được khi họ không đóng các đối tượng liên quan - "ORA-01000: con trỏ mở tối đa vượt quá" - http: // stackoverflow.com/questions/12192592/java-sql-sqlexception-ora-01000-maximum-open-cursors-vượt quá –

+0

con trỏ cơ sở dữ liệu - http://stackoverflow.com/questions/3861558/what-are-the-benefits-of-using -database-cursor Con trỏ là công cụ cho phép bạn lặp lại các bản ghi trong một bộ. Nó có các khái niệm về thứ tự và bản ghi hiện tại. –

Trả lời

51

Một vấn đề với CHỈ đóng kết nối chứ không phải tập hợp kết quả, là nếu mã quản lý kết nối của bạn đang sử dụng kết nối tổng hợp, connection.close() sẽ chỉ đưa kết nối trở lại trong nhóm. Ngoài ra, một số cơ sở dữ liệu có một nguồn tài nguyên con trỏ trên máy chủ sẽ không được giải phóng đúng cách trừ khi nó được đóng một cách rõ ràng.

+14

Các javadocs cho phương thức đóng Connection # nói rằng "giải phóng cơ sở dữ liệu của đối tượng Connection và tài nguyên JDBC ngay lập tức". Tôi nghĩ rằng vấn đề là một số triển khai xấu không làm đúng công việc. Khi các hồ không đóng tài nguyên liên quan, chúng có làm sai điều gì không? – marcospereira

+2

Hầu hết các trình điều khiển JDBC của nhà cung cấp chính tương ứng với thông số kỹ thuật. Tuy nhiên, hầu hết (nếu không phải tất cả) máy chủ ứng dụng duy trì một nhóm các kết nối. Chúng bao bọc kết nối gốc và thực hiện lại các phương thức như close() để các kết nối có thể được 'gộp lại'. Điều này có nghĩa rằng nếu bạn làm việc trong các môi trường này, bạn phải đóng các tài nguyên như bản thân câu lệnh và ResultSets. –

+4

@ Ryan Fernandes: Vâng, một số hồ bơi chỉ cung cấp cho bạn một đối tượng connectionProxy lưu tất cả các câu lệnh mở trên đó. Khi nó được trả về trong hồ bơi, nó đóng tất cả các câu lệnh mở. –

27

Tôi đã gặp sự cố với ResultSets không được tiết lộ trong Oracle, mặc dù kết nối đã bị đóng. Lỗi tôi nhận được là

"ORA-01000: maximum open cursors exceeded" 

Vì vậy: Luôn đóng kết quả!

+1

+1 - để đề cập đến hậu quả của việc không đóng tài nguyên. –

8

Oracle sẽ cung cấp cho bạn các lỗi về con trỏ mở trong trường hợp này.

Theo: http://java.sun.com/javase/6/docs/api/java/sql/Statement.html

nó trông giống như tái sử dụng một tuyên bố sẽ đóng bất kỳ resultsets mở, và đóng một tuyên bố sẽ đóng bất kỳ resultsets, nhưng tôi không thấy bất cứ điều gì về đóng một kết nối sẽ đóng bất kỳ tài nguyên nó tạo ra.

Tất cả các chi tiết đó đều được để lại cho nhà cung cấp trình điều khiển JDBC.

Luôn an toàn nhất để đóng mọi thứ một cách rõ ràng. Chúng tôi đã viết một lớp util kết thúc tốt đẹp mọi thứ với try {xxx} catch (Throwable {} để bạn có thể gọi Utils.close (rs) và Utils.close (stmt), vv mà không phải lo lắng về các trường hợp ngoại lệ quét gần như được cho là ném

+0

_ nhưng tôi không thấy bất kỳ điều gì về việc đóng kết nối sẽ đóng bất kỳ tài nguyên nào được tạo._ http://docs.oracle.com/javase/6/docs/api/java/sql/Connection.html#close() _releases cơ sở dữ liệu của đối tượng kết nối này ** và tài nguyên JDBC ** ngay lập tức_ Nhưng yeah, luôn luôn tốt hơn để đóng tất cả mọi thứ. – user454322

4

Tôi chắc chắn đã gặp sự cố với ResultSets không rõ ràng và có thể làm tổn thương gì khi đóng chúng mọi lúc, đúng không? Nó có thể không khả thi trong môi trường phát triển của bạn, nhưng tôi đã có may mắn khi sử dụng Spring để quản lý các giao dịch JPA.Thông tin chi tiết về việc mở các kết nối, câu lệnh, tập hợp kết quả và viết quá phức tạp try/catch/cuối cùng là khối (với khối try/catch trong khối cuối cùng!) để đóng chúng lại chỉ biến mất, để lại cho bạn để actuall y hoàn thành công việc. Tôi khuyên bạn nên chuyển sang loại giải pháp đó.

+1

Điểm của nó trong ứng dụng này. Chúng tôi sử dụng EJB và phần này là loại plugin - ứng dụng khách của chúng tôi sẽ triển khai nguồn dữ liệu trên serwer và sau đó có thể truy vấn nó thông qua plugin này. –

4

Trong Java, báo cáo (không phải kết quả) tương quan với con trỏ trong Oracle. Tốt nhất là đóng các tài nguyên mà bạn mở vì hành vi không mong muốn có thể xảy ra liên quan đến tài nguyên hệ thống và JVM. Ngoài ra, một số khung công tác tổng hợp JDBC và các kết nối, do đó, không đóng chúng có thể không đánh dấu các đối tượng đó là miễn phí trong hồ bơi và gây ra các vấn đề về hiệu suất trong khung công tác.

Nói chung, nếu có một phương thức close() hoặc destroy() trên một đối tượng, có lý do để gọi nó, và bỏ qua nó được thực hiện như vậy nguy hiểm của riêng bạn.

8

Cầu ODBC có thể tạo rò rỉ bộ nhớ với một số trình điều khiển ODBC.

Nếu bạn sử dụng trình điều khiển JDBC tốt thì bạn sẽ không gặp bất kỳ sự cố nào khi đóng kết nối. Nhưng có 2 vấn đề:

  • Bạn có biết mình có tài xế tốt không?
  • Bạn có sử dụng các trình điều khiển JDBC khác trong tương lai không?

Cách tốt nhất là đóng tất cả.

19

Bạn phải luôn đóng tất cả các tài nguyên JDBC một cách rõ ràng. Như Aaron và John đã nói, việc đóng một kết nối thường sẽ chỉ trả về một hồ bơi và không phải tất cả các trình điều khiển JDBC đều được thực hiện chính xác theo cùng một cách.

Đây là một phương pháp hữu ích có thể được sử dụng từ một khối finally:

public static void closeEverything(ResultSet rs, Statement stmt, 
     Connection con) { 
    if (rs != null) { 
     try { 
      rs.close(); 
     } catch (SQLException e) { 
     } 
    } 
    if (stmt != null) { 
     try { 
      stmt.close(); 
     } catch (SQLException e) { 
     } 
    } 
    if (con != null) { 
     try { 
      con.close(); 
     } catch (SQLException e) { 
     } 
    } 
} 
+6

Man, có lẽ chúng ta cần một giao diện Closable, huh? – marcospereira

+2

ResultSet được đóng tự động khi bạn đóng Báo cáo. (Xem JavaDoc http://download.oracle.com/javase/1.4.2/docs/api/java/sql/Statement.html) –

+0

Cảm ơn bạn đã đăng bài. Tôi thích ý tưởng này. – Abboq

8

Tôi làm việc trong một môi trường J2EE web lớn. Chúng tôi có một số cơ sở dữ liệu có thể được kết nối trong một yêu cầu duy nhất. Chúng tôi bắt đầu nhận được những sai lầm hợp lý trong một số ứng dụng của chúng tôi. Vấn đề là như sau:

  1. tài sẽ yêu cầu trang
  2. máy chủ kết nối vào DB 1
  3. server Selects trên DB 1
  4. Server "đóng cửa" kết nối đến DB 1
  5. máy chủ kết nối với DB 2
  6. Đã khóa!

Điều này xảy ra vì 2 lý do, chúng tôi gặp lưu lượng truy cập cao hơn nhiều so với bình thường và thông số J2EE theo mặc định không thực sự đóng kết nối của bạn cho đến khi kết thúc quá trình thực thi. Vì vậy, trong ví dụ trên bước 4 không bao giờ thực sự đóng kết nối ngay cả khi chúng đã được đóng đúng cách cuối cùng.

Để khắc phục điều này, bạn phải sử dụng tài liệu tham khảo tài nguyên trong tệp web.xml cho Kết nối cơ sở dữ liệu của mình và bạn phải đặt phạm vi chia sẻ lại thành không thể chia sẻ.

Ví dụ:

<resource-ref> 
    <description>My Database</description> 
    <res-ref-name>jdbc/jndi/pathtodatasource</res-ref-name> 
    <res-type>javax.sql.DataSource</res-type> 
    <res-auth>Container</res-auth> 
    <res-sharing-scope>Unshareable</res-sharing-scope> 
</resource-ref> 
Các vấn đề liên quan