#java #coding-style
#java #стиль кодирования
Вопрос:
Я работаю над обновлением проекта, и я просто хочу знать лучшие практики по этим двум вопросам:
- Должен ли я продолжать использовать Connections / Statements / ResultSet для выполнения запросов или я должен использовать что-то еще?
- Является ли это правильным способом фиксации / закрытия соединений / Операторов / наборов результатов? Я имею в виду, выполняю ли я фиксацию / закрытие в правильном порядке с блоками try / catch / finally в нужных местах?
Пожалуйста, предоставьте примеры измененного кода этих фрагментов, чтобы прояснить вашу точку зрения.
Фрагмент кода из select
метода типа:
public ArrayList getMethod() {
ArrayList a = new ArrayList();
Connection con = null;
ResultSet rs = null;
Statement stmt = null;
try {
con = BoneCPConnection.getConnectionPool().getConnection(); // get a connection
con.setAutoCommit(true);
stmt = con.createStatement();
String query = "select * from example";
rs = stmt.executeQuery(query);
while(rs.next()) {
System.out.println("Result: " rs.getInt(1));
}
} catch (Exception e) {
System.out.println("Issue with getMethod");
e.printStackTrace();
} finally {
try {
rs.close();
stmt.close();
con.close();
} catch (Exception e) {
con = null;
}
rs = null;
stmt = null;
con = null;
}
return a;
}
Фрагмент кода из update
метода типа
public void updateMethod() {
ArrayList a = new ArrayList();
Connection con = null;
Statement stmt = null;
int updateCount = null;
try {
con = BoneCPConnection.getConnectionPool().getConnection(); // get a connection
stmt = con.createStatement();
String query = "update example set id = 1";
updateCount = stmt.executeUpdate(query);
System.out.println("Result: " updateCount);
} catch (Exception e) {
System.out.println("Issue with updateMethod");
e.printStackTrace();
} finally {
try {
con.commit();
stmt.close();
con.close();
} catch (Exception e) {
con = null;
}
stmt = null;
con = null;
}
}
Ответ №1:
Как минимум, вам, вероятно, следует переключиться на PreparedStatement
инструкции instead of plain . Причина этого в том, что драйвер JDBC в большинстве случаев отправляет инструкцию в базу данных при создании, чтобы ее можно было предварительно скомпилировать. Затем вы можете привязать свои параметры к оператору и выполнить. Помимо преимуществ производительности при предварительной компиляции, вы также получаете небольшую защиту от атак SQL-инъекций, поскольку способ установки параметров более строго типизирован. На сайте Oracle есть хорошее описание подготовленных инструкций.
Если вы используете Spring (или хотите сделать скачок, чтобы добавить его в свою систему), вы можете взглянуть на JdbcTemplate
JdbcDaoSupport
классы and (оба описаны здесь). Основное преимущество заключается в том, что он заботится о коде очистки соединения для вас (поэтому вам не нужно так сильно беспокоиться о пропущенном close
вызове).
Аналогично, если вы добавите Spring в свой проект, вы можете использовать его для настройки своих транзакций (либо с помощью аннотаций, либо с помощью контекстного файла Spring). Это позволит вам извлечь управление транзакциями из фактической реализации и сделать код в вашем Dao немного чище.
Что касается вашей обработки фиксации / закрытия: вы должны переместить свои операторы фиксации из ваших блоков finally в основной путь выполнения. Вы должны сохранить свои операторы close в блоке finally, хотя, поскольку вы хотите, чтобы они происходили, несмотря ни на что.
Примером того, как будет выглядеть ваш код обновления с использованием PreparedStatements, является:
public void updateMethod() {
Connection con = null;
PreparedStatement stmt = null;
int updateCount = null;
try {
con = BoneCPConnection.getConnectionPool().getConnection();
stmt = con.prepareStatement("update example set id = ?");
stmt.setInt(1,1);
updateCount = stmt.executeUpdate(query);
con.commit();
} catch (Exception e) {
if(con != null){
con.rollback();
}
} finally {
try {
if(stmt != null){
stmt.close();
}
if(con != null){
con.close();
}
} catch (Exception e) {
con = null;
}
}
}
Если бы вы использовали JdbcDaoSuport от Spring, это выглядело бы так:
public class YourDao extends JdbcDaoSupport{
public void updateMethod(){
String sql = "update example set id = ?";
getJdbcTemplate().update(sql, new Object[] { new Integer(1)});
}
}
Ответ №2:
- Вы почти всегда должны предпочитать PreparedStatement для параметризованных запросов, поскольку это:
- защищает вас от SQL-инъекций
- получает предварительную компиляцию по базе данных
- Вам не нужно повторно инициализировать rs, stmt и con в значение null, поскольку они выходят за пределы области действия в конце метода
- Вам почти всегда лучше использовать какую-нибудь платформу доступа к БД. Одним из примеров является Spring с его JdbcTemplate, другим является Apache Commons DBUtils
- Если вы обновляете систему, обновите ее до java 7 — вы получите try-with-resources, который закроет ваши соединения и операторы бесплатно, и ваш код будет выглядеть следующим образом:
public void updateMethod() {
int updateCount = 0;
String query = "update example set id = ?";
try (Connection con = BoneCPConnection.getConnectionPool().getConnection();
PreparedStatement stmt = con.prepareStatement(query)) {
stmt.setInt(1, 1);
updateCount = stmt.executeUpdate();
System.out.println("Result: " updateCount);
} catch (Exception e) {
System.out.println("Issue with updateMethod");
e.printStackTrace();
}
}
public ArrayList getMethod() {
ArrayList a = new ArrayList();
String query = "select * from example";
try (Connection con = BoneCPConnection.getConnectionPool().getConnection();
Statement stmt = con.createStatement()) {
con.setAutoCommit(false);
try (
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
System.out.println("Result: " rs.getInt(1));
}
}
} catch (Exception e) {
System.out.println("Issue with getMethod");
e.printStackTrace();
}
return a;
}
Комментарии:
1. Полезно знать о java 7, но у меня нет возможности обновлять это, так что на данный момент это не вариант.
Ответ №3:
самое важное, что нужно закрыть, — это соединение. как правило, вы хотите попробовать /, наконец, каждый ресурс (rs, stmt, conn) при закрытии.
кроме того, вы не хотите фиксировать в блоке finally. вы хотите зафиксировать только в счастливом пути (внутри блока catch).
наконец, никогда, никогда, никогда не помещайте «e.printStackTrace()» в свой код.
Обновить:
Объяснение предыдущего предостережения о printStackTrace()
: Это запах кода, и это, скорее всего, означает, что программа проглатывает исключение. он автоматически вставляется IDE или вручную вставляется программистами и в значительной степени означает, что они фактически не обрабатывают исключение. когда-нибудь, где-нибудь, они захотят увидеть это исключение, но оно будет потеряно навсегда. (тогда есть наихудший сценарий, когда код случайно продолжается, как будто исключения никогда не было, оставляя код в заблокированном состоянии). В принципе, есть 2 варианта:
- если вы хотите продолжить после исключения, запишите его в соответствующее средство ведения журнала (используйте java.util.Регистратор, если ничего другого)
- если вы не можете обработать исключение, повторно выбросьте его
(технически, существует также несколько случаев, когда это действительно игнорируемое исключение, и в этом случае добавьте комментарий в like // i never, ever care if i get this exception
).
Комментарии:
1.
lastly, never, ever, ever put "e.printStackTrace()" in your code.
Почему бы и нет?2. @Dave — обновление ответа, слишком много, чтобы писать в комментарии.
3. Хорошо, запах кода, я куплюсь на это. Тем не менее, это не значит, что это неправильно, просто флаг для незаконченных дел; Я думаю, что «никогда, никогда, никогда» немного сильно без оговорки «в окончательном коде». Вы заставили меня думать, что я не знал о каком-то побочном эффекте.
4. @Dave — дело в том, что это неправильно. если вы не хотите иметь дело с обработкой этого исключения в данный момент времени, то просто дайте ему всплыть или повторно создайте его с соответствующей оболочкой. это так же просто, как ввести функцию printStackTrace(), и у нее нет никакого риска случайного проглатывания исключения. нет никаких веских причин когда-либо включать это в свой код.
5. @Dave — есть побочный эффект: исключение остановлено, и маловероятно, что кто-нибудь увидит эту трассировку стека, иначе «скрывающую ошибку».
Ответ №4:
Ответ на вопрос 1: — если вам не очень нравятся фреймворки и ваша бизнес-логика взаимодействия с БД достаточно проста, вы можете продолжать использовать этот подход. Другими альтернативами являются: Hibernate, JPA или Spring JDBC. Все они делают одно и то же, аналогично вашему коду, но скрыто в реализации фреймворка. Каждый из этих фреймворков имеет свои преимущества / недостатки. Но опять же, это вопрос предпочтений / требований / сложности.
Ответ на вопрос 2: — вы открываете / закрываете ресурсы в правильном порядке, однако есть некоторые проблемы: 1. Попробуйте перехватить более конкретное исключение, а не исключение. Исключение SQLException должно быть зафиксировано в вашем примере. 2. Лучше использовать PreparedStatement вместо Statement, чтобы избежать внедрения SQL или если вы запускаете один и тот же запрос несколько раз. 3. Производственный код не должен использовать System.out.println или e.printStackTrace, но для спайков или использования новых материалов это не проблема. (Что касается предыдущего ответа).