好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

mybatis查询语句揭秘之封装数据

一、前言

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

二、分析

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

@override

  public <e> list<e> doquery(mappedstatement ms, object parameter, rowbounds rowbounds, resulthandler resulthandler, boundsql boundsql) throws sqlexception {

  statement stmt = null ;

  try {

  configuration configuration = ms.getconfiguration();

  // 这里也就是采用了策略模式(个人感觉有点像),实际的statementhandler为routingstatementhandler

  statementhandler handler = configuration.newstatementhandler(wrapper, ms, parameter, rowbounds, resulthandler, boundsql);

  stmt = preparestatement(handler, ms.getstatementlog());

  // 虽然是执行的routingstatementhandler.query,但返回结果的还是preparedstatementhandler处理

  return handler.query(stmt, resulthandler);

  } finally {

  closestatement(stmt);

  }

  }

 

private statement preparestatement(statementhandler handler, log statementlog) throws sqlexception {

  statement stmt;

  // 使用了代理模式,也可以理解为对connection进行了一层包装,这里的作用就是加了log处理

  connection connection = getconnection(statementlog);

  //进行预编译,即类似jdbc的 sql,如 select * from user where id=?

  stmt = handler.prepare(connection, transaction.gettimeout());

  // 对执行查询的sql进行参数设置

  handler.parameterize(stmt);

  return stmt;

  }

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

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

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

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

@override

  public void setparameters(preparedstatement ps) {

  errorcontext.instance().activity( "setting parameters" ).object(mappedstatement.getparametermap().getid());

  // 获取配置文件里面的sql参数信息,如sql为select * from user where id=#{userid,jdbctype=integer}

  // parametermapping 记录了参数名也就是userid,还有记录了对应的jdbc类型,还有对应的javatype等等,具体可以debug看下

  list<parametermapping> parametermappings = boundsql.getparametermappings();

  if (parametermappings != null ) {

  for ( int i = 0 ; i < parametermappings.size(); i++) {

  parametermapping parametermapping = parametermappings.get(i);

  if (parametermapping.getmode() != parametermode.out) {

   object value;

   string propertyname = parametermapping.getproperty();

   // 如果为true,那么sql参数中有类似 user.name 格式

   if (boundsql.hasadditionalparameter(propertyname)) { // issue #448 ask first for additional params

   value = boundsql.getadditionalparameter(propertyname);

   } else if (parameterobject == null ) {

   value = null ;

   } else if (typehandlerregistry.hastypehandler(parameterobject.getclass())) {

   value = parameterobject;

   } else {

   // metaobject 类似一个工具类,它里面有一个反射工厂,可以专门解析一个类的信息,如字段的setter/getter/属性信息,这里不做多余介绍

   // 1、下面详细介绍

   metaobject metaobject = configuration.newmetaobject(parameterobject);

   value = metaobject.getvalue(propertyname); // 取值

   }

   // 获取对应的typehandler,一般情况不设置的话,基本都是objecttypehandler

   typehandler typehandler = parametermapping.gettypehandler();

   jdbctype jdbctype = parametermapping.getjdbctype();

   if (value == null && jdbctype == null ) {

   jdbctype = configuration.getjdbctypefornull();

   }

   try {

   // 进行设值

   typehandler.setparameter(ps, i + 1 , value, jdbctype);

   } catch (typeexception e) {

   throw new typeexception( "could not set parameters for mapping: " + parametermapping + ". cause: " + e, e);

   } catch (sqlexception e) {

   throw new typeexception( "could not set parameters for mapping: " + parametermapping + ". cause: " + e, e);

   }

  }

  }

  }

  }

对于上述代码中的一部分这里负责将parameterobject的里面的值整出来(也就是传入的参数),如果参数是map结构,就从map里面取值,如果不是,如单个非javabean参数,则直接取值,如果是单个javabean,则通过metaobject类转换成一个beanwrapper,进行取值

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

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

