Ошибки, как известно, случаются. И случаются они без нашего желания, поэтому остаётся их только обрабатывать.
SQLException
JDBC использует стандартный механизм Java для работы с ошибками: исключения. В java.sql заведена целая новая иерархия исключений, прародителем которой является SQLException.
SQLException наследуется от Exception и является, таким образом, checked exception. Кроме стандартных данных, которые передаются в Exception, SQLException всегда содержит дополнительные данные о ошибке:- getMessage() возвращает сообщение об ошибке.
- getSqlState() возвращает стандартный код состояния запроса. Говоря по простому — каждый раз, когда SQL база данных выполняет запрос, она присваивает этому запросу некий статус: успешно; успешно, но с предупреждениями; совсем не успешно итд. Значения этих статусов стандартизированы в XOpen SQLState convention. SQLState всегда состоит из 5 знаков, первые два знака отмечают класс состояния, оставшиеся три — подкласс состояния. Обычно классы 00 и 01 означают, что запрос выполнился нормально, остальные говорят об ошибке в запросе. Не смотря на то, что значения статусов стандартизированы, лучше проверять документацию по вашей конкретной реализации базы данных, поскольку стандарты это конечно стандарты, но всё равно все реализуют их по своему.
- getErrorCode() возвращает номер ошибки, специфичный для вашей реализации БД.
- getNextException() возвращает следующий SQLException в цепочке, если при исполнении запроса было сгенерировано несколько SQLException. Вызов getNextException() для последнего SQLException в цепочке возвращает его самого.
Пример обработки SQLException, возникающего при попытке вставить в таблицу некорректные данные:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
try (PreparedStatement addSt = db.prepareStatement(ADD_CLIENT)) {
addSt.setInt(1, 1);
addSt.setString(2, "TEST");
addSt.addBatch();
addSt.setInt(1, 2);
addSt.setString(2, "TEST");
addSt.addBatch();
addSt.executeBatch();
} catch (SQLException ex) {
System.out.println("SQLException message:" + ex.getMessage());
System.out.println("SQLException SQL state:" + ex.getSQLState());
System.out.println("SQLException SQL error code:" + ex.getErrorCode());
}
|
1
2
3
4
|
SQLException message:Unique index or primary key violation: "CONSTRAINT_INDEX_5 ON PUBLIC.CLIENTS(LOGIN) VALUES ('TEST', 1)"; SQL statement:
INSERT INTO CLIENTS(ID, LOGIN) VALUES(?, ?) [23505-190]
SQLException SQL state:23505
SQLException SQL error code:23505
|
Наследники SQLException подразделяются на три больших класса:
- SQLRecoverableException — ошибка, которая может быть исправлена методом «попробуйте выключить и включить»: переустановить соединение, переповторить транзакцию итд.
- SQLTransientException — ошибка, которая может быть исправлена при повторе операции позднее.
- SQLNonTransientException — ошибка, которая не может быть исправлена кроме как исправлением запроса или его данных.
Особняком держится BatchUpdateException, который не относится ни к одному из трёх классов перечисленных выше и выбрасывается когда в executeBatch() возникает ошибка (или select запрос). Это исключение содержит дополнительный метод getUpdateCounts(), которые возвращает массив чисел. Каждое число массива это количество строк, на которые подействовал соответствующий запрос в наборе.
SQLWarning
SQLWarning это исключение, которое не выбрасывается. Будучи унаследованным от SQLException, SQLWarning имеет все те же самые дополнительные возможности, что и SQLException. Однако, экземпляры этого класса не выбрасываются, а запрашиваются напрямую методом getWarnings() у объектов Connection, Statement или ResultSet.Суть в том, что SQLWarning возникает, когда ошибки как бы и нет, но вот результат может отличаться от запрошенного. Например когда MySQL получает запрос update, который ничего не изменяет, он возвращает warning, в котором говорится, что запрос ничего не изменил. В остальном SQLWarning можно обрабатывать так же, как и SQLException.
К сожалению, добиться генерации SQLWarning от H2 базы мне не удалось, поэтому примера не будет.
Код примера доступен на github.