软件版本 java8 spring-jdbc-4.1.6.RELEASE.jar
简单查询
首先我们来看这样一段代码。
Map map = jdbcTemplate.queryForMap("SELECT * FROM t_user WHERE user_id = '123'");
看起来只是非常简单的一句查询,它使用jdbcTemplate#queryForMap
方法去查询一个user_id = '123'
的用户,并将查询结果返回到Map
。
看似一切都很正常合理。但是,事情真的有这么简单吗?
如果有人review
了你的代码突然这么反问你,那么在这简单的背后往往都暗藏了一些猫腻。
背后那些不为人知的秘密
我们先进queryForMap
方法去看一下。
public Map<String, Object> queryForMap(String sql) throws DataAccessException {
return (Map)this.queryForObject(sql, this.getColumnMapRowMapper());
}
果然,从queryForMap
的方法能看到,
- 它抛出了一个非检查性异常
DataAccessException
, - 并且我们能发现
queryForMap
内部,实际是调用的queryForObject
方法。
我们再进queryForObject
方法去看一下。
public <T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException {
List<T> results = this.query(sql, rowMapper);
return DataAccessUtils.requiredSingleResult(results);
}
从queryForObject
的方法我们能得到一下信息,
- 它抛出了一个非检查性异常
DataAccessException
, - 根据方法的返回参数判断,它的返回值为单个实体,也就是说
queryForMap
方法的返回值也只是一条数据。 - 对返回值做了
requiredSingleResult
检查。
我们再进requiredSingleResult
方法去看一下。
spring-tx-4.1.6.RELEASE.jar
public static <T> T requiredSingleResult(Collection<T> results) throws IncorrectResultSizeDataAccessException {
int size = results != null ? results.size() : 0;
if (size == 0) {
throw new EmptyResultDataAccessException(1);
} else if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, size);
} else {
return results.iterator().next();
}
}
好家伙!原来坑就在这里,
- 如果查询结果集为空,则会抛出
EmptyResultDataAccessException
, - 如果查询结果集大于1,则会抛出
IncorrectResultSizeDataAccessException
, - 只有当查询结果集等于一条数据时,才能正常返回数据。
spring-tx-5.1.14.RELEASE.jar
public static <T> T requiredSingleResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException {
if (CollectionUtils.isEmpty(results)) {
throw new EmptyResultDataAccessException(1);
} else if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, results.size());
} else {
return results.iterator().next();
}
}
@Nullable
public static <T> T nullableSingleResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException {
if (CollectionUtils.isEmpty(results)) {
throw new EmptyResultDataAccessException(1);
} else if (results.size() > 1) {
throw new IncorrectResultSizeDataAccessException(1, results.size());
} else {
return results.iterator().next();
}
}
这里我还贴出了 5.x 版本的源码。在 5.x 版本的queryForObject
方法,是使用的nullableSingleResult
方法进行结果集的校验。
nullableSingleResult
翻译为可为空的单一结果
,我个人觉得这个方法的命名是具有误导性的,更推荐使用requiredSingleResult
。
推荐的使用方式
其实对于查询结果集为空时,我们并不希望它直接抛出异常,所以我们可以把EmptyResultDataAccessException
捕获一下,并返回一个空的Map。
Map<String, Object> map = null;
try {
map = jdbcTemplate.queryForMap("SELECT * FROM t_user WHERE user_id = '123'");
} catch (EmptyResultDataAccessException e) {
map = Collections.EMPTY_MAP;
}
小结一下
我们在使用JdbcTemplate
的这两个查询API时必须要加try {} catch () {}
进行处理:
queryForMap
queryForObject
想想,如果我们不进行异常捕获或处理,当查询结果集为空时,是很容易导致线上事故的。
彩蛋
今天对我来说是特别的一天,因为今天是我的生日!
今天我还邀请了两个同事,一起去大井巷的秀孃孃串串打了个卡,生日过的也算是蛮开心的呢。
宝贝给我说,我又老了一岁了!我永远都会比她大一岁,她好开心啊!
哈哈,确实是酱紫。
祝自己25岁生日快乐呀🎂🎂🎂!Happy birthday to me!