2013-07-12 17 views
17

mẫu:Có sử dụng lại tài nguyên phát hành Statement và Resultet từ lần sử dụng trước đó không? Hay tôi phải đóng chúng một cách rõ ràng trước khi sử dụng lại? đang

 aStmt = aConn.prepareStatement(aQuery); 
     aRset = aStmt.executeQuery(cQuery); 

     while (cRset.next()) { 
      //stuff to determine value of parm1 

      aStmt.setString(1, parm1);     
      aRset = aStmt.executeQuery(); 

      //more stuff 
     } 

Tôi có phải đóng aStmt và aRset sau mỗi vòng lặp bên trong câu lệnh while? Hoặc sẽ tái sử dụng chúng trong các vòng lặp thành công phát hành bộ nhớ/tài nguyên được sử dụng từ các vòng trước?

Trả lời

23

Hành vi của resultsets và (đã chuẩn bị) báo cáo được ghi rõ trong Java API. Tôi đề nghị bạn đọc tài liệu thực tế (và đặc tả JDBC) để có được các chi tiết.

Các Statement API nói:

Theo mặc định, chỉ có một đối tượng ResultSet mỗi Statement đối tượng có thể được mở cùng một lúc. Do đó, nếu việc đọc một đối tượng ResultSet được xen kẽ với việc đọc một đối tượng khác, mỗi đối tượng phải được tạo bởi các đối tượng khác nhau Statement. Tất cả các phương thức thực thi trong giao diện Statement hoàn toàn đóng đối tượng ResultSet hiện tại của một thống kê nếu một đối tượng mở đang tồn tại.

(nhấn mạnh mỏ).

Trong mã cụ thể của bạn, khi bạn gọi aStmt.executeQuery(), số ResultSet cũ được gán cho aRset sẽ được trình điều khiển đóng hoàn toàn. Điều đó nói rằng, nó sẽ là tốt hơn để đóng một cách rõ ràng cho mình (hoặc sử dụng Java 7 thử-với-nguồn lực), để ngăn chặn bạn quên để đóng các ResultSet trong vòng lặp cuối cùng thông qua vòng lặp.

Bây giờ đến PreparedStatement: Khi bạn chuẩn bị một câu lệnh (nói chung, việc triển khai có thể khác nhau), truy vấn được gửi đến máy chủ để biên dịch.Khi thực hiện các tham số cho việc thực thi cụ thể đó được gửi đến máy chủ. Gọi close() trên aStmt sẽ dẫn đến tuyên bố chuẩn bị được deallocated trên máy chủ, rõ ràng là NOT những gì bạn muốn ở đây vì bạn muốn sử dụng lại tuyên bố với các giá trị khác nhau cho tham số của nó.

Vì vậy, trong ngắn

  1. Đóng ResultSet không phải là về mặt kỹ thuật cần thiết ở đây (trừ trường hợp cuối cùng ResultSet tạo), nhưng nó là tốt hơn để làm điều đó một cách rõ ràng
  2. Bạn chỉ nên đóng PreparedStatement khi bạn đang thực hiện với nó.

Sử dụng try-with-resources là một cách để loại bỏ một phần của sự nhầm lẫn về những vấn đề, như mã của bạn sẽ tự động nhả nguồn khi nó được thực hiện với nó (ở phần cuối của phạm vi sử dụng):

try (
    ResultSet cRset = cStmt.executeQuery(cQuery); 
    PreparedStatement aStmt = aConn.prepareStatement(aQuery); 
) { 
    while (cRset.next()) { 
     //stuff to determine value of parm1 

     aStmt.setString(1, parm1);     
     try (ResultSet aRset = aStmt.executeQuery()) { 
      //more stuff 
     } 
    } 
} 

Ở cuối đoạn mã này tất cả các tài nguyên JDBC được đóng đúng (theo thứ tự đúng, ngay cả khi ngoại lệ xảy ra, vv)

+0

Thực ra, đối với ResultSet cuối cùng được tạo (tất cả các tài nguyên không được khai báo khác), tôi có một phương thức để đóng mọi thứ ở cuối chương trình. Xem xét điều đó, liệu tôi có nên rời khỏi logic của tôi không? – heisenbergman

+0

@heisenbergman Bạn nên đóng tài nguyên ngay khi bạn không còn cần đến chúng nữa. Việc để chúng mở có thể làm tăng mức sử dụng bộ nhớ trong cả ứng dụng và cơ sở dữ liệu của bạn. –

0

API được chuẩn bị trước: Một câu lệnh SQL được biên dịch sẵn và được lưu trữ trong đối tượng PreparedStatement. Đối tượng này sau đó có thể được sử dụng để thực thi hiệu quả câu lệnh này nhiều lần.

Nhưng bạn không thể sử dụng lại đối tượng ResultSet. Khi bạn gọi executeQuery trên một đối tượng PreparedStatement lần thứ hai một ResultSet mới được tạo ra, nếu bạn không đóng ResultSet trước đó, bạn có nguy cơ bị rò rỉ tài nguyên.

+2

Đặc tả JDBC yêu cầu trình điều khiển đóng 'ResultSet' cũ nếu một mã mới được tạo từ cùng một đối tượng 'Statement'. –

2

Không, bạn không được đóng ResultSetStatement bên trong vòng lặp while.

Bạn phải đóng chúng sau vòng lặp.

Ngoài ra nếu bạn muốn sử dụng lại PreparedStatement thì bạn có thể không đóng nó cho đến khi bạn sẵn sàng xử lý.

Quy tắc tốt nhất là đóng tài nguyên như vậy trong cùng một khối khi chúng được tạo. Trong trường hợp của bạn, điều tốt nhất cần làm là đóng tài nguyên trong khối finally sau khi bắt được SQLException.

Ví dụ:

try { 
    aStmt = aConn.prepareStatement(aQuery); 
    cRset = cStmt.executeQuery(cQuery); 

    while (cRset.next()) { 
     //stuff to determine value of parm1 

     aStmt.setString(1, parm1); 
     try { 
      aRset = aStmt.executeQuery(); 
     } finally { 
      aRset.close(); 
     } 

     //more stuff 
    } 
} catch (SQLException ex) { 
    // Do error handling 
} finally { 
    // Close Resultset 
} 

Trong Java 7 bạn có thể dùng thử với tài nguyên.

+0

Bạn đang quên về 'ResultSet' được tạo bên trong vòng lặp –

+0

@MarkRotteveel Tôi đã mở rộng câu trả lời của mình. Cảm ơn. –

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