/**

  * mybatis数据处理有单结果集和多结果集处理,一般多结果集出现存储过程中,如果存储过程中写了两条select语句,如

  * select * from user , select * from classes 这种情况这里不做介绍,因为本人用的不多,理解的也不是很透彻。

  * 这里不多做介绍,这里只针对简单映射做一个大概介绍

  *

  */

public list<object> handleresultsets(statement stmt) throws sqlexception {

  errorcontext.instance().activity( "handling results" ).object(mappedstatement.getid());

  // 保存查询结果

  final list<object> multipleresults = new arraylist<>();

 

  int resultsetcount = 0 ;

  // 获取第一条数据

  resultsetwrapper rsw = getfirstresultset(stmt);

  // 如果不是多结果集映射,一般resultmaps的大小为1

  // resultmap中存储的有类的字段属性,数据库字段名称等信息

  list<resultmap> resultmaps = mappedstatement.getresultmaps();

  int resultmapcount = resultmaps.size();

  // 校验数据的正确性

  validateresultmapscount(rsw, resultmapcount);

  while (rsw != null && resultmapcount > resultsetcount) {

  resultmap resultmap = resultmaps.get(resultsetcount);

  // 处理结果集映射

  handleresultset(rsw, resultmap, multipleresults, null );

  rsw = getnextresultset(stmt);

  cleanupafterhandlingresultset();

  resultsetcount++;

  }

  // 处理slect 标签的resultsets属性,多个用逗号隔开,个人几乎没用过,略过

  string[] resultsets = mappedstatement.getresultsets();

  if (resultsets != null ) {

  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);

  }

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

private void handleresultset(resultsetwrapper rsw, resultmap resultmap, list<object> multipleresults, resultmapping parentmapping) throws sqlexception {

  try { // 针对简单映射,parentmapping是为null的

  if (parentmapping != null ) {

  handlerowvalues(rsw, resultmap, null , rowbounds. default , parentmapping);

  } else {

  // 默认使用defaultresulthandler,如需使用自定义的,则可在传参加入resulthandler接口实现类

  if (resulthandler == null ) {

   defaultresulthandler defaultresulthandler = new defaultresulthandler(objectfactory);

   // 处理结果,结果存在resulthandler里

   handlerowvalues(rsw, resultmap, defaultresulthandler, rowbounds, null );

   multipleresults.add(defaultresulthandler.getresultlist());

  } else {

   handlerowvalues(rsw, resultmap, resulthandler, rowbounds, null );

  }

  }

  } finally {

  // issue #228 (close resultsets)

  closeresultset(rsw.getresultset());

  }

  }

?

1

2

3

4

5

6

7

8

9

10

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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

private void handlerowvaluesforsimpleresultmap(resultsetwrapper rsw, resultmap resultmap, resulthandler<?> resulthandler, rowbounds rowbounds, resultmapping parentmapping)

  throws sqlexception {

  defaultresultcontext<object> resultcontext = new defaultresultcontext<>();

  resultset resultset = rsw.getresultset();

  // 跳过多少行,到达指定记录位置,如在传参的时候传入了rowbounds,则会根据该类的offset值跳到指定记录位置

  skiprows(resultset, rowbounds);

  // shouldprocessmorerows 用来检测是否能继续对后续的结果进行映射

  while (shouldprocessmorerows(resultcontext, rowbounds) && !resultset.isclosed() && resultset.next()) {

  //用来处理resultmap节点中配置了discriminator节点,这里忽略掉

  resultmap discriminatedresultmap = resolvediscriminatedresultmap(resultset, resultmap, null );

  // 得到的结果就是sql执行后的一行记录,如返回user对象信息,则rowvalue就代表一个user实例,里面已经有值了

  object rowvalue = getrowvalue(rsw, discriminatedresultmap, null );

  //保存数据

  storeobject(resulthandler, resultcontext, rowvalue, parentmapping, resultset);

  }

  }

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

