在封装方法的时候突然发现通过 ResultSetMetaData的getColumnCount()获取到的列明会多一列(ROWSTAT),而且每次的值都是1,目前没有找到相关信息,在国外网站上看到有类似的情况,但是都没有人回答。于是想到spring 的JDBC部分是怎么实现映射的,于是通过spring的源代码发现了大致的流程:

(这里先说明一下自己得到收获:spring的query查询返回对象T的方法是首先获取要返回对象的所有的writeMethod,也就是set方法,然后存放在一个PropertyDescriptor的数组中,然后把有set方法的字段存放在一个类型为Map(String,String)的mappedFields变量中,通过ResultSetMetaData的getColumnCount获取列数,然后遍历列数获取列的名称,通过列的名称从mappedFields中获取该字段的set方法进行对象属性赋值。)

这里我是的方法入口是query(sql, new BeanPropertyRowMapper(voClass))

于是跟踪到

public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
Assert.notNull(sql, "SQL must not be null");
Assert.notNull(rse, "ResultSetExtractor must not be null");
if (logger.isDebugEnabled()) {
logger.debug("Executing SQL query [" + sql + "]");
}
class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
public T doInStatement(Statement stmt) throws SQLException {
ResultSet rs = null;
try {
rs = stmt.executeQuery(sql);
ResultSet rsToUse = rs;
if (nativeJdbcExtractor != null) {
rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
}
return rse.extractData(rsToUse);
}
finally {
JdbcUtils.closeResultSet(rs);
}
}
public String getSql() {
return sql;
}
}
return execute(new QueryStatementCallback());
}

  该方法返回就已经是T对象了,可以看到是return rse.extractData(rsToUse);这个代码起了作用,于是继续跟踪extractData方法,这里发现参数ResultSetExtractor<T> rse是一个接口,于是返回调用query方法的上一层发现代码如下:

public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException {
return query(sql, new RowMapperResultSetExtractor<T>(rowMapper));
}

  参数是RowMapperResultSetMapper对象,继续进入该对象内部查看extractData方法:

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;
while (rs.next()) {
results.add(this.rowMapper.mapRow(rs, rowNum++));
}
return results;
}

  发现是RowMapper的mapRow方法把一行记录映射成一个对象了,这里的rowMaper是一个接口,于是需要我们返回上层看看谁实现了该接口,于是发现在我们调用的地方传入了一个new BeanPropertyRowMapper(voClass)对象,到这个对象内部看看,首先看到构造函数:

public BeanPropertyRowMapper(Class<T> mappedClass) {
initialize(mappedClass);
}
protected void initialize(Class<T> mappedClass) {
this.mappedClass = mappedClass;
this.mappedFields = new HashMap<String, PropertyDescriptor>();
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());
}
}
}

  从构造函数中看出是通过initialize方法来实现voClass中字段和set方法保存在mappedFields和pds的变量中,然后我们再找mapRow这个方法,该方法就是设置vo的属性值

public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
Assert.state(this.mappedClass != null, "Mapped class was not specified");
T mappedObject = BeanUtils.instantiate(this.mappedClass);
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
initBeanWrapper(bw); 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);
if (logger.isDebugEnabled() && rowNumber == 0) {
logger.debug("Mapping column '" + column + "' to property '" +
pd.getName() + "' of type " + pd.getPropertyType());
}
try {
bw.setPropertyValue(pd.getName(), value);
}
catch (TypeMismatchException e) {
if (value == null && primitivesDefaultedForNullValue) {
logger.debug("Intercepted TypeMismatchException for row " + rowNumber +
" and column '" + column + "' with value " + value +
" when setting property '" + pd.getName() + "' of type " + pd.getPropertyType() +
" on object: " + mappedObject);
}
else {
throw e;
}
}
if (populatedProperties != null) {
populatedProperties.add(pd.getName());
}
}
catch (NotWritablePropertyException ex) {
throw new DataRetrievalFailureException(
"Unable to map column " + column + " to property " + pd.getName(), ex);
}
}
} 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;
}

  从该方法中可以看到spring是先获取列明,根据列明找到字段,通过字段的set方法为vo设置值。这个就是spring返回对象的流程。

