SpringBoot JDBC 源码分析之——NamedParameterJdbcTemplate 查询数据返回bean对象
1,NamedParameterJdbcTemplate 查询列表
/***测试***/
public void queyBeanTest(){
String s = "select * from PT_USER ";
List<PtUser> list = namedJdbcTemplate.query(s, new BeanPropertyRowMapper<PtUser>(PtUser.class));
System.out.println(list);
}
2,如果有参数,期间会把 参数绑定的 例如: :name 替换成 ?
源码:getPreparedStatementCreator 方法。 所以最后还是调用 JdbcTemplate 模板。
3,按源码一直走下去。下面是一个抽象类实现的模板方法。
/**
* Query using a prepared statement, allowing for a PreparedStatementCreator
* and a PreparedStatementSetter. Most other query methods use this method,
* but application code will always work with either a creator or a setter.
* @param psc Callback handler that can create a PreparedStatement given a
* Connection
* @param pss object that knows how to set values on the prepared statement.
* If this is null, the SQL will be assumed to contain no bind parameters.
* @param rse object that will extract results.
* @return an arbitrary result object, as returned by the ResultSetExtractor
* @throws DataAccessException if there is any problem
*/
public <T> T query(
PreparedStatementCreator psc, final PreparedStatementSetter pss, final ResultSetExtractor<T> rse)
throws DataAccessException { Assert.notNull(rse, "ResultSetExtractor must not be null");
logger.debug("Executing prepared SQL query"); return execute(psc, new PreparedStatementCallback<T>() {
@Override
public T doInPreparedStatement(PreparedStatement ps) throws SQLException {
ResultSet rs = null;
try {
if (pss != null) {
pss.setValues(ps);
}
rs = ps.executeQuery();
ResultSet rsToUse = rs;
if (nativeJdbcExtractor != null) {
rsToUse = nativeJdbcExtractor.getNativeResultSet(rs);
}
return rse.extractData(rsToUse);
}
finally {
JdbcUtils.closeResultSet(rs);
if (pss instanceof ParameterDisposer) {
((ParameterDisposer) pss).cleanupParameters();
}
}
}
});
}
4,因为一开始 我们 用的 BeanPropertyRowMapper 类,也就是用这个类来装载返回的数据。
一开始new 的时候会初始化方法。
/**
* Create a new {@code BeanPropertyRowMapper}, accepting unpopulated
* properties in the target bean.
* <p>Consider using the {@link #newInstance} factory method instead,
* which allows for specifying the mapped type once only.
* @param mappedClass the class that each row should be mapped to
*/
public BeanPropertyRowMapper(Class<T> mappedClass) {
initialize(mappedClass);
}
这个方法,其实是把实体类的属性拆分了来存的,例如:userName 存为user_name.
/**
* Initialize the mapping metadata for the given class.
* @param mappedClass the mapped class
*/
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(lowerCaseName(pd.getName()), pd);
String underscoredName = underscoreName(pd.getName());
if (!lowerCaseName(pd.getName()).equals(underscoredName)) {
this.mappedFields.put(underscoredName, pd);
}
this.mappedProperties.add(pd.getName());
}
}
}
下面这个方法是最终实现,会把数据库的字段都转成小写。所以也支持Oracle 。
/**
* Extract the values for all columns in the current row.
* <p>Utilizes public setters and result set metadata.
* @see java.sql.ResultSetMetaData
*/
@Override
public T mapRow(ResultSet rs, int rowNumber) throws SQLException {
Assert.state(this.mappedClass != null, "Mapped class was not specified");
T mappedObject = BeanUtils.instantiateClass(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);
String field = lowerCaseName(column.replaceAll(" ", ""));
PropertyDescriptor pd = this.mappedFields.get(field);
if (pd != null) {
try {
Object value = getColumnValue(rs, index, pd);
if (rowNumber == 0 && logger.isDebugEnabled()) {
logger.debug("Mapping column '" + column + "' to property '" + pd.getName() +
"' of type '" + ClassUtils.getQualifiedName(pd.getPropertyType()) + "'");
}
try {
bw.setPropertyValue(pd.getName(), value);
}
catch (TypeMismatchException ex) {
if (value == null && this.primitivesDefaultedForNullValue) {
if (logger.isDebugEnabled()) {
logger.debug("Intercepted TypeMismatchException for row " + rowNumber +
" and column '" + column + "' with null value when setting property '" +
pd.getName() + "' of type '" +
ClassUtils.getQualifiedName(pd.getPropertyType()) +
"' on object: " + mappedObject, ex);
}
}
else {
throw ex;
}
}
if (populatedProperties != null) {
populatedProperties.add(pd.getName());
}
}
catch (NotWritablePropertyException ex) {
throw new DataRetrievalFailureException(
"Unable to map column '" + column + "' to property '" + pd.getName() + "'", ex);
}
}
else {
// No PropertyDescriptor found
if (rowNumber == 0 && logger.isDebugEnabled()) {
logger.debug("No property found for column '" + column + "' mapped to field '" + field + "'");
}
}
} 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.getName() + "]: " +
this.mappedProperties);
} return mappedObject;
}
总结: 因为源码就是这样写死的。所以要约定装配实体类。
SpringBoot JDBC 源码分析之——NamedParameterJdbcTemplate 查询数据返回bean对象的更多相关文章
- Solr4.8.0源码分析(5)之查询流程分析总述
Solr4.8.0源码分析(5)之查询流程分析总述 前面已经写到,solr查询是通过http发送命令,solr servlet接受并进行处理.所以solr的查询流程从SolrDispatchsFilt ...
- Mybatis源码分析--关联表查询及延迟加载原理(二)
在上一篇博客Mybatis源码分析--关联表查询及延迟加载(一)中我们简单介绍了Mybatis的延迟加载的编程,接下来我们通过分析源码来分析一下Mybatis延迟加载的实现原理. 其实简单来说Myba ...
- 【原创】005 | 搭上SpringBoot请求处理源码分析专车
前言 如果这是你第二次看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 专车介绍 该趟专车是开往Spring Boot请求处理源码分析专车,主要用来分析S ...
- HDFS源码分析心跳汇报之数据块汇报
在<HDFS源码分析心跳汇报之数据块增量汇报>一文中,我们详细介绍了数据块增量汇报的内容,了解到它是时间间隔更长的正常数据块汇报周期内一个smaller的数据块汇报,它负责将DataNod ...
- Django之DRF源码分析(二)---数据校验部分
Django之DRF源码分析(二)---数据校验部分 is_valid() 源码 def is_valid(self, raise_exception=False): assert not hasat ...
- 【原创】002 | 搭上SpringBoot事务源码分析专车
前言 如果这是你第二次看到师长,说明你在觊觎我的美色! 点赞+关注再看,养成习惯 没别的意思,就是需要你的窥屏^_^ 专车介绍** 该趟专车是开往Spring Boot事务源码分析的专车 专车问题 为 ...
- Spring源码分析(十八)创建bean
本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 目录 一.创建bean的实例 1. autowireConstructor 2 ...
- HDFS源码分析心跳汇报之数据块增量汇报
在<HDFS源码分析心跳汇报之BPServiceActor工作线程运行流程>一文中,我们详细了解了数据节点DataNode周期性发送心跳给名字节点NameNode的BPServiceAct ...
- 源码分析:Exchanger之数据交换器
简介 Exchanger是Java5 开始引入的一个类,它允许两个线程之间交换持有的数据.当Exchanger在一个线程中调用exchange方法之后,会阻塞等待另一个线程调用同样的exchange方 ...
随机推荐
- 针对IE6,IE7,IE8,IE9,FF等不同浏览器的CSS写法
首先我们介绍一下HACK原理,就是不同浏览器对字符的识别不同 在 CSS中常用特殊字符识别表: (1)*: IE6+IE7都能识别*,而标准浏览器FF+IE8是不能识别*的; (2)!importan ...
- 20155228 实验一《Java开发环境的熟悉》实验报告
20155228 实验一<Java开发环境的熟悉>实验报告 实验内容 使用JDK编译.运行简单的Java程序: 使用IDEA 编辑.编译.运行.调试Java程序. 实验要求 没有Linux ...
- RNN实例
https://blog.csdn.net/jmh1996/article/details/78821216
- Django之真正创建一个django项目
真正创建一个django项目 1 创建Django项目 :new-project 2 创建APP : python manager.py startapp app01 3 setting 配 ...
- python isinstance()方法的使用
1. 描述Python中的 isinstance() 函数,是Python中的一个内置函数,用来判断一个函数是否是一个已知的类型,类似 type(). 2. 语法isinstance(object,c ...
- 拜占庭将军问题(Byzantine Generals Problem),一个关于分布式系统容错问题故事
拜占庭将军问题(Byzantine Generals Problem),一个关于分布式系统容错问题故事 背景:拜占庭帝国派出10支军队,去包围进攻一个强大的敌人,至少6支军队同时进攻才能攻下敌国. 难 ...
- window、linux安装jdk,excel 导入oracle,WebService,window 端口查看,svn服务安装,oracle用户解锁
内存泄露分析插件http://download.eclipse.org/mat/1.3/update-site/birt插件http://download.eclipse.org//birt/upda ...
- shell脚本和python脚本实现批量ping IP测试
先建一个存放ip列表的txt文件: [root@yysslopenvpn01 ~]# cat hostip.txt 192.168.130.1 192.168.130.2 192.168.130.3 ...
- Java动态菜单添加
自己做出来的添加数据库配置好的动态菜单的方法 private void createMenu() { IMenuDAO dao = new MenuDAOImpl(); String sql1 = ...
- ansible中的playbook详解
首先简单说明一下playbook,playbook是什么呢?根本上说playbook和shell脚本没有任何的区别,playbook就像shell一样,也是把一堆的命令组合起来,然后加入对应条件判断等 ...