- 浏览: 787253 次
- 性别:
- 来自: 深圳
文章分类
最新评论
-
萨琳娜啊:
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 7214关于maven的资源过滤,官方文档有个例子: <pro ... -
自己动手实现Java Validation
2015-09-18 20:37 10139参数检查用得最多的是J ... -
BeanUtils.copyProperties使用笔记
2015-07-06 22:17 32636BeanUtils.copyProperties VS Pro ... -
重新发明轮子——解析xml并实例化类
2015-03-11 21:00 1819需求如图: 说明: bl ... -
Haproxy+Keepalived高可用双机单活
2015-01-06 17:37 6642我们的应用MyApp不支持集群,但要求双机单活(两台机器:ma ... -
返回null还是empty
2014-05-16 15:35 100第一个问题,函数是应当返回null还是长度为0的数组(或集合) ... -
返回null还是empty
2014-05-16 15:34 2331第一个问题,函数是应当返回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 1495以FileSystemXmlApplicationContex ... -
Spring源码学习-JdbcTemplate batchUpdate批量操作
2014-05-07 16:21 18858Spring JdbcTemplate的batch操作最后还是 ... -
Spring源码学习-PropertyPlaceholderHelper
2014-04-25 18:47 2638今天在看Spring 3.0.0.RELEASE的源码,发现P ... -
J2EE设计模式-Intercepting Filter
2013-11-27 16:56 1541Intercepting Filter类似于职责链模式 有两种 ... -
Spring中JdbcDaoSupport的DataSource注入问题
2013-11-19 17:04 3922参考以下两篇文章: http://www.mkyong.com ... -
CAS实现单点登录(SSO)
2013-07-29 18:08 1487参考以下两篇文章,对原作者表示感谢: http://blog. ... -
《重构,改善现有代码的设计》第八章 Duplicate Observed Data
2012-12-04 20:34 1523import java.awt.Color; impor ... -
org.apache.tools.zip实现文件的压缩和解压,支持中文
2012-08-08 19:32 5137刚开始用java.util.Zip,发 ...
相关推荐
在本项目中,"spring-springMvc-jdbctemplate.rar" 是一个包含了使用Spring框架、Spring MVC和JdbcTemplate实现的Web应用示例。这个压缩包可能包含了一系列的配置文件、源代码和数据库脚本,旨在展示如何整合这些...
《深入解析Spring JdbcTemplate》 Spring JDBC Template是Spring框架中...这个实例工程为我们提供了一个学习和实践Spring JdbcTemplate的良好平台,通过对其中代码的分析和运行,可以更好地理解和掌握其用法和精髓。
标题中提到的"JdbcTemplate"是Spring框架中提供的一个用于简化数据库操作的JDBC抽象库。它是对Java标准数据库编程接口JDBC的一种封装,旨在简化JDBC编程,减少样板代码,使得开发者在使用Spring框架时能够更便捷地对...
通过对Spring 4.3.2.RELEASE源码的深入研究,我们可以了解其设计理念,学习到如何优雅地管理依赖、实现面向切面编程,以及如何利用Spring构建高效、健壮的Web应用。同时,源码阅读也能帮助我们理解Spring如何与其他...
Spring的JdbcTemplate提供了一种模板方法模式,避免了繁琐的JDBC代码,降低了错误率,提高了代码的可读性和可维护性。同时,它支持SQL语句的动态构造,使得数据访问更加灵活。 在MVC方面,Spring 1.0-m2提供了初步...
return jdbcTemplate.queryForObject(sql, new Object[]{username}, new UserRowMapper()); } ``` ### 5. JdbcTemplate 与 MyBatis 比较 - **灵活性**:MyBatis 提供了更灵活的 SQL 编写方式,支持 XML 或注解...
Spring的JdbcTemplate是Spring框架中的一个核心组件,用于简化数据库操作。它提供了一种模板方法模式,抽象出常见的JDBC代码,使得开发者可以避免编写大量的重复性代码,从而更加专注于业务逻辑。本项目是对Spring ...
文件`spring_3_1`、`spring_3`和`spring_3_2`可能包含了Spring框架关于JdbcTemplate和事务管理的更多示例代码和配置信息,学习这些资源有助于深入理解Spring4中如何有效地利用JdbcTemplate进行事务控制。通过实践,...
`jdbcTemplate`是Spring JDBC模块的一部分,提供了简化数据库访问的API,使得开发者无需直接操作JDBC API,从而减少错误并提高代码的可维护性。 首先,我们来看`spring-jdbc-4.2.4.RELEASE.jar`。这个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-framework-5.2.7.RELEASE.zip"这个压缩包中,包含了Spring Framework 5.2.7版本的所有源码、文档和库文件。 Spring 5是该框架的一个重大更新,引入了许多新特性和改进,旨在提高性能、增强可扩展性,并...
6. **文档更新**:"spring-5.2.1.RELEASE-docs.zip"包含的文档详细解释了这些新特性和变化,是开发者学习和理解源码的重要资源。 7. **XML配置优化**:Spring 5.2.1继续推动向Java配置的迁移,同时对XML配置进行了...
`spring-jdbc-4.2.xsd`是Spring 4.2版本的JDBC配置XML Schema定义,它定义了一系列元素和属性,用来描述如何配置Spring的JdbcTemplate、NamedParameterJdbcTemplate、SimpleJdbcInsert等核心组件,以及事务管理相关...
而`jdbcTemplate`是Spring框架的一个核心组件,专为简化数据库操作而设计。在SpringMVC中集成`jdbcTemplate`,可以让我们在处理数据库交互时避免直接编写JDBC代码,减少出错的可能性,并提高代码的可读性和可维护性...
例如,它增强了JdbcTemplate,使其更加易用,同时增加了对JPA和Hibernate等ORM框架的支持,使得数据库操作更加简便。 在AOP(面向切面编程)方面,Spring 3.1引入了注解驱动的切面支持,使得开发者可以更容易地定义...
而jdbcTemplate是Spring框架中的一个核心组件,专门用于简化Java数据库连接(JDBC)操作,提供了一种模板化的查询和更新数据的方法,避免了大量重复的JDBC代码。在本教程中,我们将探讨如何在SpringMVC环境中使用...
2.5.6 版本是 Spring 的一个较早版本,发布于2009年,尽管如此,它仍然包含了大量重要的功能和概念,对于学习和理解Spring框架的历史及发展具有参考价值。 首先,Spring框架的核心特性之一是依赖注入(Dependency ...
《深入剖析Spring Framework 5.1.14源码》 Spring Framework作为Java开发领域中的基石,其核心...通过阅读和学习源码,我们可以深入探究DI、AOP、Web MVC、数据访问等核心概念,提升我们的编程技巧和解决问题的能力。