private object getrowvalue(resultsetwrapper rsw, resultmap resultmap, string columnprefix) throws sqlexception {

  final resultloadermap lazyloader = new resultloadermap();

  // 创建对象,可以理解为对resultmap节点的type属性值,进行了反射处理,得到了一个对象,但属性值都是默认值。

  object rowvalue = createresultobject(rsw, resultmap, lazyloader, columnprefix);

  if (rowvalue != null && !hastypehandlerforresultobject(rsw, resultmap.gettype())) {

  final metaobject metaobject = configuration.newmetaobject(rowvalue);

  boolean foundvalues = this .useconstructormappings;

  //是否需要自动映射,有三种映射,分别为none,partial,full,默认第二种,处理非嵌套映射,可通过automappingbehavior 配置

  if (shouldapplyautomaticmappings(resultmap, false )) {

  // 映射resultmap中未明确指定的列,如类中含有username属性,但是resultmap中没配置,则通过这个进行数据映射,还是可以查询到结果

  foundvalues = applyautomaticmappings(rsw, resultmap, metaobject, columnprefix) || foundvalues;

  }

  // 处理resultmap中指定的列

  foundvalues = applypropertymappings(rsw, resultmap, metaobject, lazyloader, columnprefix) || foundvalues;

  foundvalues = lazyloader.size() > 0 || foundvalues;

  // 如果没查询到结果,但配置可返回空对象(指的是没有设置属性值得对象),则返回空对象,否则返回null

  rowvalue = foundvalues || configuration.isreturninstanceforemptyrow() ? rowvalue : null ;

  }

  return rowvalue;

  }

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

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

private boolean applypropertymappings(resultsetwrapper rsw, resultmap resultmap, metaobject metaobject, resultloadermap lazyloader, string columnprefix)

  throws sqlexception {

  // 获取数据字段名

  final list<string> mappedcolumnnames = rsw.getmappedcolumnnames(resultmap, columnprefix);

  boolean foundvalues = false ;

  // 获取的数据就是resultmap节点中配置的result节点,有多个result节点,这个集合大小就是多少

  // 里面存储的是属性名/字段名等信息

  final list<resultmapping> propertymappings = resultmap.getpropertyresultmappings();

  for (resultmapping propertymapping : propertymappings) {

  string column = prependprefix(propertymapping.getcolumn(), columnprefix);

  // 是否有嵌套映射

  if (propertymapping.getnestedresultmapid() != null ) {

  // the user added a column attribute to a nested result map, ignore it

  column = null ;

  }

  // 针对1来说一般常与嵌套查询配合使用

  // 2 判断属性基本映射

  // 3 多结果集的一个处理

  if (propertymapping.iscompositeresult() // 1

   || (column != null && mappedcolumnnames.contains(column.touppercase(locale.english))) // 2

   || propertymapping.getresultset() != null ) { // 3

  // 获取当前column字段对于的值,有用到typehandler来进行参数的一个转换

  object value = getpropertymappingvalue(rsw.getresultset(), metaobject, propertymapping, lazyloader, columnprefix);

 

  //获取类的属性字段名

  final string property = propertymapping.getproperty();

  if (property == null ) {

   continue ;

  } else if (value == deferred) { // 类似占位符。处理懒加载数据

   foundvalues = true ;

   continue ;

  }

  if (value != null ) {

   foundvalues = true ;

  }

  if (value != null || (configuration.iscallsettersonnulls() && !metaobject.getsettertype(property).isprimitive())) {

   // 进行设置属性值

   metaobject.setvalue(property, value);

  }

  }

  }

  return foundvalues;

  }

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

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。

原文链接:https://HdhCmsTestcnblogs测试数据/qm-article/p/10588627.html

查看更多关于mybatis查询语句揭秘之封装数据的详细内容...

  阅读:8次