1. 概述
在这个简短教程中,我们将介绍最常见的NonTransientDataAccessException
类型的几种,并通过示例进行说明。
2. 基础异常类
这个主要异常类的子类表示与数据访问相关的非临时或永久性异常。
简单来说,这意味着只要根本原因未被修复,导致异常的方法的所有未来尝试都将失败。
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
,当尝试保存具有已存在的主键或唯一约束列(如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_hibernate5_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项目中找到。当然,所有示例都使用内存数据库,因此您可以轻松运行而无需设置任何环境。