在封装方法的时候突然发现通过 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))

于是跟踪到

  1. public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
  2. Assert.notNull(sql, "SQL must not be null");
  3. Assert.notNull(rse, "ResultSetExtractor must not be null");
  4. if (logger.isDebugEnabled()) {
  5. logger.debug("Executing SQL query [" + sql + "]");
  6. }
  7. class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
  8. public T doInStatement(Statement stmt) throws SQLException {
  9. ResultSet rs = null;
  10. try {
  11. rs = stmt.executeQuery(sql);
  12. ResultSet rsToUse = rs;
  13. if (nativeJdbcExtractor != null) {
  14. rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
  15. }
  16. return rse.extractData(rsToUse);
  17. }
  18. finally {
  19. JdbcUtils.closeResultSet(rs);
  20. }
  21. }
  22. public String getSql() {
  23. return sql;
  24. }
  25. }
  26. return execute(new QueryStatementCallback());
  27. }

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

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

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

  1. public List<T> extractData(ResultSet rs) throws SQLException {
  2. List<T> results = (this.rowsExpected > 0 ? new ArrayList<T>(this.rowsExpected) : new ArrayList<T>());
  3. int rowNum = 0;
  4. while (rs.next()) {
  5. results.add(this.rowMapper.mapRow(rs, rowNum++));
  6. }
  7. return results;
  8. }

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

  1. public BeanPropertyRowMapper(Class<T> mappedClass) {
  2. initialize(mappedClass);
  3. }
  4. protected void initialize(Class<T> mappedClass) {
  5. this.mappedClass = mappedClass;
  6. this.mappedFields = new HashMap<String, PropertyDescriptor>();
  7. this.mappedProperties = new HashSet<String>();
  8. PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(mappedClass);
  9. for (PropertyDescriptor pd : pds) {
  10. if (pd.getWriteMethod() != null) {
  11. this.mappedFields.put(pd.getName().toLowerCase(), pd);
  12. String underscoredName = underscoreName(pd.getName());
  13. if (!pd.getName().toLowerCase().equals(underscoredName)) {
  14. this.mappedFields.put(underscoredName, pd);
  15. }
  16. this.mappedProperties.add(pd.getName());
  17. }
  18. }
  19. }

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

  1. public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
  2. Assert.state(this.mappedClass != null, "Mapped class was not specified");
  3. T mappedObject = BeanUtils.instantiate(this.mappedClass);
  4. BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(mappedObject);
  5. initBeanWrapper(bw);
  6.  
  7. ResultSetMetaData rsmd = rs.getMetaData();
  8. int columnCount = rsmd.getColumnCount();
  9. Set<String> populatedProperties = (isCheckFullyPopulated() ? new HashSet<String>() : null);
  10.  
  11. for (int index = 1; index <= columnCount; index++) {
  12. String column = JdbcUtils.lookupColumnName(rsmd, index);
  13. PropertyDescriptor pd = this.mappedFields.get(column.replaceAll(" ", "").toLowerCase());
  14. if (pd != null) {
  15. try {
  16. Object value = getColumnValue(rs, index, pd);
  17. if (logger.isDebugEnabled() && rowNumber == 0) {
  18. logger.debug("Mapping column '" + column + "' to property '" +
  19. pd.getName() + "' of type " + pd.getPropertyType());
  20. }
  21. try {
  22. bw.setPropertyValue(pd.getName(), value);
  23. }
  24. catch (TypeMismatchException e) {
  25. if (value == null && primitivesDefaultedForNullValue) {
  26. logger.debug("Intercepted TypeMismatchException for row " + rowNumber +
  27. " and column '" + column + "' with value " + value +
  28. " when setting property '" + pd.getName() + "' of type " + pd.getPropertyType() +
  29. " on object: " + mappedObject);
  30. }
  31. else {
  32. throw e;
  33. }
  34. }
  35. if (populatedProperties != null) {
  36. populatedProperties.add(pd.getName());
  37. }
  38. }
  39. catch (NotWritablePropertyException ex) {
  40. throw new DataRetrievalFailureException(
  41. "Unable to map column " + column + " to property " + pd.getName(), ex);
  42. }
  43. }
  44. }
  45.  
  46. if (populatedProperties != null && !populatedProperties.equals(this.mappedProperties)) {
  47. throw new InvalidDataAccessApiUsageException("Given ResultSet does not contain all fields " +
  48. "necessary to populate object of class [" + this.mappedClass + "]: " + this.mappedProperties);
  49. }
  50.  
  51. return mappedObject;
  52. }

  从该方法中可以看到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. python3使用configparser解析配置文件

    http://www.jb51.net/article/87402.htm 需要注意的是每一个字段后面的值外面没有引号,切记,自己第一次配置时,加了引号,搞了半天 没找到错误,, 在用Python做开 ...

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

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

  3. 分布式服务自增长唯一ID小结

    1.常用生成唯一ID的方式,例如UUID 2.生成唯一自自增长ID方式: 例如: Zookeeper的增加ID: redis的incr方法 mongodb的objectId 3.采用雪花模型 如下代码 ...

  4. Windows无法删除文件 提示找不到该项目怎么办

    1 如图所示,我想要删除某个文件,提示如图所示,一般用360的强力删除也不管用.   2 在桌面新建一个文本文档,并输入以下内容.保存为bat格式(比如Delete.bat).然后把这个删不掉的文件拖 ...

  5. [脚本编程] 过云盾、D盾各种盾shell

    作者: dean <?php //过云盾.D盾各种盾shell $id = $_GET['id']; //debug echo $catid = isset($_GET['catid'])?ba ...

  6. Java 循环结构 - for, while 及 do...while

    Java 循环结构 - for, while 及 do...while 顺序结构的程序语句只能被执行一次.如果您想要同样的操作执行多次,,就需要使用循环结构. Java中有三种主要的循环结构: whi ...

  7. hdu5303(2015多校2)--Delicious Apples(贪心+枚举)

    Delicious Apples Time Limit: 5000/3000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Other ...

  8. C语言之基本算法33—矩阵的基本运算

    //矩阵基础 /* ================================================================== 题目:输入矩阵a,b,输出a,b,a的转置矩阵 ...

  9. Singleton单例模式是最简单的设计模式,它的主要作用是保证在程序执行生命周期中,使用了单类模式的类仅仅能有一个实例对象存在。

                                                                                                        ...

  10. jQuery.delegate() 函数详解

    delegate()函数用于为指定元素的一个或多个事件绑定事件处理函数. 此外,你还可以额外传递给事件处理函数一些所需的数据. 即使是执行delegate()函数之后新添加的元素,只要它符合条件,绑定 ...