知識庫 / Spring RSS 訂閱

Spring 非瞬態數據訪問異常指南

Spring
HongKong
5
02:49 PM · Dec 06 ,2025

1. 概述

本快速教程將介紹最常見的 <em >NonTransientDataAccessException</em> 的主要類型,並提供示例説明。

2. 基異常類

該異常類是子類,代表與數據訪問相關的異常,這些異常被認為是不可變(非瞬態)或永久性的。

簡單來説,這意味着——在根原因未修復之前,導致異常的方法的所有後續嘗試都將失敗。

3. 數據完整性違反異常 (DataIntegrityViolationException)

本異常類型是 非瞬態數據訪問異常 (NonTransientDataAccessException) 的一個子類型,當嘗試修改數據時,如果違反了完整性約束時拋出。

在我們的 Foo 類示例中,name 列被定義為不允許 null 值的狀態:

@Column(nullable = false)
private String name;

如果嘗試在未為名稱設置值的情況下保存一個實例,我們預計將會拋出 <em DataIntegrityViolationException 異常:

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

3.1. DuplicateKeyException

DuplicateKeyExceptionDataIntegrityViolationException 的一個子類,當嘗試保存一個具有已存在的主鍵或在具有 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. 數據源查找失敗異常

當指定的數據庫連接池無法獲取時,會拋出此異常。例如,我們將使用 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

然後,當嘗試執行語句時,將會拋出 <emCanNotGetJdbcConnectionException 異常。

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

8. 結論

在本教程中,我們深入探討了 NonTransientDataAccessException 類中最常見的幾種子類型。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.