Haruskah Hasil dan Pernyataan JDBC ditutup secara terpisah meskipun Koneksi ditutup sesudahnya?

256

Dikatakan sebagai kebiasaan yang baik untuk menutup semua sumber daya JDBC setelah penggunaan. Tetapi jika saya memiliki kode berikut, apakah perlu untuk menutup Resultset dan Pernyataan?

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = // Retrieve connection
    stmt = conn.prepareStatement(// Some SQL);
    rs = stmt.executeQuery();
} catch(Exception e) {
    // Error Handling
} finally {
    try { if (rs != null) rs.close(); } catch (Exception e) {};
    try { if (stmt != null) stmt.close(); } catch (Exception e) {};
    try { if (conn != null) conn.close(); } catch (Exception e) {};
}

Pertanyaannya adalah apakah penutupan koneksi berfungsi atau apakah ada sumber daya yang digunakan.

Zeemee
sumber
Kemungkinan rangkap dari Koneksi Database Penutupan di Jawa
Austin Schäfer

Jawaban:

199

Apa yang telah Anda lakukan adalah latihan yang sempurna dan sangat baik.

Alasan saya mengatakan ini adalah praktik yang baik ... Misalnya, jika karena alasan tertentu Anda menggunakan tipe penyatuan basis data "primitif" dan Anda menelepon connection.close(), koneksi akan dikembalikan ke kolam dan ResultSet/ Statementtidak akan pernah ditutup dan kemudian Anda akan mengalami banyak masalah baru yang berbeda!

Jadi Anda tidak bisa selalu mengandalkan connection.close()pembersihan.

Saya harap ini membantu :)

Paul
sumber
4
... dan alasan paling jelas untuk menutup semuanya secara eksplisit.
Zeemee
2
Saya setuju bahwa itu adalah praktik yang baik untuk menutup set dan pernyataan hasil. Namun, set dan pernyataan hasil adalah sampah yang dikumpulkan - mereka tidak tetap terbuka selamanya dan Anda tidak "mengalami banyak masalah baru yang berbeda".
stepanian
3
@Ralph Stevens - Anda tidak dapat mengandalkan itu. Saya pernah mengalami situasi di mana driver MSSQL JDBC membocorkan memori karena ResultSet tidak ditutup, bahkan setelah dikumpulkan.
Paul
7
@ Paul - Menarik. Bagi saya itu kedengarannya seperti kekurangan driver JDBC.
stepanian
2
@ tleb - itu akan berfungsi seperti yang diharapkan. meskipun secara teori, pengecualian "mahal" sehingga akan ada ketukan kinerja yang sangat kecil (yang telah Anda identifikasi)
Paul
124

Java 1.7 membuat hidup kita lebih mudah berkat pernyataan coba-dengan-sumber daya .

try (Connection connection = dataSource.getConnection();
    Statement statement = connection.createStatement()) {
    try (ResultSet resultSet = statement.executeQuery("some query")) {
        // Do stuff with the result set.
    }
    try (ResultSet resultSet = statement.executeQuery("some query")) {
        // Do more stuff with the second result set.
    }
}

Sintaks ini cukup singkat dan elegan. Dan connectionmemang akan ditutup bahkan ketika yang statementtidak bisa dibuat.

