Руководство по Spring NonTransientDataAccessException

Руководство по Spring NonTransientDataAccessException

1. обзор

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

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

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

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

3. DataIntegrityViolationExceptionс

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

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

@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, который выдается, когда есть попытка сохранить запись с уже существующим первичным ключом или значением, которое уже присутствует в столбце сunique, например, попытка вставить две строки в таблицуfoo с тем жеid равным 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 IncorrectResultSetColumnCountException

Этот подкласс исключения генерируется при попытке получить несколько столбцов из таблицы без создания правильного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 IncorrectResultSizeDataAccessException

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

@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.

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