Zamykanie połączeń z bazą danych w Javie


121

Jestem trochę zdezorientowany, czytałem poniżej z 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();
}

Nie musisz zamykać połączenia Connected? Co tak naprawdę się dzieje, jeśli conn.close () nie występuje?

Mam prywatną aplikację internetową, którą utrzymuję, która obecnie nie zamyka żadnego z formularzy, ale czy naprawdę ważna jest ta stmt, połączona, czy obie?

Witryna sporadycznie wyłącza się, ale serwer wciąż mówi, że jest to problem z połączeniem z bazą danych. Podejrzewam, że nie jest zamykana, ale nie wiem, który z nich zamknąć.


Najlepszą praktyką jest samodzielne zamykanie połączeń, bez polegania na innych sterownikach i szablonach do obsługi zamykania. Niepowodzenie zamknięcia połączenia spowoduje, że gniazda i zasoby będą otwarte na zawsze, aż do awarii (koniec scenariusza dotyczącego zasobów) lub ponownego uruchomienia.
— Arun Joshla

Odpowiedzi:


196

Kiedy skończysz używać swojego Connection, musisz jawnie zamknąć go, wywołując jego close()metodę, aby zwolnić inne zasoby bazy danych (kursory, uchwyty itp.), Które może utrzymywać połączenie.

Faktycznie, bezpieczne deseń w Javie jest zamknąć ResultSet, Statementi Connection(w tej kolejności) w finallybloku, kiedy jesteś z nimi zrobić, coś takiego:

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 */}
    }
}

finallyBlok może być nieznacznie poprawiła się (w celu uniknięcia sprawdzanie null):

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

Ale nadal jest to bardzo rozwlekłe, więc generalnie kończysz używając klasy pomocniczej do zamykania obiektów w metodach pomocniczych o wartości null-safe, a finallyblok staje się mniej więcej taki:

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

W rzeczywistości Apache Commons DbUtils ma DbUtilsklasę, która dokładnie to robi, więc nie ma potrzeby pisania własnej.


3
Świetna pomoc, dziękuję! Nie złapałem ani nie pomyślałem o instrukcjach conn! = Null.
— onaclov2000

1
@ onaclov2000 Tak rs, ps, connmoże być nullw zależności od miejsca złamania kodu. Dlatego jest to znane jako „bezpieczny” wzorzec.
— Pascal Thivent

12
@Pascal Thivent: Właściwie nie musimy ich wszystkich zamykać. Książka „Core Java Volume 2 - Advanced Features” napisała: closeMetoda Statementobiektu automatycznie zamyka skojarzony, ResultSetjeśli instrukcja ma otwarty zestaw wyników. Podobnie, closesposób z Connectionklasy zamyka wszystkie Statementsz Connection.
— Majid Azimi

12
@Majid: Chyba że jest to połączenie puli. Oświadczenia wyciekłyby wtedy.
— BalusC,

1
@BalusC: Czy możesz wyjaśnić, co się dzieje, gdy połączenie zbiorcze jest zamykane za pomocą metody connection.close ()
— Krsna Caitanya,

61

Zawsze lepiej jest zamknąć bazę danych / obiekty zasobów po użyciu. Lepiej zamknąć obiekty połączenia, zestawu wyników i instrukcji w finallybloku.

Aż do Java7 wszystkie te zasoby muszą być zamknięte za pomocą finallybloku. Jeśli używasz języka Java 7, to w celu zamknięcia zasobów możesz wykonać następujące czynności.

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

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

Teraz obiekty con, stmt i rs stają się częścią bloku try, a java automatycznie zamyka te zasoby po użyciu.

Mam nadzieję, że byłem pomocny.


Co się stanie, jeśli moja instrukcja jest niejawna, tj. ResultSet rs = conn.createStatement().executeQuery(sql);Wewnątrz trybloku?
— Antares42,

1
Nie będzie można odwoływać się do nich w ostatnim bloku {} w celu zamknięcia. Jeśli zostanie zgłoszony wyjątek, metoda close () elementu ResultSet nigdy nie zostanie wywołana
— Dan

