暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

使用 queryForObject 方法时需注意的地方

rookiedev 2020-06-30
631

我们应该都使用过 JdbcTemplate 来查询数据库,一般用在配置了多数据源的情况下,在一个服务里面需要简单查询多个数据库的数据,这时通过 JdbcTemplate 来构建 SQL 查询数据库往往比较方便快捷些,不需要用封装的 repository 那一套东西。
举一个简单的例子,比如查询一个活动的浏览量。
1private Long findByActivityId(String activityId) {
2   String selectSql = "select times_viewed from activity_stats where activity_id = ?";
3   Long timesViewed = this.jdbcTemplate.queryForObject(selectSql, Long.class,
4            activityId);
5   return timesViewed;
6}
复制
其实我们咋一看上面这段代码,好像也没多大问题,只是一个普通的查询而已。在有数据的情况下,经过测试,程序也运行正常。但是如果你把你要查询的那个活动 id 的那条数据删除,这时再去测试会发现程序抛异常了,抛的异常类是 EmptyResultDataAccessException,从类的名字我们就可以看出,查询结果为空异常。
这里为什么会产生这种情况呢,我们深入查看下源码就知道了。
1public <T> queryForObject(String sql, SqlParameterSource paramSource, RowMapper<T> rowMapper)
2            throws DataAccessException 
{
3
4        List<T> results = getJdbcOperations().query(getPreparedStatementCreator(sql, paramSource), rowMapper);
5        return DataAccessUtils.nullableSingleResult(results);
6}
复制
我们点进去查看 queryForObject 的方法体可以看到,在返回查询结果前调用了 DataAccessUtils 类的 nullableSingleResult 方法,那么再继续点进去查看 nullableSingleResult 的方法体:
1public static <T> nullableSingleResult(@Nullable Collection<T> results) throws IncorrectResultSizeDataAccessException {
2        if (CollectionUtils.isEmpty(results)) {
3            throw new EmptyResultDataAccessException(1);
4        } else if (results.size() > 1) {
5            throw new IncorrectResultSizeDataAccessException(1, results.size());
6        } else {
7            return results.iterator().next();
8        }
9}
复制
从 nullableSingleResult 的方法体中可以看到,当查询的结果为空的时候,手动抛了一个 EmptyResultDataAccessException 异常,同时我们还可以看到当查询结果记录数大于 1 时,手动抛了一个 IncorrectResultSizeDataAccessException 异常。
这时候我们就要注意了,如果当前我们要执行的 SQL 的查询结果可能为空,或者结果集可能大于 1 时就需要根据我们的业务需求来进行手动处理这两个异常,不然程序会由于异常无法进行下去。
在这里个人觉得结果大于 1 的情况抛出异常还能理解,但是结果为空,其实在很多情况下我们都是希望代码正常运行下去,而不是直接抛出异常,有可能我们还有别的业务需要执行。所以不太清楚这里为什么需要做这个处理。
对于结果为空时抛出异常的情况,处理方式也很简单,在调用 queryForObject 方法的地方用 try catch 包起来,对 EmptyResultDataAccessException 异常进行捕获,同时忽略异常信息就好。
1private Long queryByActivityId(String activityId) {
2    String selectSql = "select times_viewed from activity_stats where activity_id = ?";
3    Long timesViewed = null;
4    try {
5        timesViewed = this.jdbcTemplate.queryForObject(selectSql, Long.class,
6                activityId);
7    }
8    catch (EmptyResultDataAccessException ignored) {
9    }
10    return timesViewed;
11}
复制
同理,如果对结果集可能大于 1 的情况也要特殊处理的话,我们就直接捕获 IncorrectResultSizeDataAccessException 异常就好,因为你可以看到 EmptyResultDataAccessException 是继承 IncorrectResultSizeDataAccessException 的。所以就直接捕获 IncorrectResultSizeDataAccessException 就好。
1private Long queryByActivityId(String activityId) {
2    String selectSql = "select times_viewed from activity_stats where activity_id = ?";
3    Long timesViewed = null;
4    try {
5        timesViewed = this.jdbcTemplate.queryForObject(selectSql, Long.class,
6                activityId);
7    }
8    catch (IncorrectResultSizeDataAccessException ignored) {
9    }
10    return timesViewed;
11}
复制
这里再提下 JdbcTemplate 和 NamedParmeterJdbcTemplate 的主要区别:
对于 JdbcTemplate,sql 的查询条件参数是使用占位符 ? 表示,受到了顺序的限制,如果是需要多个参数,在传入数组参数的时候,必须按照占位符的顺序传入参数,一旦传入的顺序错误就可能会造成非预期的结果。
而 NamedParmeterJdbcTemplate,在 Spring Jdbc 框架中,通过这个 NameParameterJdbcTemplate 使用具名参数的方式来绑定 Sql 参数。具名参数的格式为 “ :parameterName”,多个具名参数通过 Map key value 的方式传入,key 和 parameterName 保持一致,这种情况下就和参数的顺序没有关系了,主要是通过 Map 的 key 来取值。
  励志成为一名菜鸟码农,共勉!


文章转载自rookiedev,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论