Spring JDBC查询返回对象代码跟踪
在封装方法的时候突然发现通过 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查询返回对象代码跟踪的更多相关文章
- spring jdbc 查询结果返回对象、对象列表
首先,需要了解spring jdbc查询时,有三种回调方式来处理查询的结果集.可以参考 使用spring的JdbcTemplate进行查询的三种回调方式的比较,写得还不错. 1.返回对象(queryF ...
- HQL查询——查询返回对象类型分析
关于HQL查询,我们可以结合hibernate的API文档,重点围绕org.hibernate.Query接口,分析其方法,此接口的实例对象是通过通过session.对象的creatQuery(Str ...
- Spring JDBC查询数据
以下示例将展示如何使用Spring jdbc进行查询数据记录,将从student表中查询记录. 语法: String selectQuery = "select * from student ...
- spring jdbc查询 依赖JdbcTemplate这个类模版封装JDBC的操作
package cn.itcast.spring.jdbc; import java.util.List; import org.springframework.jdbc.core.support.J ...
- Spring JDBC保存枚举对象含关键字报错原因之一
报错信息: org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized S ...
- Spring Data Jpa 查询返回自定义对象
转载请注明出处:http://www.wangyongkui.com/java-jpa-query. 今天使用Jpa遇到一个问题,发现查询多个字段时返回对象不能自动转换成自定义对象.代码如下: //U ...
- Spring JDBC对象批量操作
以下示例将演示如何使用spring jdbc中的对象进行批量更新.我们将在单次批次操作中更新student表中的记录. student表的结果如下 - CREATE TABLE student( id ...
- Spring JDBC常用方法详细示例
Spring JDBC使用简单,代码简洁明了,非常适合快速开发的小型项目.下面对开发中常用的增删改查等方法逐一示例说明使用方法 1 环境准备 启动MySQL, 创建一个名为test的数据库 创建Mav ...
- Entity Framework Code First实体对象变动跟踪
Entity Framework Code First通过DbContext.ChangeTracker对实体对象的变动进行跟踪,实现跟踪的方式有两种:变动跟踪快照和变动跟踪代理. 变动跟踪快照:前面 ...
随机推荐
- python3使用configparser解析配置文件
http://www.jb51.net/article/87402.htm 需要注意的是每一个字段后面的值外面没有引号,切记,自己第一次配置时,加了引号,搞了半天 没找到错误,, 在用Python做开 ...
- SWIG 多语言接口变换 【转】
一. SWIG 是Simple Wrapper and Interface Generator的缩写,是一个帮助使用C或者C++编写的软件创建其他编语言的API的工具.例如,我 ...
- 分布式服务自增长唯一ID小结
1.常用生成唯一ID的方式,例如UUID 2.生成唯一自自增长ID方式: 例如: Zookeeper的增加ID: redis的incr方法 mongodb的objectId 3.采用雪花模型 如下代码 ...
- Windows无法删除文件 提示找不到该项目怎么办
1 如图所示,我想要删除某个文件,提示如图所示,一般用360的强力删除也不管用. 2 在桌面新建一个文本文档,并输入以下内容.保存为bat格式(比如Delete.bat).然后把这个删不掉的文件拖 ...
- [脚本编程] 过云盾、D盾各种盾shell
作者: dean <?php //过云盾.D盾各种盾shell $id = $_GET['id']; //debug echo $catid = isset($_GET['catid'])?ba ...
- Java 循环结构 - for, while 及 do...while
Java 循环结构 - for, while 及 do...while 顺序结构的程序语句只能被执行一次.如果您想要同样的操作执行多次,,就需要使用循环结构. Java中有三种主要的循环结构: whi ...
- hdu5303(2015多校2)--Delicious Apples(贪心+枚举)
Delicious Apples Time Limit: 5000/3000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Other ...
- C语言之基本算法33—矩阵的基本运算
//矩阵基础 /* ================================================================== 题目:输入矩阵a,b,输出a,b,a的转置矩阵 ...
- Singleton单例模式是最简单的设计模式,它的主要作用是保证在程序执行生命周期中,使用了单类模式的类仅仅能有一个实例对象存在。
...
- jQuery.delegate() 函数详解
delegate()函数用于为指定元素的一个或多个事件绑定事件处理函数. 此外,你还可以额外传递给事件处理函数一些所需的数据. 即使是执行delegate()函数之后新添加的元素,只要它符合条件,绑定 ...