Bağlantı daha sonra kapatılmasına rağmen JDBC Sonuç Kümeleri ve Deyimleri ayrı olarak kapatılmalı mı?


256

Kullanımdan sonra tüm JDBC kaynaklarını kapatmak iyi bir alışkanlıktır. Ancak aşağıdaki kod varsa, Resultset ve deyimi kapatmak gerekli mi?

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) {};
}

Soru, bağlantının kapatılmasının işi yapıp yapmadığı veya bazı kaynakları kullanımda bırakıp bırakmadığıdır.


Yanıtlar:


199

Yaptığınız şey mükemmel ve çok iyi bir uygulamadır.

İyi uygulamasını söylememin sebebi ... Örneğin, herhangi bir nedenle "ilkel" tipte bir veritabanı havuzu kullanıyorsanız ve çağırırsanız connection.close(), bağlantı havuza geri gönderilir ve ResultSet/ / Statementasla kapatılmaz ve sonra birçok farklı yeni sorunla karşılaşacak!

Böylece her zaman connection.close()temizlemek için güvenemezsiniz .

Umarım bu yardımcı olur :)


4
... ve her şeyi açıkça kapatmak için en belirgin neden.
Zeemee

2
Sonuç kümelerini ve ifadeleri kapatmanın iyi bir uygulama olduğunu kabul ediyorum. Ancak, sonuç kümeleri ve ifadeler çöp toplanır - sonsuza kadar açık kalmazlar ve "birçok farklı yeni sorunla karşılaşmazsınız".
stepanian

3
@Ralph Stevens - Buna güvenemezsin. Çöp toplandıktan sonra bile ResultSet'in kapatılmadığı için MSSQL JDBC sürücüsünün bellek sızdırdığı bir durum yaşadım.
Paul

7
@Paul - İlginç. Bu bana JDBC sürücüsünün bir eksikliği gibi geliyor.
stepanian

2
@tleb - beklendiği gibi çalışır. teoride istisnalar "pahalı" olmasına rağmen (zaten tanımlamış olduğunuz) çok küçük bir performans vuruş olacaktır
Paul

124

Java 1.7, kaynaklarla deneme ifadesi sayesinde hayatımızı kolaylaştırır .

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.
    }
}

Bu sözdizimi oldukça kısa ve zariftir. Ve connectiongerçekten statementyaratılamadığı zamanlarda bile kapatılacak .


56
Böyle yuva yapmanıza gerek yok, hepsini tek bir kaynak-denemede yapabilirsiniz, sadece kaynak bildirimlerini ayrı ifadeler olarak ayırın (ayırarak ;)
Mark Rotteveel

2
Rotteveel'i İşaretle: Üç Bağlantı, Deyim ve Sonuç Kümesi için tek bir deneme kullanabilirsiniz, ancak birkaç sorgu gerçekleştirmek istiyorsanız, yeni bir sorgu başlatmadan önce önceki Sonuç Kümesini kapatmanız gerekir. En azından ben kullandım DBMS böyle çalıştı.
Raúl Salinas-Monteagudo

neden böyle bir şey yapmıyorsun? try (açık bağlantı) {try (birden çok ifade ve sonuç kümesi) {özellikle sonraki sorgu sonuçları öncekilerle hesaplanabiliyorsa.
Daniel Hajduk

Daniel: Bu kalıbı kullandığımda, temeldeki JDBC arka ucu bir ResultSet'in açık ve ikinci bir açık kalmasını desteklemedi.
Raúl Salinas-Monteagudo

rascio, catch bloğunda ihtiyacınız olan her şeyi yapabilirsiniz
Raúl Salinas-Monteagudo

73

Gönderen javadocs :

Bir Statementnesne kapatıldığında, ResultSetvarsa mevcut nesnesi de kapatılır.

Bununla birlikte, javadoclar, altta yatanları kapattığınızda kapalı Statementve ResultSetkapalı olup olmadıkları konusunda net değildir Connection. Sadece bir Bağlantıyı kapatmanın:

ConnectionOtomatik olarak serbest bırakılmasını beklemek yerine, bu nesnenin veritabanını ve JDBC kaynaklarını derhal serbest bırakır .

Bence, her zaman mutlaka kapatılması ResultSets, Statementsve Connectionssen uygulanması gibi kendileriyle bittiğinde closeveritabanı sürücüleri arasında değişebilir.

Aşağıdaki gibi yöntemlerle kendinize kazan plakalı bir sürü kod kaydedebilirsiniz closeQuietlyiçinde DbUtils Apache'den.


1
Teşekkürler dogbane. Mesele şu ki, Connection.close uygulamasına güvenemezsiniz, değil mi?
Zeemee


39

Şimdi Java ile Oracle kullanıyorum. Benim açımdan:

Sen kapatmalıdır ResultSetve StatementOracle imleçler tutmak ile daha önce sorunları var açıkça çünkü bağlantıyı kapattıktan sonra bile açın. ResultSet(İmleci) kapatmazsanız, Maksimum açık imleç aşıldı gibi bir hata atar .

Kullandığınız diğer veritabanlarında da aynı sorunla karşılaşabileceğinizi düşünüyorum.

Tamamlandığında ResultSet'i kapat öğretici aşağıdadır :

Bittiğinde Sonuç Kümesini Kapat

Nesne kapatıldığında nesneyi örtük olarak kapatsa bile nesneyle ResultSetçalışmayı bitirir bitirmez nesneyi kapatın , açıkça kapatılması , çöp toplayıcısına belleği sorgulamaya bağlı olarak çok fazla bellek kaplayabileceği için olabildiğince erken hatırlama şansı verir .ResultSetStatementResultSetResultSetResultSet

ResultSet.close();


Teşekkürler hilal, bunlar mümkün olduğunca erken kapatmak için iyi nedenlerdir. Ancak, ResultSet ve Deyimin Bağlantıdan önce doğrudan kapatılması önemli midir (bu, bazı durumlarda mümkün olduğu kadar erken değil)?
Zeemee

Bağlantıyı kapatırsanız, tüm

Bağlantıdan önce sonuç kümesini neden kapatmalıyım? Kahin sürücü problemleri yüzünden mi demek istiyorsun?
Zeemee

1
İşte daha genel açıklama :) stackoverflow.com/questions/103938/…

Teoride, ifadeyi kapatırsanız sonuç kümelerini kapatmak zorunda kalmazsınız , ancak muhtemelen iyi bir uygulamadır.
rogerdpack

8

Daha kompakt bir kod istiyorsanız, Apache Commons DbUtils kullanmanızı öneririm . Bu durumda:

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);
}

