myibats原理深入理解总结

1. MyBatis初始化的过程,就是创建 Configuration对象的过程

MyBatis的初始化可以有两种方式:

  • 基于XML配置文件:基于XML配置文件的方式是将MyBatis的所有配置信息放在XML文件中,MyBatis通过加载并XML配置文件,将配置文信息组装成内部的Configuration对象
  • 基于Java API:这种方式不使用XML配置文件,需要MyBatis使用者在Java代码中,手动创建Configuration对象,然后将配置参数set 进入Configuration对象中 
 mybatis使用过程:mybatis初始化 -->创建SqlSession -->执行SQL语句 返回结果三个过程
2.解析配置文件过程
处理properties节点->处理typealiases节点->处理插件->处理objectfactory->处理
objectWrapperFactory->处理settings->处理environment->处理database->处理typehandler->处理mapper(核心内容,加载相应的配置文件,分析sql语句)。

3.mybatis一级缓存
SqlSession对象中创建一个本地缓存(local cache),对于每一次查询,都会尝试根据查询的条件去本地缓存中查找是否在缓存中,如果在缓存中,就直接从缓存中取出,然后返回给用户;否则,从数据库读取数据,将查询结果存入缓存并返回给用户。 对于某个查询,根据statementId,params,rowBounds来构建一个key值,根据这个key值去缓存Cache中取出对应的key值存储的缓存结果; statementId  + rowBounds  + 传递给JDBC的SQL  + 传递给JDBC的参数值
当创建了一个SqlSession对象时,MyBatis会为这个SqlSession对象创建一个新的Executor执行器,而缓存信息就被维护在这个Executor执行器中,MyBatis将缓存和对缓存相关的操作封装成了Cache接口中。

a. MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

b. 如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用;

c. 如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用;

d.SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用;

cache配置:

  1. <cache eviction="FIFO" flushInterval="60000" size="512"   
  2. readOnly="true"/>  

4.MyBatis支持配置多个dataSource 环境,可以将应用部署到不同的环境上,如DEV(开发环境),TEST(测试换将),QA(质量评估环境),UAT(用户验收环境),PRODUCTION(生产环境),可以通过将默认environment值设置成想要的 environment id 值。

  1. <environments default="development">  
  2.     <environment id="development">  
  3.       <transactionManager type="JDBC" />  
  4.       <dataSource type="POOLED">  
  5.         <property name="driver" value="${jdbc.driverClassName}" />  
  6.         <property name="url" value="${jdbc.url}" />  
  7.         <property name="username" value="${jdbc.username}" />  
  8.         <property name="password" value="${jdbc.password}" />  
  9.       dataSource>  
  10.     environment>  
  11.     <environment id="production">  
  12.       <transactionManager type="MANAGED" />  
  13.       <dataSource type="JNDI">  
  14.         <property name="data_source" value="java:comp/jdbc/MyBatisDemoDS" />  
  15.       dataSource>  
  16.     environment>  
  17.   environments>  

5. dataSource的类型可以配置成其内置类型之一,如 UNPOOLED,POOLED,JNDI。
    - l  如果将类型设置成UNPOOLED,MyBatis会为每一个数据库操作创建一个新的连接,并关闭它。该方式适用于只有小规模数量并发用户的简单应用程序上。
    - l   如果将属性设置成POOLED,MyBatis会创建一个数据库连接池,连接池中的一个连接将会被用作数据库操作。一旦数据库操作完成,MyBatis会将此连接返回给连接池。在开发或测试环境中,经常使用此种方式。
    - 如果将类型设置成JNDI,MyBatis从在应用服务器向配置好的JNDI数据源dataSource获取数据库连接。在生产环境中,优先考虑这种方式。

6. R esultMaps被用来 将SQL SELECT语句的结果集映射到 JavaBeans的属性中。我们可以定义结果集映射ResultMaps并且在一些SELECT语句上引用resultMap。   用来作为嵌套的resultmap。MyBatis可以使用RowBounds逐页加载表数据。RowBounds对象可以使用offset和limit参数来构建。参数o
ffset表示开始位置,而limit表示要取的记录的数目。MyBatis提供了ResultHandler插件形式允许我们以任何自己喜欢的方式处理结果集ResultSet。
元素将在特定的以逗号分隔的包名列表中搜索映射器Mapper接口。

