Закрытие соединений JDBC в пуле

109

Наш стандартный раздел кода для использования JDBC ...

Connection conn = getConnection(...);
Statement  stmt = conn.conn.createStatement (ResultSet.TYPE_SCROLL_INSENSITIVE,
                                                ResultSet.CONCUR_READ_ONLY);
ResultSet  rset = stmt.executeQuery (sqlQuery);

// do stuff with rset

rset.close(); stmt.close(); conn.close();

Вопрос 1: следует ли при использовании пула подключений закрывать подключение в конце? Если да, то не потеряна ли цель объединения? А если нет, то как DataSource узнает, что конкретный экземпляр Connection освобожден и может быть использован повторно? Я немного запутался в этом, любые указатели оценены.

Вопрос 2: близок ли следующий метод к стандартному? Похоже на попытку получить соединение из пула, и если DataSource не может быть установлен, используйте старомодный DriverManager. Мы даже не уверены, какая часть выполняется во время выполнения. Повторяя вопрос выше, следует ли закрывать соединение, выходящее из такого метода?

Спасибо, - ср.

synchronized public Connection getConnection (boolean pooledConnection)
                                                        throws SQLException {
        if (pooledConnection) {
                if (ds == null) {
                        try {
                                Context envCtx = (Context)
                                        new InitialContext().lookup("java:comp/env");
                                ds = (DataSource) envCtx.lookup("jdbc/NamedInTomcat");
                                return ds.getConnection();
                        } catch (NamingException e) {
                                e.printStackTrace();
                }}
                return (ds == null) ? getConnection (false) : ds.getConnection();
        }
        return DriverManager.getConnection(
                "jdbc:mysql://"+ipaddy+":"+dbPort +"/" + dbName, uName, pWord);
}

Изменить: я думаю, что мы получаем объединенное соединение, так как мы не видим трассировку стека.

Манидип Сенгупта
источник

Ответы:

121

При использовании пула соединений следует ли закрывать соединение в конце? Если да, то не потеряна ли цель объединения? А если нет, то как DataSource узнает, что конкретный экземпляр Connection освобожден и может быть использован повторно? Я немного запутался в этом, любые указатели оценены.

Да, конечно, вам также необходимо закрыть объединенное соединение. На самом деле это оболочка вокруг фактического соединения. Под покровом он вернет фактическое соединение обратно в пул. Это дополнительно к бассейну , чтобы решить , будет ли фактическое соединение фактически быть закрытым или повторно использовать для нового getConnection()вызова. Итак, независимо от того, используете ли вы пул соединений или нет, вы всегда должны закрывать все ресурсы JDBC в обратном порядке в finallyблоке tryблока, в котором вы их приобрели. В Java 7 это можно еще больше упростить, используя try-with-resourcesоператор.


Близок ли следующий метод к стандартному? Похоже на попытку получить соединение из пула, и если DataSource не может быть установлен, используйте старомодный DriverManager. Мы даже не уверены, какая часть выполняется во время выполнения. Повторяя вопрос выше, следует ли закрывать соединение, выходящее из такого метода?

Пример довольно пугающий. Вам просто нужно найти / инициализировать DataSourceтолько один раз во время запуска приложения в некотором конструкторе / инициализации класса конфигурации БД в масштабе всего приложения. Затем просто вызывайте getConnection()один и тот же источник данных на протяжении всего остального времени существования приложения. Нет необходимости ни в синхронизации, ни в проверках нуля.

Смотрите также:

BalusC
источник
Вот что он делает (инициализируется один раз), не так ли? ds - это переменная экземпляра, а if (ds == null) ... - часть инициализации.
Манидип Сенгупта
Странно проводить проверки каждый раз в get-методе getConnection(). Просто сделайте это в c'tor или блоке инициализации того же класса, без синхронизации / нулевых проверок. Он будет вызван только один раз. Для получения дополнительных подсказок и примеров начала, эта статья может оказаться полезной.
BalusC
Отличная статья, BalusC. Класс, с которым я имею дело, в значительной степени реализует уровень данных, используя DTO. Я согласен с вами, инициализация должна быть в конструкторе. Теперь у этого класса есть масса методов, каждый с conn, stmt и rset в качестве локальных переменных, соединения находятся в блоке try, и, наконец, есть однострочный вызов csrClose (conn, stmt, rset), где все 3 закрываются (в обратном порядке). Теперь DTO, которое вы разрабатываете в примере, является зеркальным отображением строки таблицы БД. У нас есть сложные SQL-запросы с объединениями (и другими предложениями). У вас есть статья о том, как разрабатывать DAO для таких результатов?
Манидип Сенгупта
2
@yat: Вы ДОЛЖНЫ вызывать их close()всех в finallyблоке того же tryблока, в котором вы их приобрели / создали. Это полностью независимо от того, объединенное ли это соединение или нет.
BalusC 06
1
@iJava: этот пул написан любителем, не имеющим полного представления о том, что он делает. Не обращайте на это внимания и ищите настоящую библиотеку. Например, HikariCP.
BalusC
22

Пулы обычно возвращают вам завернутый объект Connection, в котором метод close () переопределяется, обычно возвращая соединение в пул. Вызов close () в порядке и, вероятно, все еще требуется.

Метод close (), вероятно, будет выглядеть так:

public void close() throws SQLException {
  pool.returnConnection(this);
}

Что касается вашего второго вопроса, вы можете добавить регистратор, чтобы показать, запускается ли когда-либо нижний блок. Я предполагаю, что вам нужен только тот или иной способ конфигурации соединений с базой данных. Мы используем только пул для доступа к нашей базе данных. В любом случае, закрытие соединения было бы очень важным для предотвращения утечек.

TAER
источник
Я согласен, у нас есть регистратор, и его тоже можно использовать. Мне нужно немного изучить, как вы можете обернуть объект, переопределить его метод close (), но при этом сохранить то же имя класса (Connection)
Манидип Сенгупта
1
Calling close() is OK and probably still required., отсутствие вызова close приведет к утечке соединения, если пул не реализует какую-либо стратегию восстановления
svarog
0

На самом деле, лучший подход к управлению соединениями - не передавать их в какой-либо код.

Создайте класс SQLExecutor, который является единственным местом, которое открывает и закрывает соединения.

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

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

Удобно, что это также позволяет вам регистрировать весь ваш SQL из единого набора кода.

Родни П. Барбати
источник