3
i (rs.Close yerine bu kodu kullanırsanız,) stmt.close (), conn.Close () ne olacak
Onkar Musale

3

JDBC ile ilişkili kaynakları kapatmak için doğru ve güvenli yöntem bu ( JDBC Kaynaklarını Doğru Şekilde Kapatma - Her Zaman Alınan ):

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();
}

3

Eğer önemli değil ConnectionPooble değil. Poolable bağlantı bile havuza dönmeden önce temizlenmesi gerekir.

"Temizle" genellikle sonuç kümelerini kapatma ve bekleyen işlemleri geri alma, ancak bağlantıyı kapatma anlamına gelmez. Aksi takdirde havuzlama mantığını kaybeder.


2

Hayır, herhangi bir şeyi kapatmanız gerekmez, ancak bağlantı. Daha yüksek bir nesneyi kapatarak JDBC spesifikasyonlarına göre, daha düşük nesneleri otomatik olarak kapatacaktır. Kapatılması Connection, Statementbağlantının oluşturduğu tüm bağlantıları kapatır . Herhangi biri kapatıldığında Statement, ResultSetbununla oluşturulan tüm öğeler kapanır Statement. ConnectionPooble olup olmadığı önemli değil. Poolable bağlantı bile havuza dönmeden önce temizlenmesi gerekir.

Tabii ki Connectionçok sayıda ifade oluşturmada uzun iç içe döngüler olabilir , sonra bunları kapatmak uygundur. Neredeyse hiç kapanmam ResultSet, kapanırken Statementveya Connectiononları kapatırken aşırı görünüyor .


1

Yeniden kullanılabilir bir astar oluşturmak için aşağıdaki yöntemi oluşturdum:

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();
        }
    }
}

Bu kod DB sorguları gönderen tüm sınıfları için miras bir üst sınıfta kullanın. Bir sonuç yok bile Oneliner tüm sorgularda kullanabilirsiniz.Yöntemi ResultSet, deyimi, bağlantı doğru sırada kapatma ilgilenir. Nihayet bloğum böyle görünüyor.

finally {
    oneMethodToCloseThemAll(resultSet, preStatement, sqlConnection);
}


-1

Bazı kolaylık işlevleri:

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);
}

Burada hiçbir şeyi kapatan hiçbir şey yok. Açıkça artık istenmediği halde, tüm cevabı boşa harcayan anlamsız bir döngü.
Lorne Marquis

-1

Java 6 formu ile kapatılmadan önce kapalı olup olmadığını kontrol etmek daha iyi olduğunu düşünüyorum (örneğin bazı bağlantı havuzu diğer iş parçacığında bağlantıyı çıkarırsa) - örneğin bazı ağ sorunu - deyim ve sonuç kümesi durumu kapatılabilir. (sık sık olmaz, ancak Oracle ve DBCP ile bu sorunu yaşadım). Benim desen (eski Java sözdiziminde) içindir:

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());
        }
    }
}

Teoride% 100 mükemmel değildir, çünkü yakın durumun kontrol edilmesi ile yakının kendisi arasında devlet değişikliğine çok az yer vardır. En kötü durumda, uzun bir uyarı alacaksınız. - ancak uzun dönem sorgularda durum değişikliği olasılığından daha azdır. Bu kalıbı üretimde "ortalama" yük (150 eşzamanlı kullanıcı) ile kullanıyoruz ve onunla bir sorunumuz yoktu - bu yüzden bu uyarı mesajını asla görmeyin.


Testlere ihtiyacınız yoktur isClosed(), çünkü zaten kapalı olan bunlardan herhangi birinin kapatılması bir işlem değildir. Hangi zamanlama penceresi sorunu ortadan kaldırır. Bu da Connection, Statementve ResultSetyerel değişkenler yapılarak ortadan kaldırılır .
Lorne Marquis
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.