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对实体对象的变动进行跟踪,实现跟踪的方式有两种:变动跟踪快照和变动跟踪代理. 变动跟踪快照:前面 ...
随机推荐
- 微博(MicroBlog)
ylbtech_Miscellaneos Inner 新浪微博 www.weibo.com 搜狐微博 http://t.sohu.com 网易微博 http://t.163.com/session ...
- 41个linux命令大全(鸟哥的私房菜)
转http://www.xmws.cn/show-87-419-1.html 41个linux命令大全 发布作者:微思网络 发布时间:2017-01-10 浏览量:709次 学过linux的人 ...
- [转载]JAVA调用Shell脚本
FROM:http://blog.csdn.net/jj12345jj198999/article/details/11891701 在实际项目中,JAVA有时候需要调用C写出来的东西,除了JNI以外 ...
- CentOS6.4 安装 codeblocks-12.11
FROM: http://blog.csdn.net/theegao/article/details/8750239 一.下载 1. codeblocks-12.11-1.el6.x86_64.t ...
- 常见的Linux系统监控命令
1.free 显示当前系统未使用的和已使用的内存数目,还可以显示被内核使用的内存缓冲区 -b:以Byte为单位显示内存使用情况: -k:以KB为单位显示内存使用情况: -m:以MB为单位显示内存使 ...
- Zynq Fatfs文件系统应用笔记
Zynq Fatfs文件系统应用笔 Hello,panda 笔记介绍基于所描写叙述的Zynq Fatfs基于Xilinx xilffsv3.0和Sdpsv2.4,文件系统採用在Bare-Metal和轻 ...
- 内核中bitmap的使用
在编写应用层代码中使用位图,发现内核中已经有现成的实现便使用之.对位图的使用主要是几个 关键API. 第一:bitmap_zero函数用于初始化位图 源码如下: /* *@dst: 位图的起始地址 * ...
- Android学习(十六) 通过GestureDetector进行手势识别
一.手势交互过程: 1)触屏时,触发MotionEvent事件. 2)被OnTouchListener监听,在onTouch()中获得MotionEvent对象. 3)GestureDetector转 ...
- 一扫天下——ZXing使用全解析
一扫天下--ZXing使用全解析 二维码如今已经烂App了,无论什么App.没有二维码就好像低人一等了. 所以,在自己的项目中集成二维码功能还是非常有必要的. 网上非常多都是基于ZXing2.3的.可 ...
- javascript 数组 find
find() 方法返回通过测试(函数内判断)的数组的第一个元素的值. let arr = [1,2,3,4] console.log(arr.find(i => {return i>1}) ...