7.N+1问题: 嵌套查询就一个(即resultMap 内部就一个association标签),现查询的结果集返回条数为N,那么关联查询语句将会被执行N次,加上自身返回结果集查询1次,共需要访问数据库N+1次。如果N比较大的话,这样的数据库访问消耗是非常大的!所以使用这种嵌套语句查询的使用者一定要考虑慎重考虑,确 保N值不会很大。 Mybatis还支持一种嵌套结果的查询:即对于一对多,多对多,多对一的情况的查询,Mybatis通过联合查询,将结果从数据库内一次性查出来,然后根据其一对多,多对一,多对多的关系和ResultMap中的配置,进行结果的转换,构建需要的对象。
  1. resultMap type="com.foo.bean.BlogInfo" id="BlogInfo">  
  2.     <id column="blog_id" property="blogId"/>  
  3.     <result column="title" property="title"/>  
  4.     <association property="author" column="blog_author_id" javaType="com.foo.bean.Author">  
  5.         <id column="author_id" property="authorId"/>  
  6.         <result column="user_name" property="userName"/>  
  7.         <result column="password" property="password"/>  
  8.         <result column="email" property="email"/>  
  9.         <result column="biography" property="biography"/>  
  10.     association>  
  11.     <collection property="posts" column="blog_post_id" ofType="com.foo.bean.Post">  
  12.         <id column="post_id" property="postId"/>  
  13.         <result column="blog_id" property="blogId"/>  
  14.         <result column="create_time" property="createTime"/>  
  15.         <result column="subject" property="subject"/>  
  16.         <result column="body" property="body"/>  
  17.         <result column="draft" property="draft"/>  
  18.     collection>  
  19.       
  20. resultMap>  
  21. <select id="queryAllBlogInfo" resultMap="BlogInfo">  
  22.     SELECT   
  23.      B.BLOG_ID,  
  24.      B.TITLE,  
  25.      B.AUTHOR_ID AS BLOG_AUTHOR_ID,  
  26.      A.AUTHOR_ID,  
  27.      A.USER_NAME,  
  28.      A.PASSWORD,  
  29.      A.EMAIL,  
  30.      A.BIOGRAPHY,  
  31.      P.POST_ID,  
  32.      P.BLOG_ID   AS BLOG_POST_ID ,  
  33.   P.CREATE_TIME,  
  34.      P.SUBJECT,  
  35.      P.BODY,  
  36.      P.DRAFT  
  37. FROM BLOG B  
  38. LEFT OUTER JOIN AUTHOR A  
  39.   ON B.AUTHOR_ID = A.AUTHOR_ID  
  40. LEFT OUTER JOIN POST P  
  41.   ON P.BLOG_ID = B.BLOG_ID  
  42. select>  
执行过程:根据表的对应关系,进行join操作,获取到结果集;据结果集的信息和BlogInfo 的resultMap定义信息,对返回的结果集在内存中进行组装、赋值,构造BlogInfo;返回构造出来的结果List 结果。

8. 当我们需要创建SqlSession对象并需要执行SQL语句时,这时候MyBatis才会去调用dataSource对象来创建java.sql.Connection对象。也就是说,java.sql.Connection对象的创建一直延迟到执行SQL语句的时候。 的type属性被配置成了”UNPOOLED”,MyBatis首先会实例化一个UnpooledDataSourceFactory工厂实例,然后通过.getDataSource()方法返回一个UnpooledDataSource实例对象引用,我们假定为dataSource。使用UnpooledDataSource的getConnection(),每调用一次就会产生一个新的Connection实例对象。获得connection是通过DriverManager.getConnection()返回新的java.sql.Connection实例。 MyBatis将连接池中的PooledConnection分为两种状态: 空闲状态(idle)和活动状态(active),这两种状态的PooledConnection对象分别被存储到PoolState容器内的 idleConnections activeConnections 两个List集合中
先看是否有空闲(idle)状态下的PooledConnection对象,如果有,就直接返回一个可用的PooledConnection对象;查看活动状态的PooledConnection池activeConnections是否已满;如果没有满,则创建一个新的PooledConnection对象,然后放到activeConnections池中,然后返回此PooledConnection对象;最先进入activeConnections池中的PooledConnection对象是否已经过期:如果已经过期,从activeConnections池中移除此对象,然后创建一个新的PooledConnection对象,添加到activeConnections中,然后将此对象返回;否则线程等待。

怎样实现Connection对象调用了close()方法,而实际是将其添加到连接池中:这是要使用代理模式,为真正的Connection对象创建一个代理对象,代理对象所有的方法都是调用相应的真正Connection对象的方法实现。当代理对象执行close()方法时,要特殊处理,不调用真正Connection对象的close()方法,而是将Connection对象添加到连接池中。


9.mybats加载流程

1. 调用SqlSessionFactoryBuilder对象的build(inputStream)方法;

