Schließen von Datenbankverbindungen in Java

121

Ich bin etwas verwirrt und habe das Folgende von http://en.wikipedia.org/wiki/Java_Database_Connectivity gelesen

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

Müssen Sie die Conn-Verbindung nicht schließen? Was passiert wirklich, wenn conn.close () nicht auftritt?

Ich habe eine private Web-App, die ich gerade pflege und die derzeit keines der beiden Formulare schließt. Ist das wichtige wirklich das stmt-Formular, das conn-Formular oder beides?

Die Site fällt immer wieder aus, aber der Server sagt immer wieder, dass es sich um ein Datenbankverbindungsproblem handelt. Mein Verdacht ist, dass sie nicht geschlossen wird, aber ich weiß nicht, welche, wenn überhaupt, geschlossen werden soll.

onaclov2000
quelle
Es ist immer eine bewährte Methode, die Verbindungen selbst zu schließen, ohne von anderen Treibern und Vorlagen abhängig zu sein, um das Schließen zu handhaben. Wenn die Verbindung nicht geschlossen wird, werden die Sockets und Ressourcen bis zu einem Absturz (kein Ressourcenszenario mehr) oder einem Neustart für immer geöffnet.
Arun Joshla

Antworten:

196

Wenn Sie mit der Verwendung von your fertig sind Connection, müssen Sie es explizit schließen, indem Sie seine close()Methode aufrufen , um andere Datenbankressourcen (Cursor, Handles usw.) freizugeben, an denen die Verbindung möglicherweise festhält.

Eigentlich ist die sichere Muster in Java zu schließen Ihr ResultSet, Statementund Connection(in dieser Reihenfolge) in einem finallyBlock , wenn Sie mit ihnen gemacht, so etwas wie das:

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

Der finallyBlock kann leicht verbessert werden (um die Nullprüfung zu vermeiden):

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

Dies ist jedoch äußerst ausführlich, sodass Sie im Allgemeinen eine Hilfsklasse verwenden, um die Objekte in null-sicheren Hilfsmethoden zu schließen, und der finallyBlock wird ungefähr so:

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

Und tatsächlich hat das Apache Commons DbUtils eine DbUtilsKlasse, die genau das tut, so dass Sie keine eigene schreiben müssen.

Pascal Thivent
quelle
3
Super Hilfe, danke! Ich habe die conn! = Null-Anweisungen nicht verstanden oder darüber nachgedacht.
Onaclov2000
1
@ onaclov2000 Ja rs, ps, connkann nullje nachdem , wo der Code bricht. Deshalb wird dies als "sicheres" Muster bezeichnet.
Pascal Thivent
12
@Pascal Thivent: Eigentlich müssen wir nicht alle schließen. Das Buch "Core Java Volume 2 - Erweiterte Funktionen" hat geschrieben: Die closeMethode eines StatementObjekts schließt die zugehörige automatisch, ResultSetwenn die Anweisung eine offene Ergebnismenge enthält. In ähnlicher Weise das closeVerfahren der Connectionschließt Klasse alle Statementsvon den Connection.
Majid Azimi
12
@Majid: Es sei denn, es handelt sich um eine Poolverbindung. Die Aussagen würden dann weglaufen.
BalusC
1
@ BalusC: Können Sie bitte erklären, was passiert, wenn eine gepoolte Verbindung mit der Methode connection.close () geschlossen wird
Krsna Chaitanya
61

Es ist immer besser, die Datenbank- / Ressourcenobjekte nach der Verwendung zu schließen. Schließen Sie besser Verbindungs-, Ergebnismengen- und Anweisungsobjekte im finallyBlock.

Bis Java7 müssen alle diese Ressourcen mit einem finallyBlock geschlossen werden . Wenn Sie Java 7 verwenden, können Sie die Ressourcen wie folgt schließen.

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

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

Jetzt werden con-, stmt- und rs-Objekte Teil des try-Blocks, und Java schließt diese Ressourcen nach der Verwendung automatisch.

Hoffe ich war hilfreich.

Yadu Krishnan
quelle
Was ist, wenn meine Aussage implizit ist, dh ResultSet rs = conn.createStatement().executeQuery(sql);innerhalb des tryBlocks?
Antares42
1
Sie können sie im finally {} -Block zum Schließen nicht referenzieren. Wenn eine Ausnahme ausgelöst wird, wird die close () -Methode des ResultSet niemals aufgerufen
Dan
Was passiert, wenn ich sie nicht schließe?
Alex78191
Wenn Sie sie nicht schließen, können Speicherlecks auftreten.
Yadu Krishnan
14

