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.