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给你操作了,具体的,大家可以自行了解这个类,只能说这个类的功能很强大。
以上就是本文全部内容,
--------------------------------------------------------------------------------------------------------------------------分界线----------------------------------------------------------------------------------------------
上一篇: 解析关于SQL语句Count的一点细节