【JdbcTemplate】为什么使用JdbcTemplate的这些API必须要加try catch?

本文基于Java8和spring - jdbc - 4.1.6.RELEASE.jar,剖析简单查询背后的秘密。深入方法源码发现,查询方法会抛出非检查性异常,对结果集数量有严格要求。还给出推荐使用方式,强调使用查询API时要进行异常处理,避免线上事故。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

软件版本 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的方法能看到,

  1. 它抛出了一个非检查性异常DataAccessException

  2. 并且我们能发现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的方法我们能得到一下信息,

  1. 它抛出了一个非检查性异常DataAccessException

  2. 根据方法的返回参数判断,它的返回值为单个实体,也就是说queryForMap方法的返回值也只是一条数据。

  3. 对返回值做了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();
	}
}

好家伙!原来坑就在这里,

  1. 如果查询结果集为空,则会抛出EmptyResultDataAccessException,

  2. 如果查询结果集大于1,则会抛出IncorrectResultSizeDataAccessException,

  3. 只有当查询结果集等于一条数据时,才能正常返回数据。

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 () {}进行处理:

  1. queryForMap

  2. queryForObject

想想,如果我们不进行异常捕获或处理,当查询结果集为空时,是很容易导致线上事故的。

彩蛋

今天对我来说是特别的一天,因为今天是我的生日!

今天我还邀请了两个同事,一起去大井巷的秀孃孃串串打了个卡,生日过的也算是蛮开心的呢。

宝贝给我说,我又老了一岁了!我永远都会比她大一岁,她好开心啊!

哈哈,确实是酱紫。

祝自己25岁生日快乐呀🎂🎂🎂!Happy birthday to me!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值