У меня есть метод для получения пользователей из базы данных с JDBC:
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> users = new ArrayList<User>();
try {
Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = con.prepareStatement(sql);
ps.setInt(1, userId);
ResultSet rs = ps.executeQuery();
while(rs.next()) {
users.add(new User(rs.getInt("id"), rs.getString("name")));
}
rs.close();
ps.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
Как мне использовать Java 7 try-with-resources для улучшения этого кода?
Я пробовал с приведенным ниже кодом, но он использует много try
блоков и не сильно улучшает читаемость . Должен ли я использовать try-with-resources
по-другому?
public List<User> getUser(int userId) {
String sql = "SELECT id, name FROM users WHERE id = ?";
List<User> users = new ArrayList<>();
try {
try (Connection con = DriverManager.getConnection(myConnectionURL);
PreparedStatement ps = con.prepareStatement(sql);) {
ps.setInt(1, userId);
try (ResultSet rs = ps.executeQuery();) {
while(rs.next()) {
users.add(new User(rs.getInt("id"), rs.getString("name")));
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
java
jdbc
java-7
try-with-resources
Jonas
источник
источник
try (ResultSet rs = ps.executeQuery()) {
потому что объект ResultSet автоматически закрывается объектом Statement, который его сгенерировалConnection
,PreparedStatement
иResultSet
тоже. На самом деле нет причин не делать этого, поскольку попытка использования ресурсов делает это настолько простым и делает наш код более самодокументируемым в соответствии с нашими намерениями.Ответы:
В вашем примере нет необходимости во внешней попытке, так что вы можете, по крайней мере, опуститься с 3 до 2, а также вам не нужно закрывать
;
в конце списка ресурсов. Преимущество использования двух блоков try состоит в том, что весь ваш код представлен заранее, поэтому вам не нужно ссылаться на отдельный метод:источник
Connection::setAutoCommit
? Такой вызов не допускается в пределахtry
междуcon =
иps =
. При получении соединения из источника данных, который может быть поддержан пулом соединений, мы не можем предположить, как установлен autoCommit.DriverManager.getConnection(myConnectionURL)
в метод, который также устанавливает флаг autoCommit и возвращает соединение (или устанавливает его в эквивалентеcreatePreparedStatement
метода в предыдущем примере ...)DataSource
гдеgetConnection
метод делает, как вы говорите, получите соединение и настройте его по мере необходимости, затем передавая соединение.Я понимаю, что на это уже давно дан ответ, но хочу предложить дополнительный подход, позволяющий избежать вложенного двойного блока try-with-resources.
источник
createPreparedStatement
это небезопасно, независимо от того, как вы его используете. Чтобы исправить это, вам нужно будет добавить try-catch вокруг setInt (...), перехватить любойSQLException
, и когда это произойдет, вызовите ps.close () и повторно сгенерируйте исключение. Но это привело бы к тому, что код стал бы почти таким же длинным и нелегким, как код, который ОП хотел улучшить.Вот краткий способ использования lambdas и JDK 8 Supplier, чтобы все соответствовало внешним требованиям:
источник
Как насчет создания дополнительного класса оболочки?
Затем в вызывающем классе вы можете реализовать метод prepareStatement как:
источник
Как уже говорили другие, ваш код в основном правильный, хотя внешний
try
не нужен. Вот еще несколько мыслей.DataSource
Другие ответы здесь правильные и хорошие, такие как принятый Ответ от bpgergo. Но ни один из них не демонстрирует использование
DataSource
, обычно рекомендуемое по сравнению с использованиемDriverManager
в современной Java.Итак, ради полноты, вот полный пример, который выбирает текущую дату с сервера базы данных. Используемая здесь база данных - Postgres . Любая другая база данных будет работать аналогично. Вы бы заменили использование
org.postgresql.ds.PGSimpleDataSource
с реализациейDataSource
соответствующую вашей базе данных. Реализация, вероятно, обеспечивается вашим конкретным драйвером или пулом соединений, если вы идете по этому пути.DataSource
Реализации нужны не быть закрыты, потому что она никогда не «открыто». ADataSource
не является ресурсом, не подключен к базе данных, поэтому он не содержит сетевых подключений и ресурсов на сервере базы данных. ADataSource
- это просто информация, необходимая при установлении соединения с базой данных, с сетевым именем или адресом сервера базы данных, именем пользователя, паролем пользователя и различными параметрами, которые вы хотите указать, когда соединение в конце концов устанавливается. Таким образом, вашDataSource
объект реализации не входит в скобки try-with-resources.Вложенная попытка с ресурсами
Ваш код правильно использует вложенные операторы try-with-resources.
Обратите внимание, что в приведенном ниже примере кода мы также используем синтаксис try-with-resources дважды , один вложен в другой. Внешний
try
определяет два ресурса:Connection
иPreparedStatement
. Внутреннееtry
определяетResultSet
ресурс. Это общая структура кода.Если исключение выдается из внутреннего и не перехватывается там,
ResultSet
ресурс автоматически закрывается (если он существует, не ноль). После этогоPreparedStatement
будет закрыто, и, наконец,Connection
закрыто. Ресурсы автоматически закрываются в обратном порядке, в котором они были объявлены в инструкциях try-with-resource.Пример кода здесь слишком упрощен. Как написано, это может быть выполнено с одним оператором try-with-resources. Но в реальной работе вы, вероятно, будете выполнять больше работы между вложенными парами
try
вызовов. Например, вы можете извлекать значения из вашего пользовательского интерфейса или POJO, а затем передавать их для выполнения?
заполнителей в вашем SQL через вызовыPreparedStatement::set…
методов.Синтаксические заметки
Точка с запятой
Обратите внимание, что точка с запятой, завершающая последний оператор ресурса в скобках try-with-resources, является необязательной. Я включил его в свою собственную работу по двум причинам: согласованность, и она выглядит завершенной, и это облегчает вставку копий в сочетание строк, не беспокоясь о точках с запятой в конце строки. Ваша IDE может пометить последнюю точку с запятой как лишнюю, но не оставляя вреда.
Java 9 - использовать существующие переменные в try-with-resources
Новое в Java 9 - это улучшение синтаксиса проб с ресурсами. Теперь мы можем объявить и заполнить ресурсы за пределами скобок
try
оператора. Я еще не нашел это полезным для ресурсов JDBC, но помните об этом в своей собственной работе.ResultSet
должен закрыть себя, но не можетВ идеальном мире
ResultSet
он закрывается, как обещает документация:К сожалению, в прошлом некоторые драйверы JDBC печально не выполняли это обещание. В результате, многие программисты JDBC научились явно закрыть все свои ресурсы , включая JDBC
Connection
,PreparedStatement
иResultSet
тоже. Современный синтаксис try-with-resources сделал это проще и с более компактным кодом. Обратите внимание, что команда Java пошла на то, чтобы пометитьResultSet
какAutoCloseable
, и я предлагаю использовать это. Использование try-with-resources во всех ваших ресурсах JDBC делает ваш код более самодокументируемым в соответствии с вашими намерениями.Пример кода
источник