Spring工程中JDBC抛出"EmptyResultDataAccessException"异常的解决方案

本文解析了Spring框架中因查询结果不匹配预期导致的EmptyResultDataAccessException与IncorrectResultSizeDataAccessException异常,并提供了具体的解决策略。

一、 问题日志

严重: Servlet.service() for servlet [spring] in context with path [/XXXXX] threw exception [Request processing failed; nested exception is org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0] with root cause
org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0
	at org.springframework.dao.support.DataAccessUtils.nullableSingleResult(DataAccessUtils.java:97)
	at org.springframework.jdbc.core.JdbcTemplate.queryForObject(JdbcTemplate.java:779)

二、 问题原因

顺藤摸瓜,打开"DataAccessUtils":

/**
 * Return a single result object from the given Collection.
 * <p>Throws an exception if 0 or more than 1 element found.
 * @param results the result Collection (can be {@code null}
 * and is also expected to contain {@code null} elements)
 * @return the single result object
 * @throws IncorrectResultSizeDataAccessException if more than one
 * element has been found in the given Collection
 * @throws EmptyResultDataAccessException if no element at all
 * has been found in the given Collection
 * @since 5.0.2
 */
@Nullable
public static <T> T nullableSingleResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException {
	// This is identical to the requiredSingleResult implementation but differs in the
	// semantics of the incoming Collection (which we currently can't formally express)
	if (CollectionUtils.isEmpty(results)) {
		throw new EmptyResultDataAccessException(1);
	}
	if (results.size() > 1) {
		throw new IncorrectResultSizeDataAccessException(1, results.size());
	}
	return results.iterator().next();
}

从注释或者源码可以看出,数据库执行结果为0时,抛出"EmptyResultDataAccessException"异常,大于1时,抛出"IncorrectResultSizeDataAccessException"异常,这样设计有个好处,就是更加详细地反馈了数据信息,便于开发者处理查询结果。


三、 解决方案

在调用"queryForObject"方法的位置捕捉以上两种异常,参考:

public UserVO queryUserByEmail(String email) {
	String SQL = "select * from " + Const.User.TABLE_NAME + " where " + Const.User.COLUMN_EMAIL + " = ?";
	UserVO userVO = null;
	try {
		userVO = jdbcTemplate.queryForObject(SQL, new UserMapper(), email);
		if (userVO != null) {
			userVO.setPassword("");
		}
	} catch (EmptyResultDataAccessException e) {
		e.printStackTrace();
	} catch (IncorrectResultSizeDataAccessException e) {
		e.printStackTrace();
	}
	return userVO;
}

四、 参考文献

