欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

mybatis查询语句的背后之封装数据

程序员文章站 2023-01-02 18:50:32
转载请注明出处。。。 一、前言 继上一篇mybatis查询语句的背后,这一篇主要围绕着mybatis查询的后期操作,即跟数据库交互的时候。由于本人也是一边学习源码一边记录,内容难免有错误或不足之处,还望诸位指正,本文只可当参考作用。谨记! 二、分析 继上一篇博文的查询例子,mybatis在最后的查询 ......

 转载请注明出处。。。

一、前言

继上一篇,这一篇主要围绕着mybatis查询的后期操作,即跟数据库交互的时候。由于本人也是一边学习源码一边记录,内容难免有错误或不足之处,还望诸位指正,本文只可当参考作用。谨记! 

二、分析

继上一篇博文的查询例子,mybatis在最后的查询最终会走simpleexecutor类的doquery方法,

 1  @override
 2   public <e> list<e> doquery(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler, boundsql boundsql) throws sqlexception {
 3     statement stmt = null;
 4     try {
 5       configuration configuration = ms.getconfiguration();
 6       // 这里也就是采用了策略模式(个人感觉有点像),实际的statementhandler为routingstatementhandler
 7       statementhandler handler = configuration.newstatementhandler(wrapper, ms, parameter, rowbounds, resulthandler, boundsql);
 8       stmt = preparestatement(handler, ms.getstatementlog());
 9       // 虽然是执行的routingstatementhandler.query,但返回结果的还是preparedstatementhandler处理
10       return handler.query(stmt, resulthandler);
11     } finally {
12       closestatement(stmt);
13     }
14   }
15 
16 private statement preparestatement(statementhandler handler, log statementlog) throws sqlexception {
17     statement stmt;
18    // 使用了代理模式,也可以理解为对connection进行了一层包装,这里的作用就是加了log处理
19     connection connection = getconnection(statementlog);
20    //进行预编译,即类似jdbc的 sql,如 select * from user where id=?
21     stmt = handler.prepare(connection, transaction.gettimeout());
22     // 对执行查询的sql进行参数设置
23     handler.parameterize(stmt);
24     return stmt;
25   }

关于 handler.prepare的作用这里简单介绍下,不做代码分析。

会设置fetchsize,作用就是一次性从数据库抓取数据,好像默认值是10条,如果每次只抓取一条,则进行rs.next的时候,会再次查库。

如果是insert操作,并且数据库主键自增且还设置了可以返回主键,则会还做获取主键的操作。