2. SqlSessionFactoryBuilder会根据输入流inputStream等信息创建XMLConfigBuilder对象;

3. SqlSessionFactoryBuilder调用XMLConfigBuilder对象的parse()方法;

4. XMLConfigBuilder对象返回Configuration对象;

5. SqlSessionFactoryBuilder根据Configuration对象创建一个DefaultSessionFactory对象;

6. SqlSessionFactoryBuilder返回 DefaultSessionFactory对象给Client,供Client使用。


10.MyBatis的事务Transaction的接口设计以及其不同实现JdbcTransaction  ManagedTransaction
使用JDBC的事务管理机制:即利用java.sql.Connection对象完成对事务的提交(commit())、回滚(rollback())、关闭(close())等
使用MANAGED的事务管理机制:这种机制MyBatis自身不会去实现事务管理,而是让程序的容器如(JBOSS,Weblogic)来实现对事务的管理,相应的commit,rollback代码中没有具体的实现,而是交由容器去执行。
MyBatis初始化解析节点时,会根据type来创建相应的事物机制。
如果我们使用MyBatis构建本地程序,即不是WEB程序,若将type设置成"MANAGED",那么,我们执行的任何update操作,即使我们最后执行了commit操作,数据也不会保留,不会对数据库造成任何影响。因为我们将MyBatis配置成了“MANAGED”,即MyBatis自己不管理事务,而我们又是运行的本地程序,没有事务管理功能,所以对数据库的update操作都是无效的。

11.mybatis的核心部件
SqlSession            作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要数据库增删改查功能
Executor              MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护
StatementHandler   封装了JDBC Statement操作,负责对JDBC statement 的操作,如设置参数、将Statement结果集转换成List集合。
ParameterHandler   负责对用户传递的参数转换成JDBC Statement 所需要的参数,
ResultSetHandler    负责将JDBC返回的ResultSet结果集对象转换成List类型的集合;
TypeHandler          负责java数据类型和jdbc数据类型之间的映射和转换
MappedStatement   MappedStatement维护了一条节点的封装, 
SqlSource            负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回
BoundSql             表示动态生成的SQL语句以及相应的参数信息
Configuration        MyBatis所有的配置信息都维持在Configuration对象之中。
Executor主要功能:

(1、根据传递的参数,完成SQL语句的动态解析,生成BoundSql对象,供StatementHandler使用;

(2、为查询创建缓存,以提高性能(具体它的缓存机制不是本文的重点,我会单独拿出来跟大家探讨,感兴趣的读者可以关注我的其他博文);

(3、创建JDBCStatement连接对象,传递给StatementHandler对象,返回List查询结果。


12.myibats缓存

一级缓存是Session会话级别的缓存,位于表示一次数据库会话的SqlSession对象之中,又被称之为本地缓存。一级缓存是MyBatis内部实现的一个特性,用户不能配置,默认情况下自动支持的缓存,用户没有定制它的权利(不过这也不是绝对的,可以通过开发插件对它进行修改);二级缓存是Application应用级别的缓存,它的是生命周期很长,跟Application的声明周期一样,也就是说它的作用范围是整个Application应用。

一个 SqlSession 对象会使用一个 Executor 对象来完成会话操作, MyBatis 的二级缓存机制的关键就是对这个 Executor 对象做文章。如果用户配置了" cacheEnabled=true ",那么 MyBatis 在为 SqlSession 对象创建 Executor 对象时,会对 Executor 对象加上一个装饰者: CachingExecutor ,这时 SqlSession 使用 CachingExecutor 对象来完成操作请求。 CachingExecutor 对于查询请求,会先判断该查询请求在 Application 级别的二级缓存中是否有缓存结果,如果有查询结果,则直接返回缓存结果;如果缓存中没有,再交给真正的 Executor 对象来完成查询操作,之后 CachingExecutor 会将真正 Executor 返回的查询结果放置到缓存中,然后在返回给用户。MyBatis自己定义的二级缓存实现;你也可以通过实现org.apache.ibatis.cache.Cache接口自定义缓存;也可以使用第三方内存缓存库,如Memcached
MyBatis并不是简单地对整个Application就只有一个Cache缓存对象,它将缓存划分的更细,即是Mapper级别的,为每一个Mapper分配一个Cache缓存对象(使用节点配置);多个Mapper共用一个Cache缓存对象(使用节点配置);Mapper中配置了,并且为此Mapper分配了Cache对象,这并不表示我们使用Mapper中定义的查询语句查到的结果都会放置到Cache对象之中,我们必须指定Mapper中的某条选择语句是否支持缓存,即如下所示,在