Co się stanie, jeśli ich nie zamknę?
— Alex78191

jeśli ich nie zamkniesz, mogą wystąpić wycieki pamięci.
— Yadu Krishnan

14

Wystarczy zamknąć tylko Statementi Connection. Nie ma potrzeby jawnego zamykania ResultSetobiektu.

Dokumentacja Java mówi o java.sql.ResultSet:

Obiekt ResultSet jest automatycznie zamykany przez obiekt Statement, który go wygenerował, gdy ten obiekt Statement zostanie zamknięty, ponownie wykonany lub użyty do pobrania następnego wyniku z sekwencji wielu wyników.


Dzięki BalusC za komentarze: „Nie polegałbym na tym. Niektóre sterowniki JDBC na tym zawodzą”.


25
Nie polegałbym na tym. Niektóre sterowniki JDBC zawodzą. Np. Oracle z „Przekroczono maksymalną liczbę otwartych kursorów” itp. Po prostu jawnie zamknij wszystkie otwarte zasoby, bez wymówek.
— BalusC,

1
Wolałbym nie używać sterowników, które nie są zgodne ze specyfikacją
— Enerccio

2
Jak wskazuje BalusC, dobrym programowaniem obronnym jest jawne zamykanie połączenia zamiast podłączania na stałe zależności od konkretnego dostawcy.
— michaelok

11

Tak. Musisz zamknąć zestaw wyników, instrukcję i połączenie. Jeśli połączenie pochodzi z puli, zamknięcie go w rzeczywistości odsyła z powrotem do puli w celu ponownego wykorzystania.

Zwykle musisz to zrobić w finally{}bloku, tak że jeśli zostanie zgłoszony wyjątek, nadal masz szansę to zamknąć.

Wiele frameworków zajmie się tym problemem alokacji / zwalniania zasobów. np. Spring's JdbcTemplate . Apache DbUtils ma metody, które dbają o zamknięcie zestawu wyników / instrukcji / połączenia, niezależnie od tego, czy mają wartość null, czy nie (i przechwytywanie wyjątków po zamknięciu), co również może pomóc.


1
Kiedy wstawiam zaćmienie „ostatecznie”, lubi je podkreślać, mówiąc mi, że jest źle. czy powinno to iść po blokach catch?
— onaclov2000

Tak. spróbuj {} złapać {} w końcu {}. Przy okazji, haczyk {} jest opcjonalny. Tak jak w końcu {}
— Brian Agnew,

Przeniosłem „zamknięte” stwierdzenia na ostatnie, ale mówią tylko „sqlexception”, jakieś sugestie?
— onaclov2000

1
close () zgłasza SQLException. Musisz sobie z tym poradzić. Zobacz DbUtils.closeQuietly (), aby obsłużyć to dyskretnie.
— Brian Agnew

> Co się naprawdę dzieje, jeśli conn.close () nie występuje?
— Alex78191

8

Właściwie najlepiej jest użyć bloku try-with-resources, a Java zamknie wszystkie połączenia po wyjściu z bloku try.

Powinieneś to zrobić z każdym obiektem, który implementuje 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.");
}

Wywołanie getDatabaseConnection zostało właśnie utworzone. Zastąp go wywołaniem, które zapewnia połączenie JDBC SQL lub połączenie z puli.


Więc nie musisz ręcznie zamykać połączenia w tym przypadku?
— Colin D

1
Poprawny. Nie musisz jawnie zamykać połączenia. Zostanie zamknięty po osiągnięciu końca bloku kodu try.
— Joe

7

Tak, musisz zamknąć połączenie. W przeciwnym razie klient bazy danych będzie zazwyczaj utrzymywał otwarte połączenie z gniazdem i inne zasoby.


... dopóki nie wyjdzie. To ogranicza różne ograniczone zasoby po stronie klienta i serwera. Jeśli klient wykonuje tego typu czynności za dużo, może to powodować problemy dla samego klienta, usługi bazy danych, a nawet dla innych aplikacji działających na komputerze klienckim lub serwerze.
— Stephen C
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.