Đóng kết nối cơ sở dữ liệu trong Java


121

Tôi hơi bối rối, tôi đang đọc phần bên dưới từ http://en.wikipedia.org/wiki/Java_Database_Connectivity

Connection conn = DriverManager.getConnection(
     "jdbc:somejdbcvendor:other data needed by some jdbc vendor",
     "myLogin",
     "myPassword" );

Statement stmt = conn.createStatement();
try {
    stmt.executeUpdate( "INSERT INTO MyTable( name ) VALUES ( 'my name' ) " );
} finally {
    //It's important to close the statement when you are done with it
    stmt.close();
}

Bạn không cần phải đóng Kết nối conn? Điều gì thực sự đang xảy ra nếu conn.close () không xảy ra?

Tôi có một ứng dụng web riêng mà tôi đang duy trì hiện không đóng cả hai biểu mẫu, nhưng ứng dụng quan trọng thực sự là một stmt, một conn hay cả hai?

Trang web liên tục gặp sự cố nhưng máy chủ vẫn thông báo rằng đó là sự cố kết nối cơ sở dữ liệu, tôi nghi ngờ là nó chưa được đóng, nhưng tôi không biết nên đóng cái nào.


Luôn luôn là một phương pháp hay nhất để tự đóng các kết nối mà không phụ thuộc vào các trình điều khiển và mẫu khác để xử lý việc đóng. Việc không đóng kết nối sẽ dẫn đến các ổ cắm và tài nguyên sẽ mở mãi mãi cho đến khi gặp sự cố (không có kịch bản tài nguyên nào nữa) hoặc khởi động lại.
Arun Joshla

Câu trả lời:


196

Khi bạn sử dụng xong Connection, bạn cần phải đóng nó một cách rõ ràng bằng cách gọi close()phương thức của nó để giải phóng bất kỳ tài nguyên cơ sở dữ liệu nào khác (con trỏ, tay cầm, v.v.) mà kết nối có thể đang giữ.

Trên thực tế, mô hình an toàn trong Java là để đóng của bạn ResultSet, StatementConnection(theo thứ tự đó) trong một finallykhối khi bạn đang thực hiện với họ, một cái gì đó như thế:

Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;

try {
    // Do stuff
    ...

} catch (SQLException ex) {
    // Exception handling stuff
    ...
} finally {
    if (rs != null) {
        try {
            rs.close();
        } catch (SQLException e) { /* ignored */}
    }
    if (ps != null) {
        try {
            ps.close();
        } catch (SQLException e) { /* ignored */}
    }
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) { /* ignored */}
    }
}

Các finallykhối có thể được cải thiện đôi chút vào (để tránh việc kiểm tra null):

} finally {
    try { rs.close(); } catch (Exception e) { /* ignored */ }
    try { ps.close(); } catch (Exception e) { /* ignored */ }
    try { conn.close(); } catch (Exception e) { /* ignored */ }
}

Tuy nhiên, điều này rất dài dòng nên bạn thường sử dụng một lớp trợ giúp để đóng các đối tượng trong các phương thức trợ giúp null-safe và finallykhối sẽ trở thành một cái gì đó như thế:

} finally {
    DbUtils.closeQuietly(rs);
    DbUtils.closeQuietly(ps);
    DbUtils.closeQuietly(conn);
}

Và, trên thực tế, Apache Commons DbUtils có một DbUtilslớp chính xác làm điều đó, vì vậy không cần phải viết của riêng bạn.


3
Trợ giúp tuyệt vời, cảm ơn bạn! Tôi không hiểu hoặc không nghĩ về câu lệnh conn! = Null.
onaclov2000

1
@ onaclov2000 Vâng, rs, ps, conncó thể được nulltùy thuộc vào nơi vỡ mã. Đó là lý do tại sao đây được gọi là mẫu "an toàn".
Pascal Thivent

12
@Pascal Thivent: Thực ra chúng ta không cần phải đóng tất cả chúng. Cuốn sách "Core Java Tập hai - Tính năng Nâng cao" đã viết: closePhương thức của một Statementđối tượng tự động đóng liên kết ResultSetnếu câu lệnh có tập kết quả mở. Tương tự, closephương thức của Connectionlớp đóng tất cả Statementscác Connection.
Majid Azimi

12
@Majid: Trừ khi đó là kết nối gộp. Các tuyên bố sau đó sẽ bị rò rỉ.
BalusC

1
@BalusC: u có thể vui lòng giải thích những gì sẽ xảy ra khi một kết nối gộp được đóng bằng cách sử dụng connection.close () phương pháp
Krsna Chaitanya

61

Tốt hơn hết là đóng các đối tượng cơ sở dữ liệu / tài nguyên sau khi sử dụng. Tốt hơn để đóng các đối tượng kết nối, tập kết quả và câu lệnh trong finallykhối.

Cho đến Java7, tất cả các tài nguyên này cần được đóng lại bằng cách sử dụng một finallykhối. Nếu bạn đang sử dụng Java 7, thì để đóng tài nguyên, bạn có thể làm như sau.

try(Connection con = getConnection(url, username, password, "org.postgresql.Driver");
    Statement stmt = con.createStatement();
    ResultSet rs = stmt.executeQuery(sql);
) {

//statements
}catch(....){}

