`

Spring源码学习-JdbcTemplate queryForObject

阅读更多
JdbcTemplate中有两个可能会混淆的queryForObject方法:
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







1
0
分享到:
评论

相关推荐

    spring-springMvc-jdbctemplate.rar

    在本项目中,"spring-springMvc-jdbctemplate.rar" 是一个包含了使用Spring框架、Spring MVC和JdbcTemplate实现的Web应用示例。这个压缩包可能包含了一系列的配置文件、源代码和数据库脚本,旨在展示如何整合这些...

    spring-jdbcTemplate实例工程

    《深入解析Spring JdbcTemplate》 Spring JDBC Template是Spring框架中...这个实例工程为我们提供了一个学习和实践Spring JdbcTemplate的良好平台,通过对其中代码的分析和运行,可以更好地理解和掌握其用法和精髓。

    Spring--JdbcTemplate.pdf

    标题中提到的"JdbcTemplate"是Spring框架中提供的一个用于简化数据库操作的JDBC抽象库。它是对Java标准数据库编程接口JDBC的一种封装,旨在简化JDBC编程,减少样板代码,使得开发者在使用Spring框架时能够更便捷地对...

    spring源码spring-framework-4.3.2.RELEASE

    通过对Spring 4.3.2.RELEASE源码的深入研究,我们可以了解其设计理念,学习到如何优雅地管理依赖、实现面向切面编程,以及如何利用Spring构建高效、健壮的Web应用。同时,源码阅读也能帮助我们理解Spring如何与其他...

    spring-framework-1.0-m2.zip

    Spring的JdbcTemplate提供了一种模板方法模式,避免了繁琐的JDBC代码,降低了错误率,提高了代码的可读性和可维护性。同时,它支持SQL语句的动态构造,使得数据访问更加灵活。 在MVC方面,Spring 1.0-m2提供了初步...

    Spring-JdbcTemplate

    return jdbcTemplate.queryForObject(sql, new Object[]{username}, new UserRowMapper()); } ``` ### 5. JdbcTemplate 与 MyBatis 比较 - **灵活性**:MyBatis 提供了更灵活的 SQL 编写方式,支持 XML 或注解...

    一个简单的spring-jdbctemplate扩展

    Spring的JdbcTemplate是Spring框架中的一个核心组件,用于简化数据库操作。它提供了一种模板方法模式,抽象出常见的JDBC代码,使得开发者可以避免编写大量的重复性代码,从而更加专注于业务逻辑。本项目是对Spring ...

    Spring4--3.jdbcTemplate事务

    文件`spring_3_1`、`spring_3`和`spring_3_2`可能包含了Spring框架关于JdbcTemplate和事务管理的更多示例代码和配置信息,学习这些资源有助于深入理解Spring4中如何有效地利用JdbcTemplate进行事务控制。通过实践,...

    spring-jdbc-4.2.4.RELEASE.jar,spring-tx-4.2.4.RELEASE.jar,jdbcTemplate使用的jar包

    `jdbcTemplate`是Spring JDBC模块的一部分,提供了简化数据库访问的API,使得开发者无需直接操作JDBC API,从而减少错误并提高代码的可维护性。 首先,我们来看`spring-jdbc-4.2.4.RELEASE.jar`。这个jar包包含了...

    Spring-With-JdbcTemplate

    Spring-With-JdbcTemplate 使用JdbcTemplate完成CRUD操作的完整代码弹簧需要执行哪些操作从GitHub上导入Eclipse进行Maven安装(Git Pull)#Create DB 删除数据库manishjdbc; 创建数据库manishjdbc; 使用...

    在Java的Servlet中使用Spring JdbcTemplate-Eclipse版

    本资源是一个完整的通过Servlet-Service-Dao-JdbcTemplate访问MySQL数据库的JavaWeb Project,可以直接导入到Eclipse中进行调试运行,注意默认编译器是JDK1.8。

    spring-framework-5.2.7.RELEASE.zip

    在"spring-framework-5.2.7.RELEASE.zip"这个压缩包中,包含了Spring Framework 5.2.7版本的所有源码、文档和库文件。 Spring 5是该框架的一个重大更新,引入了许多新特性和改进,旨在提高性能、增强可扩展性,并...

    官方原版源码spring-framework-5.2.1.RELEASE.zip

    6. **文档更新**:"spring-5.2.1.RELEASE-docs.zip"包含的文档详细解释了这些新特性和变化,是开发者学习和理解源码的重要资源。 7. **XML配置优化**:Spring 5.2.1继续推动向Java配置的迁移,同时对XML配置进行了...

    spring-jdbc-4.2.xsd.zip

    `spring-jdbc-4.2.xsd`是Spring 4.2版本的JDBC配置XML Schema定义,它定义了一系列元素和属性,用来描述如何配置Spring的JdbcTemplate、NamedParameterJdbcTemplate、SimpleJdbcInsert等核心组件,以及事务管理相关...

    springMVC-jdbcTemplate jar包

    而`jdbcTemplate`是Spring框架的一个核心组件,专为简化数据库操作而设计。在SpringMVC中集成`jdbcTemplate`,可以让我们在处理数据库交互时避免直接编写JDBC代码,减少出错的可能性,并提高代码的可读性和可维护性...

    spring-framework-3.1.2.RELEASE jar包

    例如,它增强了JdbcTemplate,使其更加易用,同时增加了对JPA和Hibernate等ORM框架的支持,使得数据库操作更加简便。 在AOP(面向切面编程)方面,Spring 3.1引入了注解驱动的切面支持,使得开发者可以更容易地定义...

    SpringMVC实现数据库连接--jdbcTemplate

    而jdbcTemplate是Spring框架中的一个核心组件,专门用于简化Java数据库连接(JDBC)操作,提供了一种模板化的查询和更新数据的方法,避免了大量重复的JDBC代码。在本教程中,我们将探讨如何在SpringMVC环境中使用...

    spring-framework-2.5.6jar包

    2.5.6 版本是 Spring 的一个较早版本,发布于2009年,尽管如此,它仍然包含了大量重要的功能和概念,对于学习和理解Spring框架的历史及发展具有参考价值。 首先,Spring框架的核心特性之一是依赖注入(Dependency ...

    官方原版源码spring-framework-5.1.14.RELEASE.zip

    《深入剖析Spring Framework 5.1.14源码》 Spring Framework作为Java开发领域中的基石,其核心...通过阅读和学习源码,我们可以深入探究DI、AOP、Web MVC、数据访问等核心概念,提升我们的编程技巧和解决问题的能力。

Global site tag (gtag.js) - Google Analytics