2009-12-15 36 views
17

Dường như ResultSet sẽ tự động đóng khi tôi đóng Connection. Nhưng tôi muốn trả lại số ResultSet và sử dụng nó theo phương thức khác, thì tôi không biết phải đóng ConnectionPreparedStatement ở đâu.Nơi đóng kết nối JDBC trong khi tôi muốn trả về ResultSet

public ResultSet executeQuery(String sql, String[] getValue) 
{ 
    Connection conn = null; 
    PreparedStatement pstmt = null; 
    ResultSet rs = null; 
    try 
    { 
     conn = getConn(); 
     pstmt = conn.prepareStatement(sql); 
     if (getValue != null) 
     { 
      for (int i = 0; i < getValue.length; i++) 
      { 
       pstmt.setString(i + 1, getValue[i]); 
      } 
     } 
     rs = pstmt.executeQuery(); 
    } catch (Exception e) 
    { 
     e.printStackTrace(); 
     closeAll(conn, pstmt, rs); 
    } 
    return rs; 
} 

tôi đã chuyển closeAll(conn, pstmt, null); vào khối catch vì tôi thấy rằng nếu tôi đặt nó trong khối finally Tôi sẽ mất rs tôi ngay lập tức ngay trước khi nó trở lại. Bây giờ khi tôi muốn đóng số rs, tôi không thể đóng connpstmt. Có giải pháp nào không?

+0

được bạn cố gắng để dòng ResultSet, ví dụ: tránh đơn giản là đọc tất cả các kết quả vào một số loại bộ sưu tập và trở về đó? –

+6

Tắt chủ đề, tại sao mọi người lại sử dụng kiểu cú đúp đó? !!! Đây là Java không phải là C# – OscarRyz

+0

Cảm ơn tất cả các bạn đã rất nhiệt tình! Gần như là buổi sáng ở Trung Quốc nhưng tôi cảm động vì bạn và những câu trả lời tuyệt vời của bạn về giấc ngủ. Đây là câu hỏi đầu tiên của tôi tại Stackoverflow.com. Tôi sẽ là người thường xuyên ở đây! – Aloong

Trả lời

30

Sử dụng CachedRowSet để giữ thông tin sau khi ngắt kết nối

Connection con = ... 
ResultSet rs = ... 

CachedRowSet rowset = new CachedRowSetImpl(); 
rowset.populate(rs); 

con.close() 
+0

Sau khi đọc API của CachedRowSet, tôi thấy điều này có vẻ là cách dễ nhất.Tôi nghĩ rằng tôi sẽ lấy câu trả lời này. – Aloong

3

Cách bạn có nó ngay bây giờ, kết nối sẽ không bao giờ đóng sẽ gây ra sự cố sau này (nếu không phải ngay lập tức) cho chương trình của bạn và RDBMS. Sẽ tốt hơn nếu tạo một lớp Java để giữ các trường từ ResultSet và trả về nó. ResultSet được liên kết với kết nối, do đó trả lại kết nối và không thể kết nối.

20

Một cách sạch sẽ để mã hóa điều này là truyền vào một đối tượng có phương thức gọi lại có tập kết quả.

Phương pháp khác của bạn tạo đối tượng với phương thức gọi lại với mã kết quả là Số mã xử lý và chuyển cho phương thức thực thi SQL.

Bằng cách đó, mã SQL & DB của bạn vẫn ở nơi nó thuộc về, bộ xử lý kết quả của bạn gần với nơi bạn sử dụng dữ liệu và mã SQL của bạn sẽ dọn dẹp khi cần.

interface ResultSetCallBack{ 
    void handleResultSet(ResultSet r); 
    } 

    void executeQuery(..., ResultSetCallBack cb){ 
    //get resultSet r ... 
    cb.handleResultSet(r); 
    //close connection 
    } 

    void printReport(){ 
    executeQuery(..., new ResultSetCallBack(){ 
     public void handleResultSet(ResultSet r) { 
     //do stuff with r here 
     } 
    }); 
    } 
1

Bạn thực sự không nên xử lý với JDBC ở cấp thấp hơn. Sử dụng một khung như spring thay vào đó, nó sẽ xử lý tất cả các hoạt động yêu cầu close() cho bạn.

+0

Đây là một điểm rất tốt. Sử dụng một khung như Spring giúp bạn không phải tái tạo lại bánh xe mọi lúc và tập trung vào việc xây dựng chiếc xe. –

+2

Thật không may, thiết lập Spring trên một dự án mới mà không có bất kỳ kinh nghiệm trước đây là khá nhiều doomed thất bại. – Bombe

+0

thực sự. JDBC không phải lúc nào cũng xấu. – Bozho

