Перейти к основному содержимому

Руководство по исключению Spring NonTransientDataAccessException

· 4 мин. чтения

1. Обзор

В этом кратком руководстве мы рассмотрим наиболее важные типы распространенных исключений NonTransientDataAccessException и проиллюстрируем их примерами.

2. Базовый класс исключений

Подклассы этого основного класса исключений представляют исключения, связанные с доступом к данным, которые считаются невременными или постоянными.

Проще говоря, это означает, что до тех пор, пока основная причина не будет устранена, все будущие попытки метода, вызвавшего исключение, будут неудачными.

3. Исключение DataIntegrityViolationException

Этот подтип NonTransientDataAccessException вызывается, когда попытка изменить данные приводит к нарушению ограничения целостности.

В нашем примере класса Foo столбец имени определен как не допускающий нулевого значения:

@Column(nullable = false)
private String name;

Если мы попытаемся сохранить экземпляр, не задав значение для имени, мы можем ожидать, что будет выброшено исключение DataIntegrityViolationException :

@Test(expected = DataIntegrityViolationException.class)
public void whenSavingNullValue_thenDataIntegrityException() {
Foo fooEntity = new Foo();
fooService.create(fooEntity);
}

3.1. DuplicateKeyException

Одним из подклассов DataIntegrityViolationException является DuplicateKeyException , который выдается при попытке сохранить запись с уже существующим первичным ключом или значением, которое уже присутствует в столбце с уникальным ограничением, например при попытке вставить две строки . в таблице foo с тем же идентификатором 1:

@Test(expected = DuplicateKeyException.class)
public void whenSavingDuplicateKeyValues_thenDuplicateKeyException() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(restDataSource);
jdbcTemplate.execute("insert into foo(id,name) values (1,'a')");
jdbcTemplate.execute("insert into foo(id,name) values (1,'b')");
}

4. DataRetrievalFailureException

Это исключение возникает, когда возникает проблема при извлечении данных, например при поиске объекта с идентификатором, которого нет в базе данных.

Например, мы собираемся использовать класс JdbcTemplate , у которого есть метод, выдающий это исключение:

@Test(expected = DataRetrievalFailureException.class)
public void whenRetrievingNonExistentValue_thenDataRetrievalException() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(restDataSource);

jdbcTemplate.queryForObject("select * from foo where id = 3", Integer.class);
}

4.1. Инкоректресултсетколумнкаунтексцептион

Этот подкласс исключения возникает при попытке получить несколько столбцов из таблицы без создания надлежащего RowMapper :

@Test(expected = IncorrectResultSetColumnCountException.class)
public void whenRetrievingMultipleColumns_thenIncorrectResultSetColumnCountException() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(restDataSource);

jdbcTemplate.execute("insert into foo(id,name) values (1,'a')");
jdbcTemplate.queryForList("select id,name from foo where id=1", Foo.class);
}

4.2. Инкорректрезультатсизедатаакцессцептион

Это исключение выдается, когда количество полученных записей отличается от ожидаемого, например, при ожидании одного целочисленного значения, но получении двух строк для запроса:

@Test(expected = IncorrectResultSizeDataAccessException.class)
public void whenRetrievingMultipleValues_thenIncorrectResultSizeException() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(restDataSource);

jdbcTemplate.execute("insert into foo(name) values ('a')");
jdbcTemplate.execute("insert into foo(name) values ('a')");

jdbcTemplate.queryForObject("select id from foo where name='a'", Integer.class);
}

5. Исключение DataSourceLookupFailureException

Это исключение возникает, когда указанный источник данных не может быть получен. Например, мы будем использовать класс JndiDataSourceLookup для поиска несуществующего источника данных:

@Test(expected = DataSourceLookupFailureException.class)
public void whenLookupNonExistentDataSource_thenDataSourceLookupFailureException() {
JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
DataSource dataSource = dsLookup.getDataSource("java:comp/env/jdbc/example_db");
}

6. Исключение InvalidDataAccessResourceUsageException

Это исключение возникает при неправильном доступе к ресурсу, например, когда у пользователя нет прав SELECT .

Чтобы проверить это исключение, нам нужно отозвать право SELECT для пользователя, а затем выполнить запрос SELECT:

@Test(expected = InvalidDataAccessResourceUsageException.class)
public void whenRetrievingDataUserNoSelectRights_thenInvalidResourceUsageException() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(restDataSource);
jdbcTemplate.execute("revoke select from tutorialuser");

try {
fooService.findAll();
} finally {
jdbcTemplate.execute("grant select to tutorialuser");
}
}

Обратите внимание, что мы восстанавливаем разрешение для пользователя в блоке finally .

6.1. BadSqlGrammarException

Очень распространенным подтипом InvalidDataAccessResourceUsageException является BadSqlGrammarException , который возникает при попытке выполнить запрос с недопустимым SQL:

@Test(expected = BadSqlGrammarException.class)
public void whenIncorrectSql_thenBadSqlGrammarException() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(restDataSource);
jdbcTemplate.queryForObject("select * fro foo where id=3", Integer.class);
}

Обратите внимание, конечно, на fro , который является недопустимым аспектом запроса.

7. Исключение CannotGetJdbcConnectionException

Это исключение возникает, когда попытка подключения через JDBC завершается неудачно, например, если URL-адрес базы данных неверен. Если мы напишем URL следующим образом:

jdbc.url=jdbc:mysql:3306://localhost/spring_hibernate4_exceptions?createDatabaseIfNotExist=true

Тогда при попытке выполнить инструкцию будет выброшено исключение CannotGetJdbcConnectionException :

@Test(expected = CannotGetJdbcConnectionException.class)
public void whenJdbcUrlIncorrect_thenCannotGetJdbcConnectionException() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(restDataSource);
jdbcTemplate.execute("select * from foo");
}

8. Заключение

В этом очень точном уроке мы рассмотрели некоторые из наиболее распространенных подтипов класса NonTransientDataAccessException .

Реализацию всех примеров можно найти в проекте GitHub . И, конечно же, все примеры используют базу данных в памяти, так что вы можете легко запускать их, ничего не настраивая.