Raúl Salinas-Monteagudo
sumber
56
Anda tidak perlu bersarang seperti ini, Anda bisa melakukan semuanya dalam satu kali coba-coba dengan sumber daya, cukup memperlakukan deklarasi sumber daya sebagai pernyataan terpisah (dipisahkan oleh ;)
Mark Rotteveel
2
Mark Rotteveel: Anda dapat menggunakan percobaan tunggal untuk ketiga Koneksi, Pernyataan dan ResultSet, tetapi jika Anda ingin melakukan beberapa pertanyaan, Anda harus menutup ResultSet sebelumnya sebelum memulai permintaan baru. Setidaknya begitulah cara kerja DBMS yang saya gunakan.
Raúl Salinas-Monteagudo
kenapa kamu tidak melakukan hal seperti ini? coba (koneksi terbuka) {coba (beberapa pernyataan & hasil) {terutama ketika hasil kueri berikutnya dapat dihitung dengan yang sebelumnya.
Daniel Hajduk
Daniel: Ketika saya menggunakan pola itu, backend JDBC yang mendasarinya tidak mendukung menjaga ResultSet tetap terbuka dan membuka yang kedua.
Raúl Salinas-Monteagudo
rascio, Anda dapat melakukan apa pun yang Anda butuhkan di blok tangkapan
Raúl Salinas-Monteagudo
73

Dari javadocs :

Ketika suatu Statementobjek ditutup, ResultSetobjek saat ini , jika ada, juga ditutup.

Namun, javadocs tidak begitu jelas tentang apakah Statementdan ResultSetditutup ketika Anda menutup yang mendasarinya Connection. Mereka hanya menyatakan bahwa menutup Koneksi:

Merilis Connectionbasis data objek dan sumber daya JDBC ini segera alih-alih menunggu mereka dirilis secara otomatis.

Menurut pendapat saya, selalu ditutup secara eksplisit ResultSets, Statementsdan Connectionsketika Anda selesai dengan mereka sebagai implementasi closedapat bervariasi antara driver database.

Anda dapat menghemat banyak kode boiler-plate dengan menggunakan metode seperti closeQuietlypada DBUtils dari Apache.

dogbane
sumber
1
Terima kasih dogbane. Intinya adalah Anda tidak bisa bergantung pada implementasi Connection.close, kan?
Zeemee
1
catatan samping untuk n00bs seperti saya - stackoverflow.com/questions/3992199/what-is-boilerplate-code
david blaine
39

Saya sekarang menggunakan Oracle dengan Java. Di sini sudut pandang saya:

Anda harus menutup ResultSetdan Statementsecara eksplisit karena Oracle memiliki masalah sebelumnya dengan menjaga kursor tetap terbuka bahkan setelah menutup koneksi. Jika Anda tidak menutup ResultSet(kursor) itu akan menimbulkan kesalahan seperti kursor terbuka maksimum terlampaui .

Saya pikir Anda mungkin mengalami masalah yang sama dengan database lain yang Anda gunakan.

Ini adalah tutorial Tutup ResultSet ketika selesai :

Tutup ResultSet ketika selesai

Tutup ResultSetobjek segera setelah Anda selesai bekerja dengan ResultSetobjek meskipun Statementobjek menutup ResultSetobjek secara implisit ketika menutup, menutup ResultSetsecara eksplisit memberi kesempatan kepada pengumpul sampah untuk mengingat memori sedini mungkin karena ResultSetobjek dapat menempati banyak memori tergantung pada permintaan.

ResultSet.close();

kebiruan
sumber
Terima kasih hilal, ini adalah alasan bagus untuk menutupnya sedini mungkin. Namun, apakah masalah jika ResultSet dan Pernyataan ditutup secara langsung sebelum Koneksi (ini berarti dalam beberapa kasus: tidak sedini mungkin)?
Zeemee
Jika Anda menutup koneksi, itu akan menutup semua pernyataan
Dan mengapa saya harus menutup resultset sebelum koneksi? Maksud Anda karena masalah driver oracle?
Zeemee
1
di sini adalah klarifikasi yang lebih umum :) stackoverflow.com/questions/103938/…
Secara teori, jika Anda menutup pernyataan, Anda tidak harus menutup hasil, tetapi mungkin praktik yang baik.
rogerdpack
8

Jika Anda menginginkan kode yang lebih ringkas, saya sarankan menggunakan Apache Commons DbUtils . Pada kasus ini:

Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
    conn = // Retrieve connection
    stmt = conn.prepareStatement(// Some SQL);
    rs = stmt.executeQuery();
} catch(Exception e) {
    // Error Handling
} finally {
    DbUtils.closeQuietly(rs);
    DbUtils.closeQuietly(stmt);
    DbUtils.closeQuietly(conn);
}
baron5
sumber
3
apa yang akan terjadi jika saya menggunakan kode ini alih-alih rs.close (), stmt.close (), conn.close ()
Onkar Musale
3

Metode yang benar dan aman untuk menutup sumber daya yang terkait dengan JDBC ini (diambil dari Cara Menutup Sumber Daya JDBC dengan Benar - Setiap Saat ):

Connection connection = dataSource.getConnection();
try {
    Statement statement = connection.createStatement();

    try {
        ResultSet resultSet = statement.executeQuery("some query");

        try {
            // Do stuff with the result set.
        } finally {
            resultSet.close();
        }
    } finally {
        statement.close();
    }
} finally {
    connection.close();
}
Prasanth Jayachandran
sumber
3

Tidak masalah apakah Connectiondapat dikumpulkan atau tidak. Bahkan koneksi yang dapat dikumpulkan harus dibersihkan sebelum kembali ke kolam.

"Bersihkan" biasanya berarti menutup hasil & mengembalikan semua transaksi yang tertunda tetapi tidak menutup koneksi. Jika tidak, pooling kehilangan akal sehatnya.

Gila tenang
sumber
2

Tidak, Anda tidak perlu menutup apa pun TETAPI koneksi. Per spesifikasi JDBC yang menutup objek yang lebih tinggi akan secara otomatis menutup objek yang lebih rendah. Penutup Connectionakan menutup Statementkoneksi apa pun yang telah dibuat. Menutup apa pun Statementakan menutup semua ResultSetyang dibuat oleh itu Statement. Tidak masalah apakah Connectiondapat dikumpulkan atau tidak. Bahkan koneksi yang dapat dikumpulkan harus dibersihkan sebelum kembali ke kolam.

Tentu saja Anda mungkin memiliki loop panjang bersarang pada Connectionpembuatan banyak pernyataan, lalu menutupnya adalah tepat. Saya hampir tidak pernah menutup ResultSet, sepertinya berlebihan ketika menutup Statementatau ConnectionAKAN menutupnya.

Enerccio
sumber
1

Saya membuat Metode berikut untuk membuat One Liner yang dapat digunakan kembali:

public void oneMethodToCloseThemAll(ResultSet resultSet, Statement statement, Connection connection) {
    if (resultSet != null) {
        try {
            if (!resultSet.isClosed()) {
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (statement != null) {
        try {
            if (!statement.isClosed()) {
                statement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    if (connection != null) {
        try {
            if (!connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Saya menggunakan Kode ini dalam Kelas induk yang diwarisi ke semua kelas saya yang mengirim DB Queries. Saya dapat menggunakan Oneliner pada semua Pertanyaan, bahkan jika saya tidak memiliki resultSet. Metode ini menangani penutupan ResultSet, Pernyataan, Koneksi dalam urutan yang benar. Inilah yang akhirnya tampak seperti blok saya.

finally {
    oneMethodToCloseThemAll(resultSet, preStatement, sqlConnection);
}
menjerat
sumber
-1

Sejauh yang saya ingat, di JDBC saat ini, Resultset dan pernyataan mengimplementasikan antarmuka AutoCloseable. Itu berarti mereka ditutup secara otomatis saat dihancurkan atau keluar dari ruang lingkup.

Gila tenang
sumber
3
Tidak, itu hanya berarti bahwa pernyataan closetersebut dipanggil pada akhir pernyataan coba-dengan-sumber daya. Lihat docs.oracle.com/javase/tutorial/essential/exceptions/… dan docs.oracle.com/javase/8/docs/api/java/lang/AutoCloseable.html .
Zeemee
-1

Beberapa fungsi kenyamanan:

public static void silentCloseResultSets(Statement st) {
    try {
        while (!(!st.getMoreResults() && (st.getUpdateCount() == -1))) {}
    } catch (SQLException ignore) {}
}
public static void silentCloseResultSets(Statement ...statements) {
    for (Statement st: statements) silentCloseResultSets(st);
}
Gila tenang
sumber
Tidak ada yang menutup apa pun di sini. Hanya perulangan sia-sia yang dengan sia-sia membaca seluruh respons, meskipun jelas tidak diinginkan lagi.
Marquis of Lorne
-1

Dengan Java 6 form saya pikir lebih baik untuk memeriksa apakah sudah ditutup atau tidak sebelum ditutup (misalnya jika beberapa pooler koneksi membatalkan koneksi di utas lainnya) - misalnya beberapa masalah jaringan - pernyataan dan status resultset dapat ditutup. (tidak sering terjadi, tapi saya punya masalah dengan Oracle dan DBCP). Pola saya untuk itu (dalam sintaks Java yang lebih lama) adalah:

try {
    //...   
    return resp;
} finally {
    if (rs != null && !rs.isClosed()) {
        try {
            rs.close();
        } catch (Exception e2) { 
            log.warn("Cannot close resultset: " + e2.getMessage());
        }
    }
    if (stmt != null && !stmt.isClosed()) {
        try {
            stmt.close();
        } catch (Exception e2) {
            log.warn("Cannot close statement " + e2.getMessage()); 
        }
    }
    if (con != null && !conn.isClosed()) {
        try {
            con.close();
        } catch (Exception e2) {
            log.warn("Cannot close connection: " + e2.getMessage());
        }
    }
}

Secara teori itu tidak 100% sempurna karena antara memeriksa keadaan dekat dan penutupan itu sendiri ada sedikit ruang untuk perubahan keadaan. Dalam kasus terburuk Anda akan mendapatkan peringatan lama. - tetapi lebih kecil dari kemungkinan perubahan negara dalam permintaan jangka panjang. Kami menggunakan pola ini dalam produksi dengan muatan "rata-rata" (150 pengguna simultan) dan kami tidak punya masalah dengan itu - jadi jangan pernah melihat pesan peringatan itu.

Csákány Róbert
sumber
Anda tidak perlu isClosed()tes, karena menutup semua yang sudah ditutup ini adalah larangan. Yang menghilangkan masalah jendela waktu. Yang juga akan dihilangkan dengan membuat variabel Connection, Statement, dan ResultSetlokal.
Marquis of Lorne