先从设置参数说起,也就是handler.parameterize。先看下源码,具体位置在defaultparameterhandler类里面

 1 @override
 2   public void setparameters(preparedstatement ps) {
 3     errorcontext.instance().activity("setting parameters").object(mappedstatement.getparametermap().getid());
 4     // 获取配置文件里面的sql参数信息,如sql为select * from user where id=#{userid,jdbctype=integer}
 5     // parametermapping 记录了参数名也就是userid,还有记录了对应的jdbc类型,还有对应的javatype等等,具体可以debug看下
 6     list<parametermapping> parametermappings = boundsql.getparametermappings();
 7     if (parametermappings != null) {
 8       for (int i = 0; i < parametermappings.size(); i++) {
 9         parametermapping parametermapping = parametermappings.get(i);
10         if (parametermapping.getmode() != parametermode.out) {
11           object value;
12           string propertyname = parametermapping.getproperty();
13           // 如果为true,那么参数中有类似 user.name 格式
14           if (boundsql.hasadditionalparameter(propertyname)) { // issue #448 ask first for additional params
15             value = boundsql.getadditionalparameter(propertyname);
16           } else if (parameterobject == null) {
17             value = null;
18           } else if (typehandlerregistry.hastypehandler(parameterobject.getclass())) {
19             value = parameterobject;
20           } else {
21             // metaobject 类似一个工具类,它里面有一个反射工厂,可以专门解析一个类的信息,如字段的setter/getter/属性信息,这里不做多余介绍
22             // 这里负责将parameterobject的里面的值整出来(也就是传入的参数),实际上代码执行到了这,parameterobject就是一个map结构
23             metaobject metaobject = configuration.newmetaobject(parameterobject);
24             value = metaobject.getvalue(propertyname);// 取值
25           }
26           // 获取对应的typehandler,一般情况不设置的话,基本都是objecttypehandler
27           typehandler typehandler = parametermapping.gettypehandler();
28           jdbctype jdbctype = parametermapping.getjdbctype();
29           if (value == null && jdbctype == null) {
30             jdbctype = configuration.getjdbctypefornull();
31           }
32           try {
33             // 进行设值
34             typehandler.setparameter(ps, i + 1, value, jdbctype);
35           } catch (typeexception e) {
36             throw new typeexception("could not set parameters for mapping: " + parametermapping + ". cause: " + e, e);
37           } catch (sqlexception e) {
38             throw new typeexception("could not set parameters for mapping: " + parametermapping + ". cause: " + e, e);
39           }
40         }
41       }
42     }
43   }

 这段代码也就负责对预编译后的sql设置参数,这里逻辑主要是围绕以下步骤进行得,

    获取参数名,获取参数值,获取参数类型,然后做进行设值操作

 

 1  /**
 2  * mybatis数据处理有单结果集和多结果集处理,一般多结果集出现存储过程中,如果存储过程中写了两条select语句,如
 3  * select * from user , select * from classes   这种情况这里不做介绍,因为本人用的不多,理解的也不是很透彻。
 4  * 这里不多做介绍,这里只针对简单映射做一个大概介绍
 5  *
 6  */
 7 public list<object> handleresultsets(statement stmt) throws sqlexception {
 8     errorcontext.instance().activity("handling results").object(mappedstatement.getid());
 9     // 保存查询结果
10     final list<object> multipleresults = new arraylist<>();
11 
12     int resultsetcount = 0;
13     // 获取第一条数据
14     resultsetwrapper rsw = getfirstresultset(stmt);
15     // 如果不是多结果集映射,一般resultmaps的大小为1
16     // resultmap中存储的有类的字段属性,数据库字段名称等信息
17     list<resultmap> resultmaps = mappedstatement.getresultmaps();
18     int resultmapcount = resultmaps.size();
19     // 校验数据的正确性
20     validateresultmapscount(rsw, resultmapcount);
21     while (rsw != null && resultmapcount > resultsetcount) {
22       resultmap resultmap = resultmaps.get(resultsetcount);
23       // 处理结果集映射
24       handleresultset(rsw, resultmap, multipleresults, null);
25       rsw = getnextresultset(stmt);
26       cleanupafterhandlingresultset();
27       resultsetcount++;
28     }
29     // 处理slect 标签的resultsets属性,多个用逗号隔开,个人几乎没用过,略过
30     string[] resultsets = mappedstatement.getresultsets();
31     if (resultsets != null) {
32       while (rsw != null && resultsetcount < resultsets.length) {
33         resultmapping parentmapping = nextresultmaps.get(resultsets[resultsetcount]);
34         if (parentmapping != null) {
35           string nestedresultmapid = parentmapping.getnestedresultmapid();
36           resultmap resultmap = configuration.getresultmap(nestedresultmapid);
37           handleresultset(rsw, resultmap, null, parentmapping);
38         }
39         rsw = getnextresultset(stmt);
40         cleanupafterhandlingresultset();
41         resultsetcount++;
42       }
43     }
44 
45     return collapsesingleresultlist(multipleresults);
46   }

 

