Выполнение нескольких запросов с использованием одного объекта Statement JDBC
Прочтите, прежде чем отмечать это как дубликат. Я много искал у Google и SO, но я не мог точно ответить на этот вопрос.
Мой вопрос: В JDBC я могу использовать один объект Statement
для вызова executeQuery("")
несколько раз? Это безопасно? ИЛИ следует закрыть объект оператора после каждого запроса и создать новый объект для выполнения другого запроса.
например:
Connection con;
Statement s;
ResultSet rs;
ResultSet rs2;
try
{
con = getConnection();
s = con.prepareStatement();
try
{
rs = s.executeQuery(".......................");
// process the result set rs
}
finally
{
close(rs);
}
// I know what to do to rs here
// But I am asking, should I close the Statement s here? Or can I use it again for the next query?
try
{
rs2 = s.executeQuery(".......................");
// process the result set rs2
}
finally
{
close(rs2);
}
}
finally
{
close(s);
close(con);
}
Ответы
Ответ 1
Да, вы можете повторно использовать Statement
(в частности, PreparedStatement
) и должны делать это в общем случае с JDBC. Это был бы неэффективный и плохой стиль, если бы вы повторно не использовали ваш оператор и сразу же создали еще один идентичный объект Statement
. Что касается закрытия, было бы целесообразно закрыть его в блоке finally, как и в этом фрагменте.
Пример того, что вы запрашиваете, просмотрите эту ссылку: jOOq Docs
Ответ 2
Я не уверен, почему вы спрашиваете. Конструкция и документация API показывают, что идеально подходит (и даже предназначено) для повторного использования объекта Statement
для нескольких вызовов execute
, executeUpdate
и executeQuery
. Если это не будет разрешено, это будет явно задокументировано в документе Java (и, вероятно, API будет другим).
Кроме того, apidoc Statement
говорит:
Все методы выполнения в интерфейсе Statement
неявно закрывают объект current [sic] current ResultSet
, если существует открытый.
Это указание, что вы можете использовать его несколько раз.
TL; DR: Да, вы можете называть execute
в одиночном Statement
объекте несколько раз, если вы понимаете, что все ранее открытые ResultSet
будут закрыты.
В вашем примере неправильно используется PreparedStatement
, и вы не можете (или: не должны) иметь возможность вызвать любой из методов execute...
, принимающих String
на странице PreparedStatement
:
SQLException
- если [...] метод вызывается на PreparedStatement
или CallableStatement
Но также ответить на PreparedStatement
: вся цель PreparedStatement
состоит в том, чтобы предварительно скомпилировать инструкцию с помощью замедлителей параметров и повторно использовать ее для нескольких исполнений с разными значениями параметров.
Ответ 3
Я не могу найти что-либо в API docs, в котором указано, что вы не должны называть executeQuery()
для данного PreparedStatement
экземпляр более одного раза.
Однако ваш код не закрывает PreparedStatement
- вызов executeQuery()
будет вызывать SQLException
в этом случае, но ResultSet
, который возвращается executeQuery()
. Функция ResultSet автоматически закрывается при повторном выполнении PreparedStatement
. В зависимости от ваших обстоятельств вы должны закрыть его, когда вам это больше не нужно. Я бы закрыл его, потому что я считаю, что это плохой стиль, чтобы не делать этого.
UPDATE Упс, я пропустил ваш комментарий между двумя блоками try. Если вы закроете PreparedStatement в этот момент, вы не сможете снова вызвать executeQuery(), не получив SQLException.
Ответ 4
A Prepared Statement
сообщает базе данных о необходимости помнить ваш запрос и быть готовым принять параметризованные переменные для выполнения в этом запросе. Это очень похоже на хранимую процедуру.
Prepared Statement
выполняет две основные функции:
-
Он автоматически ускользает от ваших переменных запроса, чтобы помочь защитить SQL Injection.
-
Он сообщает базе данных, чтобы запомнить запрос и быть готовым к переносу переменных.
Число 2 важно, потому что это означает, что база данных должна только один раз интерпретировать ваш запрос, а затем у него есть процедура, готовая к работе. Таким образом, он улучшает производительность.
Вы не должны закрывать подготовленный оператор и/или соединение с базой данных между вызовами вызова. Это невероятно эффективно, и это вызовет дополнительные накладные расходы, чем использование простого старого Statement
, поскольку вы каждый раз инструктируете базу данных о создании процедуры и ее запоминании. Даже если база данных настроена для "горячих точек" и всегда сохраняет ваш запрос, даже если вы закрываете PreparedStatement
, вы по-прежнему несете сетевые издержки, а также небольшое время обработки.
Короче говоря, держите Connection
и PreparedStatement
открытыми, пока не закончите с ними.
Изменить: комментировать, не возвращая ResultSet
из выполнения, это нормально. executeQuery
вернет ResultSet
для любого выполняемого запроса.
Ответ 5
Во-первых, я смущен о вашем коде
s = con.prepareStatement();
Хорошо ли работает? Я не могу найти такую функцию в JAVA API, необходим хотя бы один параметр. Может быть, вы хотите вызвать эту функцию.
s = con.createStatement();
Я только что запустил свой код для доступа к DB2 в два раза с одним экземпляром Statement, не закрывая его между двумя операциями. Он работает хорошо. Я думаю, вы тоже можете попробовать.
String sql = "";
String sql2 = "";
String driver = "com.ibm.db2.jcc.DB2Driver";
String url = "jdbc:db2://ip:port/DBNAME";
String user = "user";
String password = "password";
Class.forName(driver).newInstance();
Connection conn = DriverManager.getConnection(url, user, password);
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
int count = 0;
while (resultSet.next()) {
count++;
}
System.out.println("Result row count of query number one is: " + count);
count = 0;
resultSet = statement.executeQuery(sql2);
while (resultSet.next()) {
count++;
}
System.out.println("Result row count of query number two is: " + count);