设为首页 收藏本站
查看: 1052|回复: 0

[经验分享] MyBatis整体预览(一)

[复制链接]

尚未签到

发表于 2016-11-25 01:32:08 | 显示全部楼层 |阅读模式
  MyBatis整体预览(一)

   题记:最近在工作之余分析了一下MyBatis的源码,促使我阅读源码的原因是为了实现MyBatis在物理上的分页。我们知道,MyBatis是在逻辑上的分页,通过用户的查询,将结果缓存下来,在查看是否传递了RowBounds对象,在查看里面的offsetlimit值,通过这两个值,从返回的结果集合中截取位于期间的值。但是这样并不是很好,可以想想,如果假设查询的数据量很大,但是有用的可以是前几条,这未免有点太浪费了。在之前,也在网上查了一下实现分页的方法,最常用的就是添加MyBatis插件,实现Interceptor接口,拦截StatementHandler接口中的prepare方法,后面会介绍为什么拦截这个接口的这个方法。在拦截ResultSetHandler接口的handlerResultSet方法,后面也会对其缘由进行介绍。但是这中方法虽然可以添加分页的SQL语句,但是并没有将分页的offsetlimit的值让Mybatis动态的添加到SQL中去,有人会说,可以在拦截StatementHandler接口的时候我们将它们拼装上去。但是这样会容易出现SQL注入的问题。所以这样不得不使我进一步的了解MyBatis的内部原理。本文将就一下几个方面对MyBatis的内部实现进行分析。
DSC0000.png
mybatis源码结构图

  数据管家——Configuration:
  
  MyBatis在运行期的基本上所有的数据都会汇总到这个类。它的初始数据是来自开发人员配置在configurationxml配置文件。通过用户配置的environments来获得系统运行的数据库环境,如事物管理以及数据源。下面给出了最基本的配置:
  

<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=GBK"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/bieber/mybatis/io/user-mapper.xml"/>
</mappers>
</configuration>

  这些配置对于MyBatis需要做哪些工作呢?通过阅读Configuration的源码会发现,Mybatis其实为configuration标签下面的子标签都有一个对应的变量来进行存储,例如:

protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();

  则是存储<typeHandlers></typeHandlers>标签下面配置的所有信息。其他的也类似可以找到。负责创建Configuration对象的则是XMLConfigurationBuilder,这里将完成从配置的XML数据映射到Configuration对象的数据。通过一下方法完成数据的映射:

private void parseConfiguration(XNode root) {
try {
propertiesElement(root.evalNode("properties"));       typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

  可以到,它为每个元素都对应了一个处理方法,这些方法将负责解析我们配置的XML文件。这里面我主要跟踪了几个方法的执行
  mapperElementtypeHandlerElementtypeAliasesElementenvironmentsElement)
  mapperElement——ORM
  
   我们知道,MyBatis支持注解形式和XML形式的ORM配置。那么当然将会有两个类来处理这两种行为,它们分别是XMLMapperBuilderMapperAnnotationBuilder,它们分别处理什么类型,我看我就不用说了。通过解析configuration/mappers元素来获得ORM配置信息。
  1)XML方式的ORM配置和方式,当我们在mappers/mapper的属性中配置了url或者是resource信息的时候将触发MyBatis采用XML的方式进行处理,并读取你指定的mapper路径。在XMLMapperBuilder类中有如下方法:
  

private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new RuntimeException("Error parsing Mapper XML. Cause: " + e, e);
}
}

  这个方法便是读取你mapper文件中所有制的ORM信息。该方法将通过调用XMLMapperBuilderparse()方法触发。
   2)注解方式配置ORM信息加载,当你配置了mappers/package或者在mapper里面配置了class属性的时候将触发信息的读取,具体的过程我就再描述了,基本和上面差不多,只是读取的是注解的信息。
   注意:MyBatis优先处理的是注解形式的方式,并且在mapper配置中,当配置了多个属性时,resource属性优先处理。
   那么在这样处理后Configuration会得到怎样的数据呢?通过这些处理在Configuration里面将会获得几个主要的变量值:sqlFragmentsresultMapsmappedStatements。其中sqlFragments就是我们定义在mapper里面的sql标签或者注解的内容,而resultMaps也是定义在mapper里面或者注解的resultMap内容。最重要的是mappedStatements,这是ORM的最关键部分。它里面通过键值对的方式存储,key这是我们配置的id属性加上namespace,而value则是MappedStatement对象,这个对象这就对应了我们配置的select/update/delete/insert标签的值。
   MappedStatement对象包含这条slq语句的ID,执行的类型(Inser,update,delte,select)statementType(指定产生Statement的类型,如PreparedStatement,还有一个就是SqlSource接口的子类对象,在MyBatis中有两种SqlSource,一种是动态的,另一种是静态的。不用解释,应该都明白,一个是生成动态SQL用的,另一个这是简单静态的SQL。在SqlSource中,包括你定义的SQL语句,以及引入的外部SQL语句块。MappedStatement最后还要包括一个重要的信息,这就是ParameterMap,这直接关系你定义的SQL语句中通过#propertyName}定义的动态填充值。如果你的是一个POJO对象,那么MyBatis将会通过反射获得这个对象的属性,并依次填入到对应的propertyName所在的位置。
   注意:此时的MappedStatement中的SQL语句还是带有#{propertyName}这样占位符的字符串,还并没有解析成待问号(?)的占位符。要执行该操作是在执行具体的数据库操作的时候才替换成(?),只是为了很好的找到这个propertyName所对应的值所在的位置。
  以上就将整个SqlSession的初始化过程所做的操作进行了解剖。完成这些操作之后,那么就等待用户触发对数据库的操作了。
   后续将会给出,MyBatis是如何触发用户自定义的插件的过程以及开发自己的TypeHandlerMyBatis允许用户的插件可以拦截ParameterHandlerResultSetHandlerStatementHandlerExecutor接口,从而进行一些操作。
   本文到此继续,后续会有新的更新。如有严重不对的地方,还望各位能够及时提出,毕竟对MyBatis的接触也只有一个星期,未免有些地方不对,还望大家谅解。











  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-304938-1-1.html 上篇帖子: guice + mybatis+ resteasy 下篇帖子: MyBatis的几种批量操作
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表