Mybatis源码分析之(五)mapper如何将数据库数据转换成java对象的

本篇对mybatis从取到数据库数据开始到映射成对象并返回的过程进行了详细的分析。

转换ResultSet成java对象

下面的代码是PreparedStatementHandler中的

  @Overridepublic <E> Cursor<E> queryCursor(Statement statement) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;//这里是执行sql并得到ResultSetps.execute();//这里是真正处理ResultSet,将之转换成java对象的地方return resultSetHandler.<E> handleCursorResultSets(ps);}

//resultSetHandler是DefaultResultSetHandler类型的

 @Overridepublic List<Object> handleResultSets(Statement stmt) throws SQLException {ErrorContext.instance().activity("handling results").object(mappedStatement.getId());final List<Object> multipleResults = new ArrayList<Object>();int resultSetCount = 0;//把statement中的ResultSet用ResultSetWrapper包装起来(里面有处理类对象的,列名,类名,列名映射等字段)ResultSetWrapper rsw = getFirstResultSet(stmt);//获得mappedStatement中的ResultMaps(这个在初始化configuration的时候就已经生成好了,这里只是取出来)List<ResultMap> resultMaps = mappedStatement.getResultMaps();int resultMapCount = resultMaps.size();//如果rsw不为空,但是resultMapCount小于1就报错(很容易理解,就是有返回结果,但是没有映射,所以mybatis不知道怎么把结果转换成java类,当然就报错了)validateResultMapsCount(rsw, resultMapCount);while (rsw != null && resultMapCount > resultSetCount) {//拿到resultMapResultMap resultMap = resultMaps.get(resultSetCount);//处理ResultSet,将处理好的结果存到multipleResults(具体看下面的函数)handleResultSet(rsw, resultMap, multipleResults, null);//从上面这个函数出来的时候,multipleResults已经是存有结果的list了//若stmt还有下一个ResultSet,则继续循环rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}//若你的mapper配置文件中配了这个ResultSets属性的话就取出来往下面走(不过一般都不会用到,最少LZ到现在还没有用过)String[] resultSets = mappedStatement.getResultSets();if (resultSets != null) {//不常用就不细细分析了,看完这整篇后,你对mybatis已经有了比较清晰的逻辑了while (rsw != null && resultSetCount < resultSets.length) {ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);if (parentMapping != null) {String nestedResultMapId = parentMapping.getNestedResultMapId();ResultMap resultMap = configuration.getResultMap(nestedResultMapId);handleResultSet(rsw, resultMap, null, parentMapping);}rsw = getNextResultSet(stmt);cleanUpAfterHandlingResultSet();resultSetCount++;}}//最后返回结果集return collapseSingleResultList(multipleResults);}//处理过程private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {try {//如果有parentMapping则走下面if (parentMapping != null) {//进行处理handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);} else {//若resultHandler为空则走下面if (resultHandler == null) {//初始化一个DefaultResultHandlerDefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);//进行处理handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);//multipleResults中加defaultResultHandler里面的结果集multipleResults.add(defaultResultHandler.getResultList());} else {//进行处理handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);}}} finally {// issue #228 (close resultsets)//最后关闭ResultSet回到handleResultSetscloseResultSet(rsw.getResultSet());}}//其实最后走的都是这个函数public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {//看是否有嵌套的ResultMap,(比如使用了association, collection标签)if (resultMap.hasNestedResultMaps()) {ensureNoRowBounds();checkResultHandler();//处理嵌套ResultMaphandleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);} else {//处理简单的ResultMap(以这个为例子)handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);}}//处理简单的ResultMapprivate void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)throws SQLException {DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();//若有分页,则先跳过前面的行数(因为mybatis自带的分页是内存分页,全部数据都取到服务器在进行分页的)skipRows(rsw.getResultSet(), rowBounds);while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {//若要继续处理列并且resultSet还有列则处理//这步拿到要进行处理的resultmapResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);//获得整行的数据,根据map映射成java对象Object rowValue = getRowValue(rsw, discriminatedResultMap);//把结果存好,存在resultHandler中的list中(DefaultResultHandler中有一个字段 List<Object> list用来存储这个结果)storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());}}

新建对象并赋值

  private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {final ResultLoaderMap lazyLoader = new ResultLoaderMap();//创建一个对象来接受数据(根据配置生成相应的对象)Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {final MetaObject metaObject = configuration.newMetaObject(rowValue);boolean foundValues = this.useConstructorMappings;//看是否需要自动映射if (shouldApplyAutomaticMappings(resultMap, false)) {//自动映射,把结果放到metaObject的originalObject也就是rowValue中(具体函数看下方)foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;}//通过属性映射来找foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;foundValues = lazyLoader.size() > 0 || foundValues;//看这段貌似可以在configuration中设置若找不到就实例化一个初始的对象rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;}//返回解析好的行值return rowValue;}

通过mapping映射属性

  private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {//这个是自动获取mapping,也是比较重要的函数(下方有具体的分析)List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);boolean foundValues = false;if (autoMapping.size() > 0) {for (UnMappedColumnAutoMapping mapping : autoMapping) {//遍历autoMapping//从resultSet中解析出值//解析的方法很简单,就不跟了,大概就是调用typeHandler中的get函数,取出和列名相对应的值就行了final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);if (value != null) {//value不为空说明找到了foundValues = true;}if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {// gcode issue #377, call setter on nulls (value is not 'found')//找到了就把值设置到 metaObject中的objectWrapper中的object中。其实就是metaObject中的originalObjectmetaObject.setValue(mapping.property, value);}}}//返回是否找到return foundValues;}

获取Mapping

//创建自动映射的mappingprivate List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {final String mapKey = resultMap.getId() + ":" + columnPrefix;看缓存里有没有List<UnMappedColumnAutoMapping> autoMapping = autoMappingsCache.get(mapKey);//没有就自己造if (autoMapping == null) {//存放映射的UnMappedColumnAutoMapping(一个对应一个字段映射)autoMapping = new ArrayList<UnMappedColumnAutoMapping>();//通过resultSetWrapper来创建未映射的字段的映射(具体方法看下面)final List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);for (String columnName : unmappedColumnNames) {//轮询unmappedColumnNamesString propertyName = columnName;if (columnPrefix != null && !columnPrefix.isEmpty()) {// When columnPrefix is specified,// ignore columns without the prefix.if (columnName.toUpperCase(Locale.ENGLISH).startsWith(columnPrefix)) {propertyName = columnName.substring(columnPrefix.length());} else {continue;}}//通过metaObject找到属性final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());//如果属性不为空且有Set方法的话进入方法if (property != null && metaObject.hasSetter(property)) {//查看该字段set的是什么类型final Class<?> propertyType = metaObject.getSetterType(property);//看typeHandlerRegistry有没有处理这种类型的handle,有的话继续if (typeHandlerRegistry.hasTypeHandler(propertyType, rsw.getJdbcType(columnName))) {//获取TypeHandlerfinal TypeHandler<?> typeHandler = rsw.getTypeHandler(propertyType, columnName);//生成UnMappedColumnAutoMapping放到autoMapping里autoMapping.add(new UnMappedColumnAutoMapping(columnName, property, typeHandler, propertyType.isPrimitive()));} else {configuration.getAutoMappingUnknownColumnBehavior().doAction(mappedStatement, columnName, property, propertyType);}} else{configuration.getAutoMappingUnknownColumnBehavior().doAction(mappedStatement, columnName, (property != null) ? property : propertyName, null);}}放到cache中autoMappingsCache.put(mapKey, autoMapping);}//返回autoMapping(回到applyAutomaticMappings方法)return autoMapping;}

//ResultSetWrapper类中

  public List<String> getUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {//看缓存有没有,没有自己造,有就直接返回List<String> unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));if (unMappedColumnNames == null) {//加载映射和没映射字段loadMappedAndUnmappedColumnNames(resultMap, columnPrefix);unMappedColumnNames = unMappedColumnNamesMap.get(getMapKey(resultMap, columnPrefix));}return unMappedColumnNames;}private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {List<String> mappedColumnNames = new ArrayList<String>();List<String> unmappedColumnNames = new ArrayList<String>();final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);//获取已经映射过的columnfinal Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);//轮询里面ResultSetWrapper中的columnNamesfor (String columnName : columnNames) {//转成大写比对final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);//若mappedColumns中有此columnif (mappedColumns.contains(upperColumnName)) {//加入到mappedColumnNamesmappedColumnNames.add(upperColumnName);} else {//否则放入unmappedColumnNamesunmappedColumnNames.add(columnName);}}//最后将mappedColumnNames放到mappedColumnNamesMapmappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);//最后将unmappedColumnNames放到unMappedColumnNamesMapunMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);}

总结

其实就几个重要的点。
1.创建对象
2.获得resultHandle
3.获得mapping
4.获得typeHandle
5.通过handle和mapping给对象赋值,然后把对象存到resultHandle中
6.最后返回

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/555907.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java Stream Collectors.groupingBy()实现分组(单字段分组,多字段分组)

1 User实体类 public class User {private Long userId;/*** 用户名(登录)*/private String username;/*** 年龄*/private int age;/*** 性别*/private int sex;/*** 密码*/private String password; }2 单字段分组 //根据年龄分组 List<User> userList new ArrayList…

怎么在mysql查询自己建的表格_oracle数据库中怎么查询自己建的表

select * from user_tables where table_nameABC;可以查询出ABC但前提得是知道表名是ABC 可以根据表建时间不同&#xff0c;来将你本人建的表和数据库自动建的表分开&#xff0c;虽然他们都是同一个用户建的。 在user_table表里没有建表时间这一字段&#xff0c;可以用user_obj…

设计模式总结篇(为什么要学习设计模式,学习设计模式的好处)

在学习完设计模式后&#xff0c;LZ想告诉大家&#xff1a; ​ 对于一名工作不久的程序员来说&#xff0c;学习设计模式是非常有必要的&#xff0c;设计模式可以让你知道在某些场景下如何来设计出适合场景的架子&#xff0c;对&#xff0c;因为经验不丰富&#xff0c;大部分程序…

Mybatis源码分析开篇

Mybatis的由来 iBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code&#xff0c;并且改名为MyBatis 。2013年11月迁移到Github。 iBATIS一词来源于“internet”和“abatis”的组合&#xff0c;是一个基于Java的持久层框架…

java数据库配置_java--数据库(文件配置连接,自定义连接池)

import java.util.ResourceBundle;public class Mtest7Demo {//使用properties配置文件完成数据库的连接/** 开发中获得连接的4个参数(驱动,URL,用户名,密码)通常都存在配置文件中&#xff0c;方便后期维护&#xff0c;* 程序如果需要更换数据库&#xff0c;只需要修改配置文件…

Mybatis源码分析之(一)搭建一个mybatis框架(写一个mybatis的Demo)

数据库工作&#xff1a; 首先准备工作&#xff0c;安装mysql&#xff0c;并且新建一张t_demo表 CREATE TABLE t_demo (name varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,id int(11) NOT NULL AUTO_INCREMENT,PRIMARY KEY (id) ) ENGINEInnoDB AUTO_INCREMENT4 DEFAUL…

后端开发 java_Java后端开发三年,你不得不了解的JVM

JAVA程序员&#xff0c;三年是个坎&#xff0c;如果过了三年你还没有去研究JVM的话&#xff0c;那么你这个程序员只能是板砖的工具了。下面来个JVM的解析可好&#xff1f;JVM是Java Virtual Machine(Java虚拟机)的缩写&#xff0c;也就是指的JVM虚拟机&#xff0c;属于是一种虚…

Mybatis源码分析之(二)根据配置文件创建SqlSessionFactory(Configuration的创建过程)

SqlSessionFactoryBuilder.build创建SqlSessionFactory&#xff08;粗略走一步流程&#xff09; 看完上篇文章后&#xff0c;你对mybatis应该有个大概的了解了&#xff0c;那么我们知道new SqlSessionFactoryBuilder().build是框架的入口&#xff0c;我们到SqlSessionFactoryB…

c++调用python接口作用是_利用Boost::Python实现C++调用python接口

利用Boost::Python实现C调用python接口2019年11月06日阅读数&#xff1a;7这篇文章主要向大家介绍利用Boost::Python实现C调用python接口,主要内容包括基础应用、实用技巧、原理机制等方面&#xff0c;希望对大家有所帮助。Boost.Python能将C的结构体暴露给Python使用。可是在运…

Mybatis源码分析之(三)mapper接口底层原理(为什么不用写方法体就能访问到数据库)

mybatis是怎么拿sqlSession 在 上一篇的时候&#xff0c;我们的SqlSessionFactoryBuilder已经从xml文件中解析出了Configuration并且返回了sessionFactory。 然后我们要从session;中拿到sqlSession public class DefaultSqlSessionFactory implements SqlSessionFactory {pr…

java中的位移_Java中的位移运算

在Java中&#xff0c;位移运算属于基本运算&#xff0c;符号是<>&#xff0c;即向左位移和向右位移。在Java中只有整数才能位移&#xff0c;所以其他的不考虑&#xff0c;位移运算是将整数在内存中表示的二进制进行位移&#xff0c;所以在Java中分为正数和负数的位移。对…

Mybatis源码分析之(四)mapper访问数据库的底层原理(代理方法中具体访问数据库的细节)

从之前的文章&#xff0c;我们知道了其实mapper真正执行的方法就下面的最后两行。&#xff08;以下所有的分析都基于一次mybatis的一次select查询。 MapperProxy类中的invoke函数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {i…

mysql count distinct case when_统计符合条件的去重过的数量 - - count distinct if case

现有表结构&#xff1a;CREATE TABLE example_dataset (id int(11) unsigned NOT NULL AUTO_INCREMENT,tel bigint(11) DEFAULT NULL,gender varchar(11) DEFAULT NULL,PRIMARY KEY (id)) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb4;插入数据INSERT INTO example_da…

Mybatis源码分析之(六)mybatis拦截器(Interceptor)的实现原理

文章目录前言InterceptorChain保存所有的Interceptor创建四大对象都走ConfigurationInterceptorChain增强对象方法Plugin封装动态代理&#xff0c;让你使用Mybatis拦截器更简单Invocation&#xff0c;让我们能在拦截器中使用动态代理类中的invoke方法中的对象调用时序图小结前言…

Mybatis源码分析之(七)Mybatis一级缓存和二级缓存的实现

文章目录一级缓存二级缓存总结对于一名程序员&#xff0c;缓存真的很重要&#xff0c;而且缓存真的是老生常谈的一个话题拉。因为它在我们的开发过程中真的是无处不在。今天LZ带大家来看一下。Mybatis是怎么实现一级缓存和二级缓存的。(自带的缓存机制)一级缓存 存在BaseExecu…

oauth2 java 获取token_OAuth2 Token 一定要放在请求头中吗?

Token 一定要放在请求头中吗&#xff1f; 答案肯定是否定的&#xff0c;本文将从源码的角度来分享一下 spring security oauth2 的解析过程&#xff0c;及其扩展点的应用场景。Token 解析过程说明当我们使用 spring security oauth2 时, 一般情况下需要把认证中心申请的 token …

Map.getOrDefault()方法

default V getOrDefault(Object key, V defaultValue) {V v;return (((v get(key)) ! null) || containsKey(key))? v: defaultValue;}这是源码&#xff0c;意思就是当Map集合中有这个key时&#xff0c;就使用这个key对应的value值&#xff0c;如果没有这个key就使用默认值de…

java开发原则_java开发中,大家处理异常的原则是什么,是如何处理的?

展开全部最熟悉的陌生人&#xff1a;异常异常的类e5a48de588b63231313335323631343130323136353331333361326365型Throwable— Exception—- RuntimeException— Error需要注意的是&#xff0c;RuntimeException及其子类不需要在方法签名中显示注明异常抛出。例如&#xff1a;v…

java8合并两个Map

合并两个Map map自己的方法 实现方式是通过 putAll() 方法将多个 map 对象中的数据放到另外一个全新的 map 对象中&#xff0c;代码如下所示&#xff0c;展示了两个 map 对象的合并&#xff0c;如果是多个 map 合并也是用这种方式。 public static void main(String[] args) {…

java 线程 spring_java中spring里实现多线程

Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程的可使用ThreadPoolTaskExecutor来实现基于线程池的TaskExecutor在实际开发中由于多是异步&#xff0c;所以使用EnableAsync来支持异步任务&#xff0c;且要在Bean的方法中使用Async来声明其是一个异步任务?????…