Spring源码之JdbcTemplate中的坑,你中招了吗
我上篇文章分析了JdbcTemplate中各种回调类的使用,但是,我们平常用JdbcTemplate最多的还是query()方法和queryForObject()方法。同样,其中还有一个使用最多的是BeanPropertyRowMapper。
但是,在JdbcTemplate.queryForObject()中有一个很不起眼的坑,BeanPropertyRowMapper中也一样。
坑一:BeanPropertyRowMapper的mapRow()
我们在使用BeanPropertyRowMapper时,是给query()方法传递一个BeanPropertyRowMapper对象,让JdbcTemplate帮我们把查询结果集ResultSet的每一行结果都使用BeanPropertyRowMapper.mapRow()方法,转化成我们想要的Java类对象。从BeanPropertyRowMapper名称上也能够看出来,它是用来映射Java对象的属性和MySQL表的字段名称的。但是,在映射的过程中,如果不注意Java对象的属性名的规范,很可能就得不到我们想要的结果。
先来看一下,在创建BeanPropertyRowMapper对象时,会调用其中的initialize方法,我们看一下initialize方法的具体实现。
BeanPropertyRowMapper中的initialize方法
如图,
首选,说一下mappedFields,mapperFields是一个HashMap,用来匹配Java对象的属性和MySQL表的字段名的。mapperFields中存放所有可能与MySQL表的字段名映射上的那些Java属性名字。
如红色框框的第一行,在initialize方法中,BeanPropertyRowMapper会把传入的泛型Java类的所有属性名称的全小写形式放入mapperFields中,
第二行,把Java类的属性名转化成下划线分割的形式,如myName会被转化成my_name,这是因为,数据库在设计字段名称的时候,一般都会使用下划线分割形式,也就是my_name。
重点(敲黑板)
所以,如果在使用时,Java类名称要想和数据库字段名称匹配上,必须要把数据库字段名称设计成以下两种中的一种,
数据库字段名设计成全小写的形式,如myname;数据库字段名设计成下划线分割的形式,如my_name;
同时,Java属性名称应该尽量遵循Java编码风格,使用camelCase风格,如myName。
坑二:JdbcTemplate.queryForObject()
在使用queryForObject()方法时,我们一般是希望返回一个对象,而不是像query()方法一样返回一个列表。
但是,如果ResultSet结果集是空的话,queryForObject会抛出异常;如果ResultSet结果集的大小大于1的话,queryForObject也会抛出异常。
查看一下queryForObject源码:
queryForObject
其实,queryForObject使用的是一个SingleColumnRowMapper对象,其中,第二个queryForObject方法会调用第一个queryForObject方法,坑就在DataAccessUtils.requiredSingleResult(results)里面。
看一下DataAccessUtils.requiredSingleResult(results)源码:
requiredSingleResult
如图所示,如果结果集为空,或者结果集的数量大于1,都会抛出异常,导致queryForObject()无法返回期望结果。
在我们想查找出一个对象时,应该使用query()方法,如果返回结果不为空,那么取其中的第一个结果就好了。一般不要使用queryForObject方法,queryForObject会要求符合条件的数据库结果集有且仅有一条记录,记录不存在或者记录数大于1都会抛出异常。