Spring JDBC查询返回对象代码跟踪的更多相关文章

  1. spring jdbc 查询结果返回对象、对象列表

    首先,需要了解spring jdbc查询时,有三种回调方式来处理查询的结果集.可以参考 使用spring的JdbcTemplate进行查询的三种回调方式的比较,写得还不错. 1.返回对象(queryF ...

  2. HQL查询——查询返回对象类型分析

    关于HQL查询,我们可以结合hibernate的API文档,重点围绕org.hibernate.Query接口,分析其方法,此接口的实例对象是通过通过session.对象的creatQuery(Str ...

  3. Spring JDBC查询数据

    以下示例将展示如何使用Spring jdbc进行查询数据记录,将从student表中查询记录. 语法: String selectQuery = "select * from student ...

  4. spring jdbc查询 依赖JdbcTemplate这个类模版封装JDBC的操作

    package cn.itcast.spring.jdbc; import java.util.List; import org.springframework.jdbc.core.support.J ...

  5. Spring JDBC保存枚举对象含关键字报错原因之一

    报错信息: org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized S ...

  6. Spring Data Jpa 查询返回自定义对象

    转载请注明出处:http://www.wangyongkui.com/java-jpa-query. 今天使用Jpa遇到一个问题,发现查询多个字段时返回对象不能自动转换成自定义对象.代码如下: //U ...

  7. Spring JDBC对象批量操作

    以下示例将演示如何使用spring jdbc中的对象进行批量更新.我们将在单次批次操作中更新student表中的记录. student表的结果如下 - CREATE TABLE student( id ...

  8. Spring JDBC常用方法详细示例

    Spring JDBC使用简单,代码简洁明了,非常适合快速开发的小型项目.下面对开发中常用的增删改查等方法逐一示例说明使用方法 1 环境准备 启动MySQL, 创建一个名为test的数据库 创建Mav ...

  9. Entity Framework Code First实体对象变动跟踪

    Entity Framework Code First通过DbContext.ChangeTracker对实体对象的变动进行跟踪,实现跟踪的方式有两种:变动跟踪快照和变动跟踪代理. 变动跟踪快照:前面 ...

随机推荐

  1. docker运行mysql

    http://blog.csdn.net/u011492260/article/details/77970445 第一步: 安装Docker:首先到docker官网下载适合自己电脑当前系统的版本,并安 ...

  2. SWIG 多语言接口变换 【转】

    一.             SWIG 是Simple Wrapper and Interface Generator的缩写,是一个帮助使用C或者C++编写的软件创建其他编语言的API的工具.例如,我 ...

  3. .Net普通三层->工厂模式->线程内唯一+单元工作模式->WebService分布式三层

    在软件世界分层的思想无处不在 主要是为了提高软件系统的维护性,扩展性,复用性和解耦等 软件的三层构架是一种最基本的分层思想的体现 结构图大体如下: 如此一来,开发人员可以只关注其中一层,而无需关心下一 ...

  4. NFS网络文件系统的配置

    NFS网络文件系统的配置 NFS网络文件系统 NFS(network file system)网络文件系统.一种使用于分散式文件协定,有SUN公司开发.功能是通过网络让不同的机器.不同的操作系统能够分 ...

  5. VB,Visual Basic如何修改代码文本大小和字体

    工具-选项-编辑器格式   修改之后效果如图所示

  6. Swif语法基础 要点归纳(一)

    常量和变量 用let声明常量      let m = 20 用var声明变量      var n = 0 类型推导机制           声明常量或变量时.能够不指定常量/变量类型,编译器会依据 ...

  7. 【VBA】获取Excle的安装路径

    在VBA中,如何获取Excle的安装路径呢?请看以下代码: Sub 获取Excle的安装路径() MsgBox "Excle的安装路径为:" & Application.P ...

  8. Codeforces548E:Mike and Foam

    Mike is a bartender at Rico's bar. At Rico's, they put beer glasses in a special shelf. There are n  ...

  9. JAVA学习第四十八课 — IO流(二):文件的复制 &amp; 缓冲区1

    一.复制文本文件 将G盘的文本文件拷贝到D盘上 也就是 读取G盘中文本文件的数据.写入D盘中->连读带写 而剪切呢.就是连读带写后,删除原盘的文件 <span style="fo ...

  10. C#中的里氏替换原则

    里氏转换原则 子类可以赋值给父类对象 父类对象可以强制转化为对应的子类对象 里氏替换原则直观理解就是"子类是父类",反过来就说不通了. 就像男人是人对的,但人是男人就不对了. 这样 ...