Закрытие подключений к базе данных в Java

121

Я немного запутался, я читал ниже 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();
}

Не нужно закрывать conn Connection? Что на самом деле происходит, если conn.close () не выполняется?

У меня есть частное веб-приложение, которое я поддерживаю, которое в настоящее время не закрывает ни одну из форм, но действительно ли важным является stmt, conn или оба?

Сайт периодически отключается, но сервер продолжает говорить, что это проблема с подключением к базе данных, я подозреваю, что он не закрывается, но я не знаю, какой из них закрыть.

onaclov2000
источник
Всегда рекомендуется закрывать соединения самостоятельно, независимо от других драйверов и шаблонов для обработки закрытия. Неудача при закрытии соединения приведет к тому, что сокеты и ресурсы будут открыты навсегда до сбоя (больше нет сценария ресурсов) или перезапуска.
Арун Джошла

Ответы:

196

Когда вы закончите использовать свой Connection, вам необходимо явно закрыть его, вызвав его close()метод, чтобы освободить любые другие ресурсы базы данных (курсоры, дескрипторы и т. Д.), За которые соединение может держаться.

Фактически, безопасный шаблон в Java - закрыть ваши ResultSet, Statementи Connection(в этом порядке) в finallyблоке, когда вы закончите с ними, что-то вроде этого:

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

finallyБлок может быть немного улучшен в (чтобы избежать проверок нуля):

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

Но, тем не менее, это чрезвычайно многословно, поэтому вы обычно используете вспомогательный класс для закрытия объектов в нулевых вспомогательных методах, и finallyблок становится примерно таким:

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

На самом деле в Apache Commons DbUtils есть DbUtilsкласс, который именно этим занимается, поэтому нет необходимости писать свой собственный.

Паскаль Тивент
источник
3
Отличная помощь, спасибо! Я не уловил и не подумал об утверждениях conn! = Null.
onaclov2000
1
@ onaclov2000 Да rs,ps , connможет быть в nullзависимости от того, где разрывы кода. Вот почему это известно как «безопасный» шаблон.
Паскаль Тивент
12
@Pascal Thivent: На самом деле нам не нужно закрывать их все. В книге "Core Java Volume two - Advanced Features" написано: closeМетод Statementобъекта автоматически закрывает связанныйResultSet если у оператора есть открытый набор результатов. Аналогичным образом , closeметод Connectionкласса закрывает все Statementsиз Connection.
Маджид Азими
12
@Majid: Если только это не объединенное соединение. Заявления тогда утекут.
BalusC 05
1
@BalusC: Не могли бы вы объяснить, что происходит, когда объединенное соединение закрывается с помощью метода connection.close ()
Кришна Чайтанья
61

Всегда лучше закрыть объекты базы данных / ресурсов после использования. Лучше закрыть объекты соединения, набора результатов и операторов в finallyблоке.

До Java7 все эти ресурсы нужно было закрывать с помощью finallyблока. Если вы используете Java 7, то для закрытия ресурсов вы можете сделать следующее.

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

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

Теперь объекты con, stmt и rs становятся частью блока try, и java автоматически закрывает эти ресурсы после использования.

Надеюсь, я был вам полезен.

Яду Кришнан
источник
Что делать, если мое утверждение неявно, т.е. ResultSet rs = conn.createStatement().executeQuery(sql);внутри tryблока?
Antares42
1
Вы не сможете ссылаться на них в блоке finally {} для закрытия. Если выбрасывается исключение, метод close () ResultSet никогда не будет вызван
Дэн
Что будет, если я их не закрою?
Alex78191
если их не закрыть, могут возникнуть утечки памяти.
Яду Кришнан
14

Достаточно просто закрыть Statementи Connection. Нет необходимости явно закрыватьResultSet объект.

Документация Java говорит о java.sql.ResultSet:

Объект ResultSet автоматически закрывается объектом Statement, который его сгенерировал, когда этот объект Statement закрывается, повторно выполняется или используется для получения следующего результата из последовательности нескольких результатов.


Спасибо BalusC за комментарии: «Я бы не стал на это полагаться. Некоторые драйверы JDBC терпят неудачу».

Григорий А.
источник
25
Я бы не стал на это полагаться. Некоторые драйверы JDBC терпят неудачу. Например, Oracle с «Превышено максимальное количество открытых курсоров» и т. Д. Просто явно закройте все открытые ресурсы, никаких отговорок.
BalusC 05
1
Тогда я бы предпочел не использовать драйверы, которые не соответствуют спецификациям
Enerccio
2
Как указывает BalusC, это хорошее защитное программирование - явное закрытие соединения вместо жесткой привязки зависимости от конкретного провайдера.
michaelok
11

Да. Вам нужно закрыть набор результатов, оператор и соединение. Если соединение пришло из пула, его закрытие фактически отправляет его обратно в пул для повторного использования.

Обычно это нужно делать в finally{} блоке, так что если возникает исключение, у вас все еще есть возможность закрыть его.

Многие фреймворки позаботятся об этой проблеме выделения / освобождения ресурсов за вас. например, Весна JdbcTemplate . В Apache DbUtils есть методы, которые следят за закрытием набора результатов / оператора / соединения, равно нулю или нет (и перехватывая исключения при закрытии), что также может помочь.

Брайан Агнью
источник
1
Когда я вставляю «наконец» затмение, мне нравится выделять его, говоря мне, что это неправильно. это должно идти после блоков catch?
onaclov2000
Да. попробуйте {} поймать {} {} , наконец. Уловка {} не обязательна, кстати. Точно так же, как и finally {}
Брайан Агнью
Я переместил операторы «close» в «finally», но они просто говорят «sqlexception», какие-нибудь предложения?
onaclov2000
1
close () выдает исключение SQLException. Вы должны с этим справиться. См. DbUtils.closeQuietly (), чтобы справиться с этим незаметно.
Брайан Агнью
> Что на самом деле происходит, если conn.close () не выполняется?
Alex78191
8

На самом деле, лучше всего использовать блок try-with-resources, и Java закроет для вас все соединения, когда вы выйдете из блока try.

Вы должны сделать это с любым объектом, который реализует 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.");
}

Вызов getDatabaseConnection только что создан. Замените его вызовом, который дает вам соединение JDBC SQL или соединение из пула.

Джо
источник
Значит, в этом случае не нужно закрывать соединение вручную?
Colin D
1
Верный. Вам не нужно явно закрывать соединение. Он будет закрыт, когда будет достигнут конец блока кода попытки.
Джо
7

Да, вам нужно закрыть соединение. В противном случае клиент базы данных обычно будет поддерживать соединение сокета и другие ресурсы открытыми.

Алекс Миллер
источник
... пока он не выйдет. Это связывает различные ограниченные ресурсы на стороне клиента и сервера. Если клиент делает такие вещи слишком много, это может вызвать проблемы для самого клиента, службы базы данных и, возможно, даже для других приложений, работающих на клиентском или серверном компьютере.
Stephen C