Возврат ResultSet
75149 просмотра
6 ответа
Я пытаюсь создать метод, из которого я могу запросить свою базу данных и получить всю таблицу.
В настоящее время все работает нормально, если я использую данные внутри метода. Однако я хочу, чтобы метод возвращал результаты.
Я получаю java.sql.SQLException: Operation not allowed after ResultSet closed
на текущий код.
Как мне этого добиться?
public ResultSet select() {
con = null;
st = null;
rs = null;
try {
con = DriverManager.getConnection(url, user, password);
st = con.createStatement();
rs = st.executeQuery("SELECT * FROM biler");
/*
if (rs.next()) {
System.out.println(rs.getString("model"));
}*/
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(MySQL.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
} finally {
try {
if (rs != null) {
rs.close();
}
if (st != null) {
st.close();
}
if (con != null) {
con.close();
}
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(MySQL.class.getName());
lgr.log(Level.WARNING, ex.getMessage(), ex);
}
}
return rs;
}
Автор: Patrick Reck
Источник
Размещён: 12.11.2019 09:34
Ответы (6)
51 плюса
Вы никогда не должны проходить ResultSet
через публичные методы. Это может привести к утечке ресурсов, потому что вы вынуждены держать оператор и соединение открытым. Закрытие их неявно закроет набор результатов. Но если они останутся открытыми, это приведет к тому, что они будут болтаться, а в БД не хватит ресурсов, когда их будет слишком много.
Сопоставьте его с коллекцией Javabeans, например, так и верните вместо этого:
public List<Biler> list() throws SQLException {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
List<Biler> bilers = new ArrayList<Biler>();
try {
connection = database.getConnection();
statement = connection.prepareStatement("SELECT id, name, value FROM Biler");
resultSet = statement.executeQuery();
while (resultSet.next()) {
Biler biler = new Biler();
biler.setId(resultSet.getLong("id"));
biler.setName(resultSet.getString("name"));
biler.setValue(resultSet.getInt("value"));
bilers.add(biler);
}
} finally {
if (resultSet != null) try { resultSet.close(); } catch (SQLException ignore) {}
if (statement != null) try { statement.close(); } catch (SQLException ignore) {}
if (connection != null) try { connection.close(); } catch (SQLException ignore) {}
}
return bilers;
}
Или, если вы уже используете Java 7, просто используйте оператор try-with-resources, который автоматически закроет эти ресурсы:
public List<Biler> list() throws SQLException {
List<Biler> bilers = new ArrayList<Biler>();
try (
Connection connection = database.getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT id, name, value FROM Biler");
ResultSet resultSet = statement.executeQuery();
) {
while (resultSet.next()) {
Biler biler = new Biler();
biler.setId(resultSet.getLong("id"));
biler.setName(resultSet.getString("name"));
biler.setValue(resultSet.getInt("value"));
bilers.add(biler);
}
}
return bilers;
}
Кстати, вы не должны быть декларантом Connection
, Statement
и ResultSet
переменные экземпляра на всех (главная проблема threadsafety!), Ни быть проглатывания SQLException
в тот момент вообще (абонент не будет иметь ни малейшего представления о том , что проблема произошла), ни быть закрытия ресурсы в том же try
(если, например, закрытие результирующего набора выдает исключение, то оператор и соединение все еще открыты). Все эти проблемы исправлены в приведенных выше фрагментах кода.
14 плюса
Если вы не знаете, что вам нужно от ResultSet при получении времени, я предлагаю сопоставить все это на карте следующим образом:
List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>();
Map<String, Object> row = null;
ResultSetMetaData metaData = rs.getMetaData();
Integer columnCount = metaData.getColumnCount();
while (rs.next()) {
row = new HashMap<String, Object>();
for (int i = 1; i <= columnCount; i++) {
row.put(metaData.getColumnName(i), rs.getObject(i));
}
resultList.add(row);
}
Таким образом, в основном у вас есть то же самое, что и ResultSet (без ResultSetMetaData).
Автор: Manuel Eder Размещён: 13.02.2013 12:597 плюса
Ну, вы делаете вызов rs.close()
в вашем finally
-блоке.
В принципе, это хорошая идея, так как вы должны закрыть все свои ресурсы (соединения, операторы, наборы результатов, ...).
Но вы должны закрыть их после их использования.
Есть как минимум три возможных решения:
не закрывайте набор результатов (и соединение, ...) и не требуйте, чтобы вызывающая сторона вызывала отдельный метод «close».
По сути, это означает, что теперь вызывающему абоненту необходимо помнить, чтобы он был близким, и это не облегчает задачу.
пусть вызывающая сторона передает класс, который получает набор результатов, и вызывает его в вашем методе
Это работает, но может стать немного многословным, так как вам понадобится подкласс некоторого интерфейса (возможно, как анонимный внутренний класс) для каждого блока кода, который вы хотите выполнить в наборе результатов.
Интерфейс выглядел так:
public interface ResultSetConsumer<T> { public T consume(ResultSet rs); }
и ваш
select
метод выглядел так:public <T> List<T> select(String query, ResultSetConsumer<T> consumer) { Connection con = null; Statement st = null; ResultSet rs = null; try { con = DriverManager.getConnection(url, user, password); st = con.createStatement(); rs = st.executeQuery(query); List<T> result = new ArrayList<T>(); while (rs.next()) { result.add(consumer.consume(rs)); } } catch (SQLException ex) { // logging } finally { try { if (rs != null) { rs.close(); } if (st != null) { st.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(MySQL.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); } } return rs; }
выполнить всю работу внутри
select
метода и вернуть частьList
в результате.Это, вероятно, наиболее широко используемый метод: переберите набор результатов и преобразуйте данные в пользовательские данные в ваших собственных DTO и возвращайте их .
4 плюса
Как все до меня говорили, плохая идея передать набор результатов. Если вы используете библиотеку пула соединений, например c3p0, вы можете безопасно использовать CachedRowSet и его реализацию CachedRowSetImpl . Используя это, вы можете закрыть соединение. Он будет использовать соединение только при необходимости. Вот фрагмент из документа Java:
Объект CachedRowSet - это отключенный набор строк, что означает, что он использует соединение с источником данных только на короткое время. Он подключается к своему источнику данных во время чтения данных, чтобы заполнить себя строками, и снова во время передачи изменений обратно в свой базовый источник данных. В остальное время объект CachedRowSet отключается, в том числе во время изменения его данных. Отключение делает объект RowSet намного более компактным и, следовательно, намного легче передать другому компоненту. Например, отключенный объект RowSet может быть сериализован и передан по проводной связи тонкому клиенту, такому как персональный цифровой помощник (PDA).
Вот фрагмент кода для запроса и возврата ResultSet:
public ResultSet getContent(String queryStr) {
Connection conn = null;
Statement stmt = null;
ResultSet resultSet = null;
CachedRowSetImpl crs = null;
try {
Connection conn = dataSource.getConnection();
stmt = conn.createStatement();
resultSet = stmt.executeQuery(queryStr);
crs = new CachedRowSetImpl();
crs.populate(resultSet);
} catch (SQLException e) {
throw new IllegalStateException("Unable to execute query: " + queryStr, e);
}finally {
try {
if (resultSet != null) {
resultSet.close();
}
if (stmt != null) {
stmt.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
LOGGER.error("Ignored", e);
}
}
return crs;
}
Вот фрагмент для создания источника данных с использованием c3p0:
ComboPooledDataSource cpds = new ComboPooledDataSource();
try {
cpds.setDriverClass("<driver class>"); //loads the jdbc driver
} catch (PropertyVetoException e) {
e.printStackTrace();
return;
}
cpds.setJdbcUrl("jdbc:<url>");
cpds.setMinPoolSize(5);
cpds.setAcquireIncrement(5);
cpds.setMaxPoolSize(20);
javax.sql.DataSource dataSource = cpds;
Автор: havexz
Размещён: 21.06.2014 06:20
3 плюса
Вы можете использовать объект CachedRowSet только для того, что вы хотите:
public CachedRowSetImpl select(String url, String user, String password) {
CachedRowSetImpl crs = null;
try (Connection con = DriverManager.getConnection(url, user, password);
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM biler");) {
crs = new CachedRowSetImpl();
crs.populate(rs);
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(MySQL.class.getName());
lgr.log(Level.SEVERE, ex.getMessage(), ex);
} catch (SQLException ex) {
Logger lgr = Logger.getLogger(MySQL.class.getName());
lgr.log(Level.WARNING, ex.getMessage(), ex);
}
return crs;
}
Вы можете прочитать документацию здесь: https://docs.oracle.com/javase/7/docs/api/javax/sql/rowset/CachedRowSet.html
Автор: César Zea Gómez Размещён: 17.03.2018 03:521 плюс
Вы закрываете ResultSet
и, следовательно, вы больше не можете его использовать.
Чтобы вернуть содержимое таблицы, вам нужно будет выполнить итерацию ResultSet
и построить представление для каждой строки ( List
возможно, в?). Предположительно, каждая строка представляет какую-то сущность, и я бы создал такую сущность для каждой строки.
while (rs.next()) {
list.add(new Entity(rs));
}
return list;
Альтернативой является предоставление некоторого объекта обратного вызова, и ваша ResultSet
итерация будет вызывать этот объект для каждой ResultSet
строки. Таким образом, вам не нужно создавать объект, представляющий всю таблицу (что может быть проблемой, если она имеет размер)
while (rs.next()) {
client.processResultSet(rs);
}
Я бы не хотел, чтобы клиенты закрывали набор результатов / утверждение / соединение. Этим нужно управлять осторожно, чтобы избежать утечек ресурсов, и вам гораздо лучше справиться с этим в одном месте (желательно рядом с тем местом, где вы их открываете!).
Примечание: вы можете использовать Apache Commons DbUtils.closeQuietly (), чтобы просто и надежно закрыть кортеж connect / Statement / resultset ( правильно обрабатывая нули и исключения)
Автор: Brian Agnew Размещён: 13.02.2013 12:25Вопросы из категории :
- java В чем разница между int и Integer в Java и C #?
- java Как я могу определить IP моего маршрутизатора / шлюза в Java?
- java Каков наилучший способ проверки XML-файла по сравнению с XSD-файлом?
- java Как округлить результат целочисленного деления?
- java Преобразование списка <Integer> в список <String>
- java Почему я не могу объявить статические методы в интерфейсе?
- mysql Двоичные данные в MySQL
- mysql Насколько большой может быть база данных MySQL до того, как производительность начнет снижаться
- mysql Выбрать все столбцы, кроме одного в MySQL?
- mysql MySQL или PDO - каковы плюсы и минусы?
- mysql Как выбрать n-ую строку в таблице базы данных SQL?
- mysql MyISAM против InnoDB
- jdbc Как избежать хранения паролей в открытом виде для tomcat server.xml Определение ресурса DataSource?
- jdbc Могу ли я подключиться к SQL Server с помощью аутентификации Windows из веб-приложения Java EE?
- jdbc Как я могу получить размер java.sql.ResultSet?
- jdbc Как выполнить модульное тестирование кода JDBC в Java?
- jdbc Как работает Java PreparedStatement?
- jdbc Параметры пула соединений с JDBC: DBCP против C3P0