MyBatis代码分析.doc_第1页
MyBatis代码分析.doc_第2页
MyBatis代码分析.doc_第3页
MyBatis代码分析.doc_第4页
MyBatis代码分析.doc_第5页
已阅读5页,还剩29页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

MyBatisBy:satan_0103基本配置:mybatis.xmlUserMapping.xmlUser.javaMain.java代码分析一:建立SessionFactorypublic static void main(String args) throws Exception /指向配置文件路径,格式为classpathString resource = mybatis.xml; /使用Mybatis的资源读取器获得reader对象Reader reader = Resources.getResourceAsReader(resource); /建立SqlSessionFactorySqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader); /创建一个会话SqlSession session = sqlMapper.openSession();User user = new User();user.setId(1);user.setUsername(satan_0103);/调用sessionsession.insert(insertUser,user);mit();session.close();分析Resources资源查询器:基本原理:使用ClassLoader.getResourceAsStream(resource)方法实现.而Resources对象,使用ClassLoader数组对象,进行遍历查询.分析:可以使用Resources的静态方法还获得对应输入输出流的对象.调用getResourceAsStream()方法.这里使用了classLoaderWrapper这个静态对象,而这个对象包含了两个ClassLoader对象.分别为systemClassLoader和defaultClassLoader对象.继续跟进调用.这里使用了getClassLoaders()这个方法来获得系统中存在的合理的类加载对象ClassLoader,注意这里的ClassLoader不止一个.查看getClassLoader()方法,返回一个ClassLoader数组对象.返回上级进入调用getResourceAsStream()查看到对资源(mtbatis.xml)的加载,是通过对ClassLoader数组的迭代查询来获得的.并且注意到,可以使用的路径表达式为:1. org/satan/mybatis.xml2. /org/satan/mybatis.xml都可以.分析SqlSessionFactoryBuilder(工厂类)代码:原理分析:通过reader获得mybatis.xml配置文件,然后通过创建XMLConfigBuilder对象来包含mybatis.xml中的内容,最后调用XMLConfigBuilder中parse()方法,来获得一个Configuration对象,来完成SqlSessionFactory对象.基本结构:SqlSessionFactoryBuilder类是包含了一组build()方法,用来创建SqlSessionFactory对象的类.各个方法的最终目的就是产生一个Configuration对象,然后使用build(Configuration)方法,来创建SqlSessionFactory对象.代码跟踪和分析:下面的代码是进入了XMLConfigBuilder对象创建,和其对象方法parse()调用产生Configuration对象,并且最后调用build(Configuration)的过程.reader是指向mybatis.xml对象的IO流.environment 是显示指示使用那个environment配置环境,如果不指定,则使用default=some的环境.properties 指定了替代属性的Key-Value ,$username进入XMLConfigBuilder过程.首先创建一个XPathParser解析对象,这个对象是对MybatisConfiguration.xml对象的解析器,解析出一段Document格式的报文.还使用XMLMapperEntityResolver()对象来验证mybatis.xml使用的DTD约束对象是否正确.在this()自构造方法中,看到new 出一个Configuration对象.而在Configuration构造方法中,是对typeAliasRegistry对象预先填充一些必要的别名,如JDBC,POOLED,UNPOOLED等而其中VENDOR别名对象设置过程中启动了log,日志系统.值得注意的是typeAliasRegistry在构造的时候自己也注册了一些别名.org.apache.ibatis.mapping.VendorDatabaseIdProvider为日志辅助类在ClassLoader加载它的时候,执行如下代码private static final Log log = LogFactory.getLog(BaseExecutor.class);在LogFactory中,看到static代码,就是这段代码完成了对日志系统的初始化.回到原来的build()方法,我们查看parser.parse()方法的执行.查看到这个方法是对mybatis.xml中configuration标签的解析.进入parseConfiguration()方法后,可以看到一些熟悉的字样,接下来就是对configuration标签的子元素进行解析,填充完一个Configuration对象中的属性,返回这个Configuration对象给上级build().对properties标签解析:可以看到最后是在configuration中设置解析完成的properties.对typeAliases标签解析:如果没有使用alias元素,则直接把type 类路径指向的类名作为alias注册到Configuration对象中去.对objectFactory标签解析:把factory对象注册到configuration的ObjectFactory属性中.对environments标签解析:可以看到参数evironment设置了启动哪个环境配置.如果没有制定,则使用default指向的环境.在配置evironment过程中,总共配置了1. transactionManager(事务管理)2. dataSource(数据源)两个内容.最后把配置完成的环境交给configuration对象.transactionManager(事务管理):首先获得transactionManager的type类型,接着用通过resolveClass(type)对象,获得合适的TransactionFactory对象把type设置为别名,调用resolveAlias()方法解析,从configuration别名容器中获取对应的Class来实例化.最后却是使用configuration中对typeAlias标签配置默认值进行解析alias,来获得一个合适的TransactionFactory对象的Class对象.dataSource(数据源):数据源配置和事务配置类似,通过别名来获得configuration容器中一个Class填充相应的属性就可以使用了.对typeHandlers标签分析:也利用了configuration中容器,把type作为别名解析.其他标签的实现也是类是的方式.现在回到build(Configuration),使用config创建了一个默认的SqlSessionFactory对象.可以发现整个DefaultSqlSessionFactory对象就包含了一个Configuration对象.到此 完成了对SqlSessionFactory对象的创建.基本过程如下:1. 使用Resources中静态方法查询获得reader对象2. 通过XMLConfigBuilder构造方法对reader进行读取,具体的读取工作给XPathParser对象完成返回一份Document对象,然后配置类的属性,完成构造过程.3. 通过XMLConfigBuilder对象的parse()方法对读取到的文档(Document)进行解析对mybatis.xml中标签内容进行解析,设置到一个Configuration对象中而这个过程中,多次利用到typeAlias标签对象的Configuration属性操作通过标签属性type转化为alias来查询容器中相应的Class对象.4. 最后new 出一个DefaultSqlSessionFactory 对象,而这个Factory属性中只是包含了Configuration对象.所以XMLConfigBuilder对象完成了对Configuration的构造.注意:这个过程中使用的日志系统,是通过static代码来完成到底mybatis使用哪个日志系统,首选是slf4j日志接口.短语的实现,平时我们使用selectUser就代替了domain.selectUser,即使没有进行注册也可以成功,的原因是在注册domain.selectUser的时候,自动注册了selectUser这个短语.对于mapping和typeHandler的解释:mapping对于一个复杂的客户类映射,而typeHandler对应的是简单的Java和JDBC字段的映射,使用的参数不同mapping使用 resultMaptypeHandler使用resultType.Session打开工作首先打开一个Session.可以看API函数名,打开一个Session从DataSource中.接着就进入了具体的函数.使用的事务管理是来自于configuration中配置的JDBC事务管理.默认不为自动提交(autoCommit),所以我们要手动提交mit();然后获得一个executor执行体对象并且启动了cache缓存,最后配置DefaultSqlSession对象,返回并且使用.1. 事务的构造过程.以下是事务工厂类获取过程,可以看到如果没配置事务工厂的话,则使用ManagedXXX事务管理工厂类,这个类在commit()和rollback()的时候什么都没有做!现在获得的是JdbcTransactionFactory对象,依靠Jdbc连接自己管理事务(要自己手动显示的使用mit()来完成对事务的提交和session.rollback()事务回滚).可以看到在构造JdbcTransaction的时候把数据源DataSource和autocommit参数递交给了JdbcTransaction.注意一个事务的提交commit()和回滚rollback()最后都是由事务管理完成的.2. 执行体的构造.执行体是Mybatis在执行一次CURD操作时候控制的对象,把具体的任务发布给执行体,由执行体完成CRUD的过程,并且执行体包含了缓存特性.一个执行体的设计模式为代理模式.原因是如果当前执行体不能执行CRUD操作(缓存中没有对象),则委托给下一个执行体完成,最基本的执行体为BaseExecutor和它的子类.在Mybatis中,执行体基本上分为两类,一类是 BaseExecutor 和它的子类,这类执行体称为一级缓存,而CachingExecutor被称为二级缓存.在下面这段代码中,可以看到对BaseExecutor 的子类SimpleExecutor的创建,和CachingExecutor的创建,并且CachingExecutor可以委托CachingExecutor完成CRUD操作.注意CachingExecutor是否启动要看是否配置启动了二级缓存.一级缓存一直都是打开的.而查询的先后顺序为,首先查询CachingExecutor然后再BaseExecutor 即二级缓存再一级缓存最后才是数据库.3. DefaultSqlSession的设置.DefaultSqlSession是一个SqlSession的实现类构造过程就是简单的设置configuration和他的执行体最后还有一个脏读标志dirty.在整个openSession()过程中,可以认为是对事务管理工厂获得一个事务管理实例,和配置一个执行体的过程.Mybatis 缓存机制一级缓存基于 PerpetualCache 的 HashMap 本地缓存,其存储作用域为 Session,而生命周期为一次事务, flush 或 close 之后,该Session中的所有 localCache 就将清空。二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),生命周期被configuration对象管理,所以不同的session可以使用同一个cache,并且可自定义存储源,如 Ehcache、Hazelcast等。对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。只有select 才会被缓存记录.一. session.insert()的过程.可以看到其实是对update方法的封装而起.进入update()看到把写入脏标记置为true了通过statement获得对应的MappedStatement对象,这个对象包含了具体的Mapping标签内容.最后通过executor执行体,完成update操作.注意的是首先有对parameter进行包装,如果是List或者Array对象的话,则包装成为一个Map对象,并且key=list|array,如果不是的话,直接使用原来对象,所以可以在mapping.xml中定义parameterType为Map对象,好处就是比较方便的传递参数.当然可以直接使用其他类型或者List.首先检查是否要刷新缓存然后通过代理完成update()可以看到从ms中获得它对应的cache对象,如何配置ms的cache对象呢?是通过在mapping.xml中添加标签既可以实现,注意这里的一个ms对象是每一条或,但是一个namespace中所有CRUD都对应一个cache对象,cache对象受管于tcm对象-一个CachingExecutor的缓存管理类返回上级,现在被委托于BaseExecutor的子类方法update()首先清理了本地缓存对象,一级缓存.进入doUpdate()获得一个StatementHandler封装对象,包含了本次JDBC动作所有必要数据,但是没有包含一个打开的连接Connection.然后使用prepareStatement()方法对handler进行处理,返回一个合格的Statement对象,最后调用JDBC方法完成update().我们查看prepareStatement()可以看到首先获得connnection对象,然后预处理stmt对象最后设置参数.获得connection对象的过程是通过transaction事务管理对象获得的.并且默认设置了autoCommit为false. 回到提交main中,在一次插入过后我们要提交他们,如果不提交的话,dirty标志不能清除,最后在session.close()的时候会回滚到上次提交点.现在进入mit()过程.设置不是强迫提交事务.意思为如果dirty=false的话不用提交事务,因为没有执行update,insert,delete操作.进入commit(false)把任务提交给执行体,并且判断是否一定需要commit()操作.进入CachingExecutor查看commit()对于代理直接使用提交对于本身缓存也发布了commit最后清理了dirty标志.BaseEmit()可以看到清理了本地缓存,说明一级缓存的作用范围是一次事务.并且最后看到对事务进行提交,具体commit交给transaction接口的实现者.比如在Spring中commit不进行mit(true)方法,直接把连接还给容器.2. session.selectOne()过程一次Select的过程首先查询二级缓存,如果二级缓存中没有的话,则通过代理查询BaseExcutor,而BaseExecutor首先查询一级缓存,如果没有的话则从数据库中获取.因为一级缓存是默认开启的,由此解决的循环依赖查询的问题.具体代码如下.调用selectList()方法,取第0个数据即可进入selectList(),首先获得对应的MappingStatement对象,然后调用query()方法完成查询工作.这个ms对象中包含了由它自己管理的缓存,而ms托管于configuration,所以不同session能使用同一个cache,因为他们共享同一个configuration.创建了一个Key,用来查询缓存,如果缓存中已经有了此项,则直接引用即可.查询ms中的缓存,由此可以知道,二级缓存的最小单位为MappingStatement.在XML中表示为或者之类,不过包含的缓存不然.如果缓存中没有或者已经更新dirty=false了,则使用代理查询,对于查询结果,通过tcm对象管理.进入使用代理查询,SimpleExecute对象为代理.可以看到这里有一级缓存PerpetualCache localeCache.如果缓存中不存在数据,则查询数据库queryFromDatabase();查询数据库的过程中,可以看到启用了缓存对结果进行保存了,这是为了防止循环依赖问题查询过程是doQuery();创建一个StatementHandler对象,用来包装MappingStatement,parameter,.的信息.然后通过预处理prepareStatement()来获得一个Statement对象,最后进行查询,完成这一切工作后,关闭了Statement对象.在预处理prepareStatement()过程中,完成1. 从连接池获取连接Connnect,通过transaction.getConnect();2. 对SQL对象参数的填充在query过程中.完成了查询和对结果集的处理!在结果集处理中主要的过程有:1. 获得一个ResultSet对象2. 使用ResultSet和configuration对象穿件一个结果处理器.3. 进行结果处理handleResultSet();如果没有自定义的结果处理器resultHandler对象则使用默认的结果处理.DefaultResultHandler;最后关闭一个用完的ResultSet对象.处理ResultSet中每一行的数据.可以看到getRowValue函数中首先创建resultMap对象指定的type对象,然后更具typeHandlerRegistry对象是否包含type决定是否进行mapping.所以对ResultSet结果集处理的过程为:1. typeHandler注册的类型2. resultMap(resultType-这个类型是resultMap的自动映射类型)因为如果typeHandlerRegistry包含type指向的对象,表示这个type是一个简单的可以直接从JDBC对象转换成为Java对象的对象类型.并且每次都创建一个对象resultObject,这就导致最后组合的Bean中没有空值的属性,因为每个属性都有被创建.回到mit(),注意只有在commit()之后,cache保存的数据才能使用.直接委托于CachingExecutor的commitCachingEmit首先让代理BaseExecutor提交,它的提交过程就是清除缓存,然后使用事务的commit完成操作过程接着使用mit过程.迭代的获取tcm中管理缓存的对象,进行commit操作.一个TransactionalCache对象首先检查是否需要清理,如果使得话则直接进入清理状态.否则进入添加实体,也就是本次select完成的对象.最后回到session.close()过程首先查看是否需要回滚.回滚与否在于dirty=true和是否强制.然后关闭任务递交给CachingExecutorcachingExecutor.close();看到要对tcm进行commit操作,操作的具体过程就是清空了.然后委托于BaseExecutorBaseExecutor.close()看到是否强制回滚设置.然后就是事务的关闭了.transaction.close();具体的事务关闭要看事务的实现类了.总的来说缓存顺序图为:类图结构总结:1.可以发现一个session执行成功要调用mit()方法.并且session是线程不安全对象.2. 在一次更新工作后,如果不提交mit()的话,导致dirty标志不能清除,最后在session.close()的时候会回滚到上次提交点.3. 双向关联的机制依赖于一级缓存机制.4. Mybatis的缓存机制在于CachingExecutor(二级缓存)和BaseExecutor(一级缓存).5. MyBatis的事务管理是参考具体的事务实现类.如JDBC和Spring.6. 一次查询过程中,首先查询二级缓存,之后查询一级缓存,最后查询数据库而一级缓存的作用范围是一个事务过程,事务提交了,也就没有了.二级缓存不然.7. 一次更新操作会导致对应的二级缓存cache清除(在一个namespace下).8. 二级缓存要生效要在mit()之后才有效.9. resultType和resultMap其实是一样的,前者是resultMap开启自动映射的结果.10. 组合完成的Bean基本上都没有空值的属性,因为每个属性都被反射创建对象,即使没有数值.11. tcm是一个session管理cache的对象,而cache真正的属主是ms对象,ms对象被configuration对象管理.所以一个session即使关闭了,ms对象还是可以使用.如下是JDBC和MyBatis对象之间的联系:Spring-MyBatis事务处理Spring-MyBatis的事务中,使用事务类工厂为 SpringManagedTransactionFactory事务类为 SpringManagedTransactionSpringManagedTransaction把DataSourceUtils作为连接处理的类整个Spring事务由DataSourceTransactionManager 来管理.并且SqlSessionTemplate实现了SqlSession接口,当时具体的代码不同于原来的DefaultSqlSession,实现机制是对原来的DefaultSqlSession进行代理.一段插入代码使用sqlSessionProxy代理完成操作,而这里代理的初始化在SqlSessionTemplate的构造函数中使用JDK动态代理完成.首先获得一个SqlSession对象,这个对象根据当前线程是否已经具有SqlSession对象来确定,而产生的SqlSession对象的实现类型是DefaultSqlSession类型,也就是没有整合前的类型.然后调用SqlSession对象的方法.执行完成后,通过函数isSqlSessionTransactional()来判断当前是否属于Spring事务管理器工作中,如果是的话则不提交当前SqlSession否则则提交.最后调用closeSqlSession()函数完成对资源的释放,释放的方法也有两种类型,一种是当前在事务管理中,则就是简单的把引用计数器减一,另外一种就是直接关闭SqlSession这保证了每次线程对session.close操作获得一个SqlSession对象:通过getResource()函数,从当前线程中提取SqlSessionHolder对象,如果有则引用计数器加一,否则调用SqlSessionFactory.openSession()方法完成打开一个Session,而这个方法调用的最后会使用SpringManagedTransaction 事务管理类来获得一个连接,最后组合成为一个SqlSession.接着判断是否当前处于Spring事务管理中,如果是的话,把这个SqlSession绑定到当前线程上,不是的话直接返回它.通过JDK的代理机制,完成了对DefaultSqlSession的代理,从而使得DefaultSqlSession能在Spring容器这种多线程的环境下工作.MyBatis延迟加载当mybatis-config.xml中配置使用Mybatis的延迟加载的时候,就可以使用延迟加载效果了,而且比hibernate好的地方是在session关闭以后还能使用延迟查询.延迟加载技术使用CGLIB代理.在对对象使用获取属性操作的时候,会引发代理功能. intercept()lazyLoader.loadAll();直接跟踪到重要代码.如果执行器关闭了,则打开一个新的执行器.执行器查询.最后关闭执行器.API总结:SqlSessionFactoryBuilder:SqlSessionFactory build(Reader reader) SqlSessionFactory build(Reader reader, String environment) SqlSessionFactory build(Reader reader, Properties properties) SqlSessionFactory build(Reader reader, String env, Properties props) SqlSessionFactory build(Configuration config) 参数解析:reader - 指向config.xml 的readerenv - 指定使用哪个配置环境properties - 额外添加的key-valueSqlSessionFactory: SqlSessionFactory有六个方法可以用来创建SqlSession实例。通常来说,如何决定是你选择下面这些方法时: l Transaction(事务):你想为session使用事务或者使用自动提交(通常意味着很多数据库和/或JDBC驱动没有事务)? l Connection(连接):你想MyBatis获得来自配置的数据源的连接还是提供你自己定义的连接? l Execution(执行):你想MyBatis复用预处理语句和/或批量更新语句(包括插入和删除)? 重载的openSession()方法签名设置允许你选择这些可选中的任何一个组合。 SqlSession openSession() SqlSession openSession(boolean autoCommit) SqlSession openSession(Connection connection) SqlSession openSession(TransactionIsolationLevel level) SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) SqlSession openSession(ExecutorType execType) SqlSession openSession(ExecutorType execType, boolean autoCommit) SqlSession openSession(ExecutorType execType, Connection connection) Configuration getConfiguration(); 默认的openSession()方法没有参数,它会创建有如下特性的SqlSession: l 将会开启一个事务(也就是不自动提交)范围 l 连接对象会从由活动环境配置的数据源实例中得到。 l 事务隔离级别将会使用驱动或数据源的默认设置。 l 预处理语句不会被复用,也不会批量处理更新。 SqlSession 如上面所提到的,SqlSession实例在MyBatis中是非常强大的一个类。在这里你会发现所有执行语句的方法,提交或回滚事务,还有获取映射器实例。 在SqlSession类中有超过20个方法,所以将它们分开成易于理解的组合。 语句执行方法 这些方法被用来执行定义在SQL映射的XML文件中的SELECT,INSERT,UPDAT E和DELETE语句。它们都会自行解释,每一句都使用语句的ID属性和参数对象,参数可以是原生类型(自动装箱或包装类),JavaBean,POJO或Map。 Object selectOne(String statement, Object parameter) List selectList(String statement, Object parameter) int insert(String statement, Object parameter) int update(String statement, Object parameter) int delete(String statement, Object parameter) selectOne和selectList的不同仅仅是selectOne必须返回一个对象。如果多余一个,或者没有返回(或返回了null),那么就会抛出异常。如果你不知道需要多少对象,使用selectList。如果你想检查一个对象是否存在,那么最好返回统计数(0或1)。因为并不是所有语句都需要参数,这些方法都是有不同重载版本的,它们可以不需要参数对象。 Object selectOne(String statement) List selectList(String statement) int insert(String statement) int update(String statement) int delete(String statement) 最后,还有查询方法的三个高级版本,它们允许你限制返回行数的范围,或者提供自定义结果控制逻辑,这通常用于大量的数据集合。 List selectList (String statement, Object parameter, RowBounds rowBounds) void select (String statement, Object parameter, ResultHandler handler) void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论