Es reicht aus, nur Statementund zu schließen Connection. Das ResultSetObjekt muss nicht explizit geschlossen werden.

In der Java-Dokumentation heißt es über java.sql.ResultSet:

Ein ResultSet-Objekt wird automatisch von dem Statement-Objekt geschlossen, das es generiert hat, wenn dieses Statement-Objekt geschlossen, erneut ausgeführt oder zum Abrufen des nächsten Ergebnisses aus einer Folge mehrerer Ergebnisse verwendet wird.


Vielen Dank an BalusC für die Kommentare: " Darauf würde ich mich nicht verlassen. Einige JDBC-Treiber scheitern daran."

Grigori A.
quelle
25
Darauf würde ich mich nicht verlassen. Einige JDBC-Treiber schlagen dabei fehl. ZB Oracle mit "Maximale Anzahl offener Cursor überschritten" usw. Schließen Sie einfach alle geöffneten Ressourcen explizit, keine Ausreden.
BalusC
1
Ich würde lieber keine Treiber verwenden, die dann nicht den Spezifikationen entsprechen
Enerccio
2
Wie BalusC betont, ist es eine gute defensive Programmierung, die Verbindung explizit zu schließen, anstatt eine Abhängigkeit von einem bestimmten Anbieter fest zu verdrahten.
Michaelok
11

Ja. Sie müssen die Ergebnismenge, die Anweisung und die Verbindung schließen. Wenn die Verbindung aus einem Pool stammt und durch Schließen geschlossen wird, wird sie zur Wiederverwendung an den Pool zurückgesendet.

Normalerweise müssen Sie dies in einem finally{}Block tun , sodass Sie beim Auslösen einer Ausnahme immer noch die Möglichkeit haben, diese zu schließen.

Viele Frameworks kümmern sich für Sie um dieses Problem der Ressourcenzuweisung / -freigabe. zB Spring's JdbcTemplate . Apache DbUtils verfügt über Methoden, um das Schließen der Ergebnismenge / Anweisung / Verbindung zu überprüfen, ob null oder nicht (und Ausnahmen beim Schließen abzufangen), was ebenfalls hilfreich sein kann.

Brian Agnew
quelle
1
Wenn ich eine "endgültige" Sonnenfinsternis einfüge, hebe ich sie gerne hervor und sage mir, dass sie falsch ist. sollte dies nach den Fangblöcken gehen?
Onaclov2000
Ja. versuche {} catch {} finally {}. Der Fang {} ist übrigens optional. Genau wie der endlich {}
Brian Agnew
Ich habe die "close" -Anweisungen auf "final" verschoben, aber sie sagen nur "sqlexception", irgendwelche Vorschläge?
Onaclov2000
1
close () löst eine SQLException aus. Damit muss man umgehen. Weitere Informationen finden Sie unter DbUtils.closeQuietly ().
Brian Agnew
> Was passiert wirklich, wenn conn.close () nicht auftritt?
Alex78191
8

Eigentlich ist es am besten, wenn Sie einen Try-with-Resources-Block verwenden und Java alle Verbindungen für Sie schließt, wenn Sie den Try-Block verlassen.

Sie sollten dies mit jedem Objekt tun, das AutoClosable implementiert.

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

Der Aufruf von getDatabaseConnection ist nur erfunden. Ersetzen Sie es durch einen Aufruf, mit dem Sie eine JDBC-SQL-Verbindung oder eine Verbindung aus einem Pool erhalten.

Joe
quelle
Sie müssen die Verbindung in diesem Fall also nicht manuell schließen?
Colin D
1
Richtig. Sie müssen die Verbindung nicht explizit schließen. Es wird geschlossen, wenn das Ende des Try-Code-Blocks erreicht ist.
Joe
7

Ja, Sie müssen die Verbindung schließen. Andernfalls hält der Datenbankclient normalerweise die Socket-Verbindung und andere Ressourcen offen.

Alex Miller
quelle
... bis es endet. Dadurch werden verschiedene endliche Ressourcen auf Client- und Serverseite gebunden. Wenn ein Client so etwas zu oft tut, kann dies Probleme für den Client selbst, den Datenbankdienst und möglicherweise sogar für andere Anwendungen verursachen, die auf einem Client- oder Servercomputer ausgeführt werden.
Stephen C