3

Bạn không thể sử dụng ResultSet sau khi bạn đã đóng Connection và/hoặc PreparedStatement. Vì vậy, bạn cần phải vượt qua một đối tượng mà trên đó để thực hiện một cuộc gọi lại vào phương pháp này.

Tất cả việc dọn dẹp phải được thực hiện trong các khối finally.

Viết lại nó như thế này

public ResultSet executeQuery(
    String sql, 
    String[] getValue, 
    CallbackObj cbObj 
) throws SQLException 
{ 
    final Connection conn = getConn(); 

    try 
    { 
    final PreparedStatement pstmt = conn.prepareStatement(sql); 

    try 
    { 
     if (getValue != null) 
     { 
     for (int i = 0; i < getValue.length; i++) 
     { 
      pstmt.setString(i + 1, getValue[i]); 
     } 
     } 

     final ResultSet rs = pstmt.executeQuery(); 

     try 
     { 
     cbObj.processResultSet(rs); 
     } 
     finally 
     { 
     // You may want to handle SQLException 
     // declared by close 
     rs.close(); 
     } 
    } 
    finally 
    { 
     // You may want to handle SQLException 
     // declared by close 
     pstmt.close(); 
    } 
    } 
    finally 
    { 
    // You may want to handle SQLException 
    // declared by close 
    conn.close(); 
    } 
} 
4

Bạn nên bao giờ qua ResultSet (hoặc Statement hoặc Connection) vào công chúng bên ngoài khối phương pháp mà họ đang có để được mua đóng cửa để tránh rò rỉ tài nguyên. Một thực tế phổ biến là chỉ lập bản đồ cho các số ResultSet đến một số List<Data> trong đó Data chỉ là một đối tượng javabean đại diện cho dữ liệu quan tâm.

Dưới đây là một ví dụ cơ bản:

public class Data { 
    private Long id; 
    private String name; 
    private Integer value; 
    // Add/generate public getters + setters. 
} 

và đây là một ví dụ cơ bản về cách xử lý nó một cách chính xác:

public List<Data> list() throws SQLException { 
    Connection connection = null; 
    PreparedStatement statement = null; 
    ResultSet resultSet = null; 
    List<Data> list = new ArrayList<Data>(); 

    try { 
     connection = database.getConnection(); 
     statement = connection.prepareStatement("SELECT id, name, value FROM data"); 
     resultSet = statement.executeQuery(); 
     while (resultSet.next()) { 
      Data data = new Data(); 
      data.setId(resultSet.getLong("id")); 
      data.setName(resultSet.getString("name")); 
      data.setValue(resultSet.getInt("value")); 
      list.add(data); 
     } 
    } finally { 
     if (resultSet != null) try { resultSet.close(); } catch (SQLException logOrIgnore) {} 
     if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {} 
     if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {} 
    } 

    return list; 
} 

bạn có thể sử dụng nó như sau:

List<Data> list = dataDAO.list(); 

Để tìm hiểu thêm về các phương pháp hay nhất với JDBC bạn có thể tìm thấy this basic kickoff article hữu ích.

+0

Điều này yêu cầu lưu trữ mọi thứ trong bộ nhớ. Khi bạn có nhiều hàng, trừ khi bạn dự định giữ dữ liệu trong bộ nhớ, thật thú vị khi chuyển một hàng chưa đọc đến bất kỳ thứ gì bạn muốn làm với nó, có thể ghi nó vào một tệp, lọc nó hoặc tính tổng, và tiêu thụ nó khi bạn xử lý nó. –

+0

@FlorianF Chỉ cần sử dụng LIMIT/OFFSET. – BalusC

+0

Điều đó giới thiệu rất nhiều phức tạp, nó nhân các truy cập cơ sở dữ liệu và phá vỡ nguyên tử của yêu cầu. Bạn có thể nhận được bản sao vì một hàng đã bị xóa ở đầu danh sách giữa hai yêu cầu "lát". –

3

Bạn có thể gọi ResultSet.getStatement để truy xuất StatementStatement.getConnection để truy xuất Connection.

Từ đây bạn có thể viết phương thức tiện ích closeResultSet sẽ đóng tất cả 3 cho bạn, không được đưa ra gì ngoài số ResultSet.

1

tôi muốn khuyên bạn nên làm điều gì đó như thế này:

