- 浏览: 779464 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
萨琳娜啊:
Java读源码之Netty深入剖析网盘地址:https://p ...
Netty源码学习-FileRegion -
飞天奔月:
写得有趣 ^_^
那一年你定义了一个接口 -
GoldRoger:
第二个方法很好
java-判断一个自然数是否是某个数的平方。当然不能使用开方运算 -
bylijinnan:
<script>alert("close ...
自己动手实现Java Validation -
paul920531:
39行有个bug:"int j=new Random ...
java-蓄水池抽样-要求从N个元素中随机的抽取k个元素,其中N无法确定
JdbcTemplate中有两个可能会混淆的queryForObject方法:
1.
Object queryForObject(String sql, Object[] args, Class requiredType)
2.
Object queryForObject(String sql, Object[] args, RowMapper rowMapper)
第1个方法是只查一列的,参数“requiredType”不可以是自定义的类
如果要把查询结果封装为自定义的类,需要采用第2个方法
例如:
分析一下第2个方法的源码
先看看BeanPropertyRowMapper
其实不用看源码都知道思路了:
new BeanPropertyRowMapper(Customer.class)
mapRow(ResultSet rs, int rowNumber)
遍历ResultSet,每一行对应一个Customer对象
遍历每一行时,顺序遍历各列,取得该列的值通过反射调用对应的setter方法,赋值给到Customer对象
注意到无论是取列名还是取列的值,都是通过index(1~columnCount)来取的
注意到mapRow方法里面,populatedProperties变量是用来检查是否所有的property都正确地实现了赋值。这个功能在2.5.5的版本是没有的。在3.0.0的版本有,且可以通过函数来配置是否作这一项检查:
public BeanPropertyRowMapper(Class<T> mappedClass, boolean checkFullyPopulated)
再看看BeanPropertyRowMapper的构造函数:
看完了BeanPropertyRowMapper,回到queryForObject
调用过程:
queryForObject(String sql, Object[] args, RowMapper<T> rowMapper)
query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper, 1))
query(sql, newArgPreparedStatementSetter(args), resultSetExtractor)
query(new SimplePreparedStatementCreator(sql), argPreparedStatementSetter, resultSetExtractor)
query(PreparedStatementCreator psc, final PreparedStatementSetter argPreparedStatementSetter, final ResultSetExtractor<T> resultSetExtractor)
整个过程跟JdbcTemplate的batchUpdate方法是类似的
1.
对于参数'sql',它最终的作用是生成一个PreparedStatement:
connection.prepareStatement(sql);
2.
对sql语句中“?”的赋值,是通过PreparedStatementSetter
需要两个数据:
一是“?”所对应的值,也就是Object[] args
二是PreparedStatement
对第一个数据:
args是在ArgPreparedStatementSetter的构造函数中传入
对第二个数据:
在最后的query方法中,把创建好的PreparedStatement作为匿名内部类PreparedStatementCallback.doInPreparedStatement方法的参数
最后得以传给ArgPreparedStatementSetter:
ArgPreparedStatementSetter的setValues方法:
最后看一下RowMapperResultSetExtractor
它有两个字段:
private final RowMapper<T> rowMapper;
private final int rowsExpected;
因为是queryForObject,因此rowsExpected=1
extractData方法很直观,是在上面的doInPreparedStatement调用的:
从代码可看出,如果ResultSet为空,返回的List也不会是null,而是empty list
这也符合《Effective Java》的提议:返回类型是集合类型时,返回空集合而不是null
1.
Object queryForObject(String sql, Object[] args, Class requiredType)
2.
Object queryForObject(String sql, Object[] args, RowMapper rowMapper)
第1个方法是只查一列的,参数“requiredType”不可以是自定义的类
如果要把查询结果封装为自定义的类,需要采用第2个方法
例如:
//只查询一列:name String sql = "SELECT NAME FROM CUSTOMER WHERE CUST_ID = ?"; String name = (String)getJdbcTemplate().queryForObject( sql, new Object[] { custId }, String.class); return name; //查询返回自定义的类 String sql = "SELECT * FROM CUSTOMER WHERE CUST_ID = ?"; Customer customer = (Customer)getJdbcTemplate().queryForObject( sql, new Object[] { custId }, new BeanPropertyRowMapper(Customer.class)); return customer;
分析一下第2个方法的源码
先看看BeanPropertyRowMapper
其实不用看源码都知道思路了:
new BeanPropertyRowMapper(Customer.class)
mapRow(ResultSet rs, int rowNumber)
遍历ResultSet,每一行对应一个Customer对象
遍历每一行时,顺序遍历各列,取得该列的值通过反射调用对应的setter方法,赋值给到Customer对象
注意到无论是取列名还是取列的值,都是通过index(1~columnCount)来取的
public T mapRow(ResultSet rs, int rowNumber) throws SQLException { T mappedObject = BeanUtils.instantiate(this.mappedClass); BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject); ResultSetMetaData rsmd = rs.getMetaData(); int columnCount = rsmd.getColumnCount(); Set<String> populatedProperties = (isCheckFullyPopulated() ? new HashSet<String>() : null); for (int index = 1; index <= columnCount; index++) { String column = JdbcUtils.lookupColumnName(rsmd, index); PropertyDescriptor pd = this.mappedFields.get(column.replaceAll(" ", "").toLowerCase()); if (pd != null) { try { Object value = getColumnValue(rs, index, pd); bw.setPropertyValue(pd.getName(), value); if (populatedProperties != null) { populatedProperties.add(pd.getName()); } } } } if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) { throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields " + "necessary to populate object of class [" + this.mappedClass + "]: " + this.mappedProperties); } return mappedObject; }
注意到mapRow方法里面,populatedProperties变量是用来检查是否所有的property都正确地实现了赋值。这个功能在2.5.5的版本是没有的。在3.0.0的版本有,且可以通过函数来配置是否作这一项检查:
public BeanPropertyRowMapper(Class<T> mappedClass, boolean checkFullyPopulated)
再看看BeanPropertyRowMapper的构造函数:
public BeanPropertyRowMapper(Class<T> mappedClass) { initialize(mappedClass); } protected void initialize(Class<T> mappedClass) { this.mappedClass = mappedClass; /*保存field 注意到对于驼峰式命名的field: 例如,对于gameId, mappedFields 会同时保存"gameid"和"game_id"——注意全部变为小写了 因此在sql语句中, select id as game_id, name from game和 select id as gameId, name from game的效果是一样的 */ this.mappedFields = new HashMap<String, PropertyDescriptor>(); //保存property this.mappedProperties = new HashSet<String>(); PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass); for (PropertyDescriptor pd : pds) { if (pd.getWriteMethod() != null) { this.mappedFields.put(pd.getName().toLowerCase(), pd); String underscoredName = underscoreName(pd.getName()); if (!pd.getName().toLowerCase().equals(underscoredName)) { this.mappedFields.put(underscoredName, pd); } this.mappedProperties.add(pd.getName()); } } } //1. "game", return "game", unchanged //2. "gameId", return "game_id" private String underscoreName(String name) { //...... }
看完了BeanPropertyRowMapper,回到queryForObject
调用过程:
queryForObject(String sql, Object[] args, RowMapper<T> rowMapper)
query(sql, args, new RowMapperResultSetExtractor<T>(rowMapper, 1))
query(sql, newArgPreparedStatementSetter(args), resultSetExtractor)
query(new SimplePreparedStatementCreator(sql), argPreparedStatementSetter, resultSetExtractor)
query(PreparedStatementCreator psc, final PreparedStatementSetter argPreparedStatementSetter, final ResultSetExtractor<T> resultSetExtractor)
整个过程跟JdbcTemplate的batchUpdate方法是类似的
1.
对于参数'sql',它最终的作用是生成一个PreparedStatement:
connection.prepareStatement(sql);
2.
对sql语句中“?”的赋值,是通过PreparedStatementSetter
需要两个数据:
一是“?”所对应的值,也就是Object[] args
二是PreparedStatement
对第一个数据:
args是在ArgPreparedStatementSetter的构造函数中传入
对第二个数据:
在最后的query方法中,把创建好的PreparedStatement作为匿名内部类PreparedStatementCallback.doInPreparedStatement方法的参数
最后得以传给ArgPreparedStatementSetter:
public <T> T query( PreparedStatementCreator psc, final PreparedStatementSetter argPreparedStatementSetter, final ResultSetExtractor<T> rse) { return execute(psc, new PreparedStatementCallback<T>() { public T doInPreparedStatement(PreparedStatement ps) { //... argPreparedStatementSetter.setValues(ps); ResultSet rsToUse = ps.executeQuery(); return rse.extractData(rsToUse); //... } } ); }
ArgPreparedStatementSetter的setValues方法:
public void setValues(PreparedStatement ps) throws SQLException { if (this.args != null) { for (int i = 0; i < this.args.length; i++) { Object arg = this.args[i]; /* doSetValue方法会根据arg的实际类型去调用ps.setSpecificType(index, arg)方法,例如: if (isStringValue(arg.getClass())) { ps.setString(paramIndex, arg.toString()); } */ doSetValue(ps, i + 1, arg); } } }
最后看一下RowMapperResultSetExtractor
它有两个字段:
private final RowMapper<T> rowMapper;
private final int rowsExpected;
因为是queryForObject,因此rowsExpected=1
extractData方法很直观,是在上面的doInPreparedStatement调用的:
public List<T> extractData(ResultSet rs) throws SQLException { List<T> results = (this.rowsExpected > 0 ? new ArrayList<T>(this.rowsExpected) : new ArrayList<T>()); int rowNum = 0; //rs.next()迭代每一行 while (rs.next()) { results.add(this.rowMapper.mapRow(rs, rowNum++)); } return results; }
从代码可看出,如果ResultSet为空,返回的List也不会是null,而是empty list
这也符合《Effective Java》的提议:返回类型是集合类型时,返回空集合而不是null
发表评论
-
深入纠结maven的资源过滤
2016-05-13 21:47 7131关于maven的资源过滤,官方文档有个例子: <pro ... -
自己动手实现Java Validation
2015-09-18 20:37 10086参数检查用得最多的是J ... -
BeanUtils.copyProperties使用笔记
2015-07-06 22:17 32573BeanUtils.copyProperties VS Pro ... -
重新发明轮子——解析xml并实例化类
2015-03-11 21:00 1772需求如图: 说明: bl ... -
Haproxy+Keepalived高可用双机单活
2015-01-06 17:37 6596我们的应用MyApp不支持集群,但要求双机单活(两台机器:ma ... -
返回null还是empty
2014-05-16 15:35 100第一个问题,函数是应当返回null还是长度为0的数组(或集合) ... -
返回null还是empty
2014-05-16 15:34 2278第一个问题,函数是应当返回null还是长度为0的数组(或集合) ... -
返回null还是empty
2014-05-16 15:34 152第一个问题,函数是应当返回null还是长度为0的数组(或集合) ... -
返回null还是empty
2014-05-16 15:34 114第一个问题,函数是应当返回null还是长度为0的数组(或集合) ... -
返回null还是empty
2014-05-16 15:33 100第一个问题,函数是应当返回null还是长度为0的数组(或集合) ... -
Spring源码学习-XML 配置方式的IoC容器启动过程分析
2014-05-15 18:46 1457以FileSystemXmlApplicationContex ... -
Spring源码学习-JdbcTemplate batchUpdate批量操作
2014-05-07 16:21 18758Spring JdbcTemplate的batch操作最后还是 ... -
Spring源码学习-PropertyPlaceholderHelper
2014-04-25 18:47 2598今天在看Spring 3.0.0.RELEASE的源码,发现P ... -
J2EE设计模式-Intercepting Filter
2013-11-27 16:56 1506Intercepting Filter类似于职责链模式 有两种 ... -
Spring中JdbcDaoSupport的DataSource注入问题
2013-11-19 17:04 3877参考以下两篇文章: http://www.mkyong.com ... -
CAS实现单点登录(SSO)
2013-07-29 18:08 1448参考以下两篇文章,对原作者表示感谢: http://blog. ... -
《重构,改善现有代码的设计》第八章 Duplicate Observed Data
2012-12-04 20:34 1478import java.awt.Color; impor ... -
org.apache.tools.zip实现文件的压缩和解压,支持中文
2012-08-08 19:32 5064刚开始用java.util.Zip,发 ...
相关推荐
spring-jdbcTemplate实例工程
使用spring+springmvc+jdbcTemplate 数据库使用orcal、redis 完成一个webdemo
spring中使用JdbcTemplate操作数据库crud,一图详解(脑图)
Spring-JdbcTemplate
Spring4--3.jdbcTemplate事务
spring-jdbc-4.2.4.RELEASE.jar,spring-tx-4.2.4.RELEASE.jar,jdbcTemplate使用的jar包
Spring-With-JdbcTemplate 使用JdbcTemplate完成CRUD操作的完整代码弹簧需要执行哪些操作从GitHub上导入Eclipse进行Maven安装(Git Pull)#Create DB 删除数据库manishjdbc; 创建数据库manishjdbc; 使用...
本资源是一个完整的通过Servlet-Service-Dao-JdbcTemplate访问MySQL数据库的JavaWeb Project,可以直接导入到Eclipse中进行调试运行,注意默认编译器是JDK1.8。
一个简单的spring的jdbcTemplate扩展
spring持久层,建立持久数据库,spring-jdbc(jdbctemplate)所需jar包 spring持久层,建立持久数据库,spring-jdbc(jdbctemplate)所需jar包
springMVC配置jdbcTemplate连接oracle数据库 所需要的jar包
主要使用Spring MVC框架,实现jdbcTemplate核心组件,利用注解,实现与数据库的连接
spring-jdbc-5.2.7.RELEASE.jar,JdbcTemplate所需要的jar包。
spring 封装了 RedisTemplate,JdbcTemplate 对象来进行对redis,jdbc的各种操作进行简化
Spirng对JDBC进行过良好的封装,通过提供相应的模板和辅助类,在相当的程度上...也就是说,即使系统没有采用Spring作为结构性框架,我们也可以单独使用Spring的JDBC部分(spring-dao.jar)来改善我们的代码······
SSH笔记-Spring JdbcTemplate,使用JdbcTemplate对数据库进行操作,使用具名参数和JDBC模板,简化操作
spring-jdbctemplate-example 这是一个Spring Monolith应用程序示例,该示例使用jdbcTemplate连接MySQL数据库并执行反馈操作的操作。测试和构建运行测试(也运行大型测试) ./gradlew clean test 有条件地根据测试...
29.2. Using JdbcTemplate 29.3. JPA and “Spring Data” 29.3.1. Entity Classes 29.3.2. Spring Data JPA Repositories 29.3.3. Creating and Dropping JPA Databases 29.3.4. Open EntityManager in View 29.4. ...
Spring Security 3.1 +Spring +Servlet+JdbcTemplate 自己找的资料,并开发成功