Bây giờ, các đối tượng con, stmt và rs trở thành một phần của khối try và java tự động đóng các tài nguyên này sau khi sử dụng.

Hy vọng tôi hữu ích.


Điều gì sẽ xảy ra nếu câu lệnh của tôi là ẩn, tức là ResultSet rs = conn.createStatement().executeQuery(sql);bên trong trykhối?
Antares42

1
Bạn sẽ không thể tham chiếu chúng trong khối {} cuối cùng để đóng. Nếu một ngoại lệ được ném ra, phương pháp chặt chẽ () của ResultSet sẽ không bao giờ được gọi
Dan

Điều gì xảy ra nếu tôi không đóng chúng?
Alex78191

nếu bạn không đóng chúng, rò rỉ bộ nhớ có thể xảy ra.
Yadu Krishnan,

14

Nó là đủ để đóng chỉ StatementConnection. Không cần phải đóng ResultSetđối tượng một cách rõ ràng .

Tài liệu Java nói về java.sql.ResultSet:

Đối tượng ResultSet được tự động đóng bởi đối tượng Statement đã tạo ra nó khi đối tượng Statement đó được đóng, thực thi lại hoặc được sử dụng để lấy kết quả tiếp theo từ một chuỗi nhiều kết quả.


Cảm ơn BalusC đã nhận xét: "Tôi sẽ không dựa vào điều đó. Một số trình điều khiển JDBC thất bại về điều đó."


25
Tôi sẽ không dựa vào điều đó. Một số trình điều khiển JDBC không thành công. Ví dụ: Oracle với "Đã vượt quá con trỏ mở tối đa", v.v. Chỉ cần đóng tất cả các tài nguyên đã mở một cách rõ ràng, không có lý do.
BalusC

1
Sau đó, tôi không muốn sử dụng các trình điều khiển không phù hợp với thông số kỹ thuật
Enerccio

2
Như BalusC đã chỉ ra, lập trình phòng thủ tốt là đóng kết nối một cách rõ ràng thay vì cố định phụ thuộc vào một nhà cung cấp cụ thể.
michaelok

11

Đúng. Bạn cần đóng tập kết quả, câu lệnh và kết nối. Nếu kết nối đến từ một nhóm, việc đóng nó sẽ thực sự gửi nó trở lại nhóm để sử dụng lại.

Bạn thường phải làm điều này trong một finally{} khối, như vậy nếu một ngoại lệ được ném ra, bạn vẫn có cơ hội để đóng điều này.

Nhiều khuôn khổ sẽ giải quyết vấn đề phân bổ / phân bổ tài nguyên này cho bạn. ví dụ: Spring's JdbcTemplate . Apache DbUtils có các phương thức để xem xét sau khi đóng tập kết quả / câu lệnh / kết nối cho dù null hay không (và bắt các ngoại lệ khi đóng), điều này cũng có thể hữu ích.


1
Khi tôi chèn nhật thực "cuối cùng" thích làm nổi bật nó cho tôi biết nó sai. cái này có nên đi sau khối bắt không?
onaclov2000

Đúng. thử {} bắt {} cuối cùng {}. Bắt {} là tùy chọn, btw. Cũng giống như cuối cùng {}
Brian Agnew

Tôi đã chuyển các câu lệnh "đóng" đến cuối cùng, nhưng họ chỉ nói "sqlexception", có gợi ý nào không?
onaclov2000

1
close () ném một SQLException. Bạn phải xử lý điều đó. Xem DbUtils.closeQuietly () để xử lý điều này một cách âm thầm.
Brian Agnew

> Điều gì thực sự đang xảy ra nếu conn.close () không xảy ra?
Alex78191

8

Trên thực tế, tốt nhất là bạn nên sử dụng khối try-with-resources và Java sẽ đóng tất cả các kết nối cho bạn khi bạn thoát khỏi khối try.

Bạn nên làm điều này với bất kỳ đối tượng nào triển khai AutoClosable.

try (Connection connection = getDatabaseConnection(); Statement statement = connection.createStatement()) {
    String sqlToExecute = "SELECT * FROM persons";
    try (ResultSet resultSet = statement.execute(sqlToExecute)) {
        if (resultSet.next()) {
            System.out.println(resultSet.getString("name");
        }
    }
} catch (SQLException e) {
    System.out.println("Failed to select persons.");
}

Lệnh gọi getDatabaseConnection vừa được tạo. Thay thế nó bằng một cuộc gọi giúp bạn có được kết nối JDBC SQL hoặc kết nối từ một nhóm.


Vì vậy, bạn không phải tự đóng kết nối trong trường hợp này?
Colin D

1
Chính xác. Bạn không cần phải đóng kết nối một cách rõ ràng. Nó sẽ bị đóng khi đạt đến cuối khối mã thử.
Joe

7

Có, bạn cần đóng Kết nối. Nếu không, máy khách cơ sở dữ liệu thường sẽ giữ kết nối socket và các tài nguyên khác mở.


... cho đến khi nó thoát ra. Điều này liên kết các tài nguyên hữu hạn khác nhau ở phía máy khách và máy chủ. Nếu một máy khách thực hiện kiểu này quá nhiều, nó có thể gây ra sự cố cho chính máy khách, dịch vụ cơ sở dữ liệu và thậm chí có thể cho các ứng dụng khác đang chạy trên máy khách hoặc máy chủ.
Stephen C
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.