以上代码就是为结果映射做一个铺垫,重点是在hanleresultset方法里,

 1 private void handleresultset(resultsetwrapper rsw, resultmap resultmap, list<object> multipleresults, resultmapping parentmapping) throws sqlexception {
 2     try {// 针对简单映射,parentmapping是为null的
 3       if (parentmapping != null) {
 4         handlerowvalues(rsw, resultmap, null, rowbounds.default, parentmapping);
 5       } else {
 6         // 默认使用defaultresulthandler,如需使用自定义的,则可在传参加入resulthandler接口实现类
 7         if (resulthandler == null) {
 8           defaultresulthandler defaultresulthandler = new defaultresulthandler(objectfactory);
 9           // 处理结果,结果存在resulthandler里
10           handlerowvalues(rsw, resultmap, defaultresulthandler, rowbounds, null);
11           multipleresults.add(defaultresulthandler.getresultlist());
12         } else {
13           handlerowvalues(rsw, resultmap, resulthandler, rowbounds, null);
14         }
15       }
16     } finally {
17       // issue #228 (close resultsets)
18       closeresultset(rsw.getresultset());
19     }
20   }
public void handlerowvalues(resultsetwrapper rsw, resultmap resultmap, resulthandler<?> resulthandler, rowbounds rowbounds, resultmapping parentmapping) throws sqlexception {
   // 处理有嵌套映射的情况
    if (resultmap.hasnestedresultmaps()) {
      ensurenorowbounds();
      checkresulthandler();
      handlerowvaluesfornestedresultmap(rsw, resultmap, resulthandler, rowbounds, parentmapping);
    } else {//没有嵌套映射
      handlerowvaluesforsimpleresultmap(rsw, resultmap, resulthandler, rowbounds, parentmapping);
    }
  }

 

 1 private void handlerowvaluesforsimpleresultmap(resultsetwrapper rsw, resultmap resultmap, resulthandler<?> resulthandler, rowbounds rowbounds, resultmapping parentmapping)
 2       throws sqlexception {
 3     defaultresultcontext<object> resultcontext = new defaultresultcontext<>();
 4     resultset resultset = rsw.getresultset();
 5     // 跳过多少行,到达指定记录位置,如在传参的时候传入了rowbounds,则会根据该类的offset值跳到指定记录位置
 6     skiprows(resultset, rowbounds);
 7     // shouldprocessmorerows 用来检测是否能继续对后续的结果进行映射
 8     while (shouldprocessmorerows(resultcontext, rowbounds) && !resultset.isclosed() && resultset.next()) {
 9       //用来处理resultmap节点中配置了discriminator节点,这里忽略掉
10       resultmap discriminatedresultmap = resolvediscriminatedresultmap(resultset, resultmap, null);
11       // 得到的结果就是sql执行后的一行记录,如返回user对象信息,则rowvalue就代表一个user实例,里面已经有值了
12       object rowvalue = getrowvalue(rsw, discriminatedresultmap, null);
13       //保存数据
14       storeobject(resulthandler, resultcontext, rowvalue, parentmapping, resultset);
15     }
16   }
 1 private object getrowvalue(resultsetwrapper rsw, resultmap resultmap, string columnprefix) throws sqlexception {
 2     final resultloadermap lazyloader = new resultloadermap();
 3     // 创建对象,可以理解为对resultmap节点的type属性值,进行了反射处理,得到了一个对象,但属性值都是默认值。
 4     object rowvalue = createresultobject(rsw, resultmap, lazyloader, columnprefix);
 5     if (rowvalue != null && !hastypehandlerforresultobject(rsw, resultmap.gettype())) {
 6       final metaobject metaobject = configuration.newmetaobject(rowvalue);
 7       boolean foundvalues = this.useconstructormappings;
 8       //是否需要自动映射,有三种映射,分别为none,partial,full,默认第二种,处理非嵌套映射,可通过automappingbehavior 配置
 9       if (shouldapplyautomaticmappings(resultmap, false)) {
10         // 映射resultmap中未明确指定的列,如类中含有username属性,但是resultmap中没配置,则通过这个进行数据映射,还是可以查询到结果
11         foundvalues = applyautomaticmappings(rsw, resultmap, metaobject, columnprefix) || foundvalues;
12       }
13       // 处理resultmap中指定的列
14       foundvalues = applypropertymappings(rsw, resultmap, metaobject, lazyloader, columnprefix) || foundvalues;
15       foundvalues = lazyloader.size() > 0 || foundvalues;
16       // 如果没查询到结果,但配置可返回空对象(指的是没有设置属性值得对象),则返回空对象,否则返回null
17       rowvalue = foundvalues || configuration.isreturninstanceforemptyrow() ? rowvalue : null;
18     }
19     return rowvalue;
20   }

 

这里只介绍resultmap中有明确指定的列

 1 private boolean applypropertymappings(resultsetwrapper rsw, resultmap resultmap, metaobject metaobject, resultloadermap lazyloader, string columnprefix)
 2       throws sqlexception {
 3      // 获取数据字段名
 4     final list<string> mappedcolumnnames = rsw.getmappedcolumnnames(resultmap, columnprefix);
 5     boolean foundvalues = false;
 6     // 获取的数据就是resultmap节点中配置的result节点,有多个result节点,这个集合大小就是多少
 7     // 里面存储的是属性名/字段名等信息
 8     final list<resultmapping> propertymappings = resultmap.getpropertyresultmappings();
 9     for (resultmapping propertymapping : propertymappings) {
10       string column = prependprefix(propertymapping.getcolumn(), columnprefix);
11       // 是否有嵌套映射
12       if (propertymapping.getnestedresultmapid() != null) {
13         // the user added a column attribute to a nested result map, ignore it
14         column = null;
15       }
16       // 针对1来说一般常与嵌套查询配合使用
17       // 2 判断属性基本映射
18       // 3 多结果集的一个处理
19       if (propertymapping.iscompositeresult()// 1
20           || (column != null && mappedcolumnnames.contains(column.touppercase(locale.english)))// 2
21           || propertymapping.getresultset() != null) {// 3
22         // 获取当前column字段对于的值,有用到typehandler来进行参数的一个转换
23         object value = getpropertymappingvalue(rsw.getresultset(), metaobject, propertymapping, lazyloader, columnprefix);
24         
25         //获取类的属性字段名
26         final string property = propertymapping.getproperty();
27         if (property == null) {
28           continue;
29         } else if (value == deferred) {// 类似占位符。处理懒加载数据
30           foundvalues = true;
31           continue;
32         }
33         if (value != null) {
34           foundvalues = true;
35         }
36         if (value != null || (configuration.iscallsettersonnulls() && !metaobject.getsettertype(property).isprimitive())) {
37           // 进行设置属性值
38           metaobject.setvalue(property, value);
39         }
40       }
41     }
42     return foundvalues;
43   }

 

或许有人奇怪为啥没看到查询的对象有set操作,值就到了对象里面去了,这里全是metaobject给你操作了,具体的,大家可以自行了解这个类,只能说这个类的功能很强大。

以上就是本文全部内容,

--------------------------------------------------------------------------------------------------------------------------分界线----------------------------------------------------------------------------------------------