public List<Map> executeQuery(Connection connection, String sql) throws SQLException 
{ 
    List<Map> rows = new ArrayList<Map>(); 

    PreparedStatement stmt = null; 
    ResultSet rs = null; 

    try 
    { 
     pstmt = conn.prepareStatement(sql); 
     rs = stmt.execute(); 
     int numColumns = rs.getMetaData().getColumnCount(); 

     while (rs.next()) 
     { 
      Map<String, Object> row = new LinkedHashMap<String, Object>(); 
      for (int i = 0; i < numColumns; ++i) 
      { 
       String column = rs.getColumnName(i+1); 
       Object value = rs.getObject(i+1); 
       row.put(column, value); 
      } 
      rows.add(row); 
     } 
    } 
    finally 
    { 
     close(rs); 
     close(stmt); 
    } 

    return rows; 
} 

public static void close(Statement s) 
{ 
    try 
    { 
     if (s != null) 
     { 
      s.close(); 
     } 
    } 
    catch (SQLException e) 
    { 
     e.printStackTrace(); 
    } 
} 

public static void close(ResultSet rs) 
{ 
    try 
    { 
     if (rs != null) 
     { 
      rs.close(); 
     } 
    } 
    catch (SQLException e) 
    { 
     e.printStackTrace(); 
    } 
} 
3

đâu để đóng một kết nối JDBC trong khi tôi muốn trở về ResultSet

Trên thực tế, bạn đã gần như tự trả lời câu hỏi đó. Như bạn đã thử nghiệm, việc đóng Connection sẽ giải phóng các tài nguyên JDBC liên quan đến nó (ít nhất, đây là cách mọi thứ hoạt động). Vì vậy, nếu bạn muốn trả lại ResultSet (Tôi sẽ quay lại sau này), bạn cần phải đóng kết nối "sau". Một cách để làm điều này sẽ rõ ràng để thông qua một kết nối đến phương pháp của bạn, một cái gì đó như thế này:

public ResultSet executeQuery(Connection conn, String sql, String[] getValue); 

Vấn đề là tôi không thực sự biết mục tiêu cuối cùng của bạn là gì và tại sao bạn cần thứ cấp quá thấp vì vậy tôi không chắc đây là lời khuyên hay. Trừ khi nếu bạn đang viết một khuôn khổ JDBC mức thấp (và xin vui lòng, đừng nói với tôi bạn không làm điều này), tôi thực sự không khuyên bạn nên trả lại một ResultSet. Ví dụ: nếu bạn muốn cấp một số lớp nghiệp vụ, hãy trả về một số đối tượng độc lập JDBC hoặc một bộ sưu tập chúng như những người khác đã khuyên thay vì một số ResultSet. Cũng xin lưu ý rằng RowSet một ResultSet vì vậy nếu bạn không nên sử dụng ResultSet thì bạn không nên sử dụng RowSet.

Cá nhân, tôi nghĩ bạn nên sử dụng một số lớp trợ giúp thay vì phát minh lại bánh xe. Trong khi mùa xuân có thể là quá mức cần thiết và có một chút đường cong học tập (quá nhiều nếu bạn không biết nó ở tất cả), mùa xuân không phải là cách duy nhất để đi và tôi khuyên bạn nên xem Commons DbUtils. Cụ thể hơn, nhìn vào QueryRunner và đặc biệt là query() phương pháp này:

public <T> T query(String sql, 
        ResultSetHandler<T> rsh, 
        Object... params) 
     throws SQLException 

Như bạn có thể thấy, phương pháp này cho phép để vượt qua một ResultSetHandler đó cho thấy một phương pháp gọi lại để chuyển đổi ResultSets thành các đối tượng khác như mô tả trong z5h's answer và DbUtils cung cấp một số thực hiện, chỉ cần chọn một trong đó sẽ phù hợp với nhu cầu của bạn. Ngoài ra, hãy xem các phương thức tiện ích của lớp DbUtils, ví dụ: DbUnit.close() khác nhau mà bạn có thể thấy thuận tiện khi đóng tài nguyên JDBC.Thực sự, trừ khi bạn có lý do rất tốt để làm điều đó (và tôi rất tò mò muốn biết chúng), không viết thêm một khung công tác JDBC nào khác, sử dụng giải pháp hiện có, nó sẽ giúp bạn tiết kiệm một số đau đớn và hơn thế nữa quan trọng, một số lỗi và bạn sẽ được hưởng lợi từ thiết kế tốt đã được chứng minh. Ngay cả đối với các công cụ cấp thấp, vẫn có các giải pháp hiện có (và đơn giản) như chúng ta đã thấy. Ít nhất, hãy kiểm tra.

3

Cách rõ ràng hơn là sử dụng CachedRowSetImpl. Nhưng trên MySQL 5.x + có một số lỗi với việc chọn cột theo tên hoặc nhãn.

Để sử dụng với MySQL sử dụng phiên bản này: https://stackoverflow.com/a/17399059/1978096

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