<think>我们正在讨论Spring JDBC中的findOne方法。根据引用[1],JdbcDaoSupport类提供了设置数据源或JdbcTemplate的方法。通常,我们会扩展这个类来简化配置。但是,findOne方法并不是Spring JDBC核心模块直接提供的,而是Spring Data JDBC中的概念。然而,用户明确提到了Spring JDBC,所以我们可能需要使用JdbcTemplate来实现类似的功能。 在Spring JDBC中,我们通常使用JdbcTemplate来执行查询,并手动映射结果集到对象。对于findOne,我们通常是根据主键查询单个对象。 步骤: 1. 创建一个DAO类,该类继承JdbcDaoSupport(或者我们也可以直接注入JdbcTemplate)。 2. 在DAO类中定义一个方法,通过主键查询记录。 3. 使用JdbcTemplate的queryForObject方法,并提供一个RowMapper来将结果集映射到对象。 示例代码: 假设我们有一个User类,具有id、name和email属性。 然后,我们创建一个UserDAO类,继承JdbcDaoSupport,并实现findById方法。 注意:引用[2]提到了审计功能(如@CreatedBy等),但这是Spring Data JDBC的特性,而这里我们使用的是Spring JDBC(即JdbcTemplate)。所以,我们不会使用那些注解。 根据引用[3],我们需要添加spring-jdbc和数据库驱动(如mysql-connector-java)的依赖。 下面是一个示例: ```java // User.java public class User { private Long id; private String name; private String email; // 省略构造器、getter和setter } // UserDao.java import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.support.JdbcDaoSupport; import java.sql.ResultSet; import java.sql.SQLException; public class UserDao extends JdbcDaoSupport { public User findById(Long id) { String sql = "SELECT id, name, email FROM users WHERE id = ?"; return getJdbcTemplate().queryForObject(sql, new Object[]{id}, new UserRowMapper()); } private static final class UserRowMapper implements RowMapper<User> { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setId(rs.getLong("id")); user.setName(rs.getString("name")); user.setEmail(rs.getString("email")); return user; } } } ``` 在配置中,我们需要为UserDao设置数据源(因为JdbcDaoSupport需要数据源来创建JdbcTemplate)。例如,在Spring配置文件中: ```xml <bean id="userDao" class="com.example.UserDao"> <property name="dataSource" ref="dataSource" /> </bean> ``` 或者使用Java配置: ```java @Configuration public class AppConfig { @Bean public DataSource dataSource() { // 创建并配置数据源 DriverManagerDataSource dataSource = new DriverManagerDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/mydb"); dataSource.setUsername("user"); dataSource.setPassword("password"); return dataSource; } @Bean public UserDao userDao() { UserDao userDao = new UserDao(); userDao.setDataSource(dataSource()); return userDao; } } ``` 这样,我们就可以通过userDao.findById(id)来查询单个用户。 注意:如果查询不到结果,queryForObject会抛出EmptyResultDataAccessException异常。因此,在调用时,如果有可能不存在,我们可以捕获这个异常并返回null,或者在方法内部处理。 另外,如果查询结果多于一条,也会抛出异常。 所以,这个findById方法就是实现了类似findOne的功能。 引用[4]提到了ORM工具,但这里我们使用的是Spring JDBC,它比ORM更底层,但比直接使用JDBC API更简单。 综上所述,我们在Spring JDBC中通过JdbcTemplate的queryForObject方法结合RowMapper来实现根据主键查询单个对象的功能。 相关问题: 1. Spring JDBC中如何实现分页查询? 2. 在Spring JDBC中如何处理多表关联查询? 3. Spring JDBCSpring Data JDBC有什么区别?</think>### Spring JDBC 中实现 `findOne` 方法示例 在 Spring JDBC 中,**没有直接提供 `findOne` 方法**,但可以通过 `JdbcTemplate` 的 `queryForObject` 实现相同功能。以下是完整实现步骤: --- #### 1. 定义实体类 ```java public class User { private Long id; private String name; private String email; // 构造器、getter 和 setter(省略) } ``` #### 2. 创建 DAO 类(继承 `JdbcDaoSupport`) ```java import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class UserDao extends JdbcDaoSupport { // 实现 findOne 方法 public User findOne(Long id) { String sql = "SELECT id, name, email FROM users WHERE id = ?"; return getJdbcTemplate().queryForObject( sql, new Object[]{id}, // 参数 new UserRowMapper() // 结果映射 ); } // 自定义结果映射器 private static class UserRowMapper implements RowMapper<User> { @Override public User mapRow(ResultSet rs, int rowNum) throws SQLException { User user = new User(); user.setId(rs.getLong("id")); user.setName(rs.getString("name")); user.setEmail(rs.getString("email")); return user; } } } ``` --- #### 3. Spring 配置(XML 示例) ```xml <!-- 数据源配置 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <property name="username" value="root"/> <property name="password" value="password"/> </bean> <!-- DAO 配置 --> <bean id="userDao" class="com.example.UserDao"> <property name="dataSource" ref="dataSource"/> <!-- 注入数据源 --> </bean> ``` --- #### 4. 使用示例 ```java // 在 Service 中调用 User user = userDao.findOne(1L); System.out.println(user.getName()); // 输出查询结果 ``` --- ### 关键点说明 1. **继承 `JdbcDaoSupport`** 通过继承此类,可直接使用 `getJdbcTemplate()` 方法[^1]。 2. **`queryForObject` 方法** - 第一个参数:SQL 查询语句 - 第二个参数:查询参数(`Object[]`) - 第三个参数:`RowMapper` 实现,用于结果集映射 3. **异常处理** 如果记录不存在,会抛出 `EmptyResultDataAccessException`,建议在业务层捕获处理。 > ⚠️ 注意:Spring JDBC 需要添加依赖: > ```xml > <dependency> > <groupId>org.springframework</groupId> > <artifactId>spring-jdbc</artifactId> > <version>5.3.20</version> > </dependency> > <dependency> > <groupId>mysql</groupId> > <artifactId>mysql-connector-java</artifactId> > <version>8.0.29</version> > </dependency> > ``` --- ### 与 Spring Data JDBC 的区别 若使用 **Spring Data JDBC**(非 Spring JDBC),可直接在 Repository 中声明: ```java public interface UserRepository extends CrudRepository<User, Long> { Optional<User> findById(Long id); // 自动实现 } ``` 但此方式需要额外引入 `spring-data-jdbc` 依赖[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值