实例
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.*;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
private SqlSessionFactory sqlSessionFactory;
@Before
public void prepare() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testSelect() throws IOException {
SqlSession session = sqlSessionFactory.openSession(); // ExecutorType.BATCH
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlogById(1);
System.out.println(blog);
} finally {
session.close();
}
}
public interface BlogMapper {
/**
* 根据主键查询文章
* @param bid
* @return
*/
public Blog selectBlogById(Integer bid);
/**
* 根据实体类查询文章
* @param blog
* @return
*/
public List<Blog> selectBlogByBean(Blog blog);
/**
* 文章列表翻页查询
* @param rowBounds
* @return
*/
public List<Blog> selectBlogList(RowBounds rowBounds);
public List<Blog> selectBlogListIf(Blog blog);
public List<Blog> selectBlogListChoose(Blog blog);
public void deleteByList(List<Blog> list);
/**
* 更新博客
* @param blog
* @return
*/
public int updateByPrimaryKey(Blog blog);
/**
* 新增博客
* @param blog
* @return
*/
public int insertBlog(Blog blog);
/**
* 批量插入博客
* @param list
* @return
*/
public int insertBlogList(List<Blog> list);
/**
* 批量更新博客
* @param list
* @return
*/
public int updateBlogList(List<Blog> list);
/**
* 根据博客查询作者,一对一,嵌套结果
* @param bid
* @return
*/
public BlogAndAuthor selectBlogWithAuthorResult(Integer bid);
/**
* 根据博客查询作者,一对一,嵌套查询,存在N+1问题
* @param bid
* @return
*/
public BlogAndAuthor selectBlogWithAuthorQuery(Integer bid);
/**
* 查询文章带出文章所有评论(一对多)
* @param bid
* @return
*/
public BlogAndComment selectBlogWithCommentById(Integer bid);
/**
* 查询作者带出博客和评论(多对多)
* @return
*/
public List<AuthorAndBlog> selectAuthorWithBlog();
List<Blog> selectByExample(BlogExample example);
}
public class Blog implements Serializable{
Integer bid; // 文章ID
String name; // 文章标题
Integer authorId; // 文章作者ID
public Integer getBid() {
return bid;
}
public void setBid(Integer bid) {
this.bid = bid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAuthorId() {
return authorId;
}
public void setAuthorId(Integer authorId) {
this.authorId = authorId;
}
@Override
public String toString() {
return "Blog{" +
"bid=" + bid +
", name='" + name + '\'' +
", authorId='" + authorId + '\'' +
'}';
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"></properties>
<settings>
<!-- 打印查询语句 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
<!-- 控制全局缓存(二级缓存),默认 true-->
<setting name="cacheEnabled" value="true"/>
<!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖-->
<setting name="aggressiveLazyLoading" value="true"/>
<!-- Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
<!--<setting name="proxyFactory" value="CGLIB" />-->
<!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 -->
<!--
<setting name="localCacheScope" value="STATEMENT"/>
-->
<setting name="localCacheScope" value="SESSION"/>
</settings>
<typeAliases>
<typeAlias alias="blog" type="com.gupaoedu.domain.Blog" />
</typeAliases>
<!-- <typeHandlers>
<typeHandler handler="com.gupaoedu.type.MyTypeHandler"></typeHandler>
</typeHandlers>-->
<!-- 对象工厂 -->
<!-- <objectFactory type="com.gupaoedu.objectfactory.GPObjectFactory">
<property name="gupao" value="666"/>
</objectFactory>-->
<!-- <plugins>
<plugin interceptor="com.gupaoedu.interceptor.SQLInterceptor">
<property name="gupao" value="betterme" />
</plugin>
<plugin interceptor="com.gupaoedu.interceptor.MyPageInterceptor">
</plugin>
</plugins>-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="BlogMapper.xml"/>
<mapper resource="BlogMapperExt.xml"/>
</mappers>
</configuration>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gupaoedu.mapper.BlogMapper">
<!-- 声明这个namespace使用二级缓存 -->
<!-- <cache/>-->
<!-- 使用Redis作为二级缓存 -->
<!--
<cache type="org.mybatis.caches.redis.RedisCache"
eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-->
<cache type="org.apache.ibatis.cache.impl.PerpetualCache"
size="1024"
eviction="LRU"
flushInterval="120000"
readOnly="false"/>
<resultMap id="BaseResultMap" type="blog">
<id column="bid" property="bid" jdbcType="INTEGER"/>
<!--
<result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.gupaoedu.type.MyTypeHandler"/>
-->
<result column="name" property="name" jdbcType="VARCHAR"/>
<result column="author_id" property="authorId" jdbcType="INTEGER"/>
</resultMap>
<!-- 根据文章查询作者,一对一查询的结果,嵌套查询 -->
<resultMap id="BlogWithAuthorResultMap" type="com.gupaoedu.domain.associate.BlogAndAuthor">
<id column="bid" property="bid" jdbcType="INTEGER"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<!-- 联合查询,将author的属性映射到ResultMap -->
<association property="author" javaType="com.gupaoedu.domain.Author">
<id column="author_id" property="authorId"/>
<result column="author_name" property="authorName"/>
</association>
</resultMap>
<!-- 另一种联合查询(一对一)的实现,但是这种方式有“N+1”的问题 -->
<resultMap id="BlogWithAuthorQueryMap" type="com.gupaoedu.domain.associate.BlogAndAuthor">
<id column="bid" property="bid" jdbcType="INTEGER"/>
<result column="name" property="name" jdbcType="VARCHAR"/>
<association property="author" javaType="com.gupaoedu.domain.Author"
column="author_id" select="selectAuthor"/> <!-- selectAuthor 定义在下面-->
</resultMap>
<!-- 查询文章带评论的结果(一对多) -->
<resultMap id="BlogWithCommentMap" type="com.gupaoedu.domain.associate.BlogAndComment" extends="BaseResultMap" >
<collection property="comment" ofType="com.gupaoedu.domain.Comment">
<id column="comment_id" property="commentId" />
<result column="content" property="content" />
</collection>
</resultMap>
<!-- 按作者查询文章评论的结果(多对多) -->
<resultMap id="AuthorWithBlogMap" type="com.gupaoedu.domain.associate.AuthorAndBlog" >
<id column="author_id" property="authorId" jdbcType="INTEGER"/>
<result column="author_name" property="authorName" jdbcType="VARCHAR"/>
<collection property="blog" ofType="com.gupaoedu.domain.associate.BlogAndComment">
<id column="bid" property="bid" />
<result column="name" property="name" />
<result column="author_id" property="authorId" />
<collection property="comment" ofType="com.gupaoedu.domain.Comment">
<id column="comment_id" property="commentId" />
<result column="content" property="content" />
</collection>
</collection>
</resultMap>
<!-- ===============以上是resultMap定义================= -->
<select id="selectBlogById" resultMap="BaseResultMap" statementType="PREPARED" >
select * from blog where bid = #{bid}
</select>
<!-- $只能用在自定义类型和map上 -->
<select id="selectBlogByBean" parameterType="blog" resultType="blog" >
select bid, name, author_id authorId from blog where name = '${name}'
</select>
<select id="selectBlogList" resultMap="BaseResultMap" >
select bid, name, author_id authorId from blog
</select>
<!-- 动态SQL where 和 if -->
<select id="selectBlogListIf" parameterType="blog" resultMap="BaseResultMap" >
select bid, name, author_id authorId from blog
<where>
<if test="bid != null">
AND bid = #{bid}
</if>
<if test="name != null and name != ''">
AND name LIKE '%${name}%'
</if>
<if test="authorId != null">
AND author_id = #{authorId}
</if>
</where>
</select>
<!-- 动态SQL choose -->
<select id="selectBlogListChoose" parameterType="blog" resultMap="BaseResultMap" >
select bid, name, author_id authorId from blog
<where>
<choose>
<when test="bid !=null">
bid = #{bid, jdbcType=INTEGER}
</when>
<when test="name != null and name != ''">
AND name LIKE CONCAT(CONCAT('%', #{name, jdbcType=VARCHAR}),'%')
</when>
<when test="authorId != null ">
AND author_id = #{authorId, jdbcType=INTEGER}
</when>
<otherwise>
</otherwise>
</choose>
</where>
</select>
<!-- 动态SQL set -->
<update id="updateByPrimaryKey" parameterType="blog">
update blog
<set>
<if test="name != null">
name = #{name,jdbcType=VARCHAR},
</if>
<if test="authorId != null">
author_id = #{authorId,jdbcType=CHAR},
</if>
</set>
where bid = #{bid,jdbcType=INTEGER}
</update>
<!-- 动态SQL trim -->
<insert id="insertBlog" parameterType="blog">
insert into blog
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="bid != null">
bid,
</if>
<if test="name != null">
name,
</if>
<if test="authorId != null">
author_id,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="bid != null">
#{bid,jdbcType=INTEGER},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
<!-- #{name,jdbcType=VARCHAR,typeHandler=com.gupaoedu.type.MyTypeHandler}, -->
</if>
<if test="authorId != null">
#{authorId,jdbcType=INTEGER},
</if>
</trim>
</insert>
<!-- foreach 动态SQL 批量插入 -->
<insert id="insertBlogList" parameterType="java.util.List">
insert into blog (bid, name, author_id)
values
<foreach collection="list" item="blogs" index="index" separator=",">
( #{blogs.bid},#{blogs.name},#{blogs.authorId} )
</foreach>
</insert>
<!-- foreach 动态SQL 批量删除 -->
<delete id="deleteByList" parameterType="java.util.List">
delete from blog where bid in
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item.bid,jdbcType=INTEGER}
</foreach>
</delete>
<!-- foreach 动态SQL 批量更新-->
<update id="updateBlogList">
update blog set
name =
<foreach collection="list" item="blogs" index="index" separator=" " open="case bid" close="end">
when #{blogs.bid} then #{blogs.name}
</foreach>
,author_id =
<foreach collection="list" item="blogs" index="index" separator=" " open="case bid" close="end">
when #{blogs.bid} then #{blogs.authorId}
</foreach>
where bid in
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item.bid,jdbcType=INTEGER}
</foreach>
</update>
<!-- 根据文章查询作者,一对一,嵌套结果,无N+1问题 -->
<select id="selectBlogWithAuthorResult" resultMap="BlogWithAuthorResultMap" >
select b.bid, b.name, b.author_id, a.author_id , a.author_name
from blog b
left join author a
on b.author_id=a.author_id
where b.bid = #{bid, jdbcType=INTEGER}
</select>
<!-- 根据文章查询作者,一对一,嵌套查询,存在N+1问题,可通过开启延迟加载解决 -->
<select id="selectBlogWithAuthorQuery" resultMap="BlogWithAuthorQueryMap" >
select b.bid, b.name, b.author_id, a.author_id , a.author_name
from blog b
left join author a
on b.author_id=a.author_id
where b.bid = #{bid, jdbcType=INTEGER}
</select>
<!-- 嵌套查询 -->
<select id="selectAuthor" parameterType="int" resultType="com.gupaoedu.domain.Author">
select author_id authorId, author_name authorName
from author where author_id = #{authorId}
</select>
<!-- 根据文章查询评论,一对多 -->
<select id="selectBlogWithCommentById" resultMap="BlogWithCommentMap" >
select b.bid, b.name, b.author_id authorId, c.comment_id commentId, c.content
from blog b, comment c
where b.bid = c.bid
and b.bid = #{bid}
</select>
<!-- 根据作者文章评论,多对多 -->
<select id="selectAuthorWithBlog" resultMap="AuthorWithBlogMap" >
select b.bid, b.name, a.author_id authorId, a.author_name authorName, c.comment_id commentId, c.content
from blog b, author a, comment c
where b.author_id = a.author_id and b.bid = c.bid
</select>
<!-- 手动实现翻页,没有对应方法,取消注释会报错 -->
<!-- <select id="selectBlogPage" parameterType="map" resultMap="BaseResultMap">
select * from blog limit #{curIndex} , #{pageSize}
</select>-->
<!-- 自动生成的Example -->
<sql id="Base_Column_List">
bid, name, author_id
</sql>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<select id="selectByExample" parameterType="com.gupaoedu.domain.BlogExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
'true' as QUERYID,
<include refid="Base_Column_List" />
from blog
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
</mapper>
官网
TypeAliasRegistry
里面是别名的配置。
可以自定义:
<typeAliases>
<typeAlias alias="blog" type="test.Blog" />
</typeAliases>
TypeHandlerRegistry
里面是Java的类型和JDBC的类型的对应关系。
可以自定义:
<typeHandlers>
<typeHandler handler="test.MyTypeHandler"></typeHandler>
</typeHandlers>
Mysql5.7支持JSON类型。
DefaultObjectFactory
将ResultSet转换成Object或者List<Object>
<objectFactory type="test.MyObjectFactory">
<property name="" value=""/>
</objectFactory>
动态SQL
动态SQL
if
choose (when, otherwise)
trim (where, set)
foreach
缓存
PerpetualCache implements Cache
一级缓存
一级缓存(本地缓存在Executor里)
public class DefaultSqlSession implements SqlSession {
private final Configuration configuration;
private final Executor executor;
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory.getLog(BaseExecutor.class);
protected Transaction transaction;
protected Executor wrapper;
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
protected PerpetualCache localCache;
protected PerpetualCache localOutputParameterCache;
同一个SqlSession,同一个查询,第二次会走缓存。如果执行了更新操作,会清空缓存。
一级缓存的作用域是SqlSession。同一个数据,一个SqlSession改了,另一个SqlSession走一级缓存时不会感知到改动。
一级缓存默认打开。
改一级缓存作用域(默认Session)为Statement,一个语句一个缓存:
<setting name="localCacheScope" value="STATEMENT"/>
二级缓存
二级缓存的作用域是mapper。
二级缓存默认打开。
关闭(全局):
<setting name="cacheEnabled" value="false"/>
mapper对应没有二级缓存,加:
<cache type="org.apache.ibatis.cache.impl.PerpetualCache"
size="1024"
eviction="LRU"
flushInterval="120000"
readOnly="false"/>
CachingExecutor:
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
@Override
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
mapper.xml里的增删改查标签:
useCache配置true、false:是否启用二级缓存
flushCache配置true、false:执行SQL后是否清空二级缓存
源码
SqlSessionFactoryBuilder的build方法
SqlSessionFactoryBuilder的build方法会解析mapper-config.xml,存放在Configuration。解析mapper.xml,增删改查存放在MappedStatement;Mapper对应的Class作为Key,new MapperProxyFactory<>(Mapper对应的Class)作为Value存放在Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
sqlSessionFactory.openSession()
Excutor
session.getMapper
XXXMapper xxxMapper = session.getMapper(XXXMapper.class);
SqlSessionFactoryBuilder的build方法会走到:mapperRegistry.addMapper(type);
session.getMapper会走到:mapperRegistry.getMapper(type, sqlSession);
依靠JDK的动态代理生成Mapper接口的实现类:
执行增删改查
二级缓存:
二级缓存没启动或者返回null就使用一级缓存。
一级缓存:
CacheKey:
Mybatis中的设计模式
代理模式:MapperProxy
工厂模式:SqlSessionFactory、MapperProxyFactory
建造者模式:SqlSessionFactoryBuilder
享元模式:MapperRegistry
装饰器模式:TransactionalCache
委派模式:CachingExecutor
策略模式:Configuration
责任链模式:InterceptorChain
流程总结
加载Mybatis Configuration和Mapper(对应SqlSessionFactoryBuilder的build方法)。创建Mapper对应的执行器(对应SqlSessionFactory的openSession方法)。创建Mapper对应的代理类(对应SqlSession的getMapper方法)。执行读写(调用Mapper代理类的方法)。
数据处理
MappedStatement中有SqlSource,SqlSource中有BoundSql,BoundSql中有sql和ParameterMapping LIst,ParameterMap中有property(propertyName)、jdbcType、javaType。ParameterHandler处理参数转换(有parameterObject)。StatementHandler处理Statement。ResultSetHandler处理ResultSet。TypeHandlerRegistry注册参数映射(register TypeHandler)。
拦截链(插件存储)
加载:
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 4.0.0以后版本可以不设置该参数 ,可以自动识别
<property name="dialect" value="mysql"/> -->
<!-- 该参数默认为false -->
<!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
<!-- 和startPage中的pageNum效果一样-->
<property name="offsetAsPageNum" value="true"/>
<!-- 该参数默认为false -->
<!-- 设置为true时,使用RowBounds分页会进行count查询 -->
<property name="rowBoundsWithCount" value="true"/>
<!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
<!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)-->
<property name="pageSizeZero" value="true"/>
<!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
<!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
<!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
<property name="reasonable" value="true"/>
<!-- 3.5.0版本可用 - 为了支持startPage(Object params)方法 -->
<!-- 增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值 -->
<!-- 可以配置pageNum,pageSize,count,pageSizeZero,reasonable,orderBy,不配置映射的用默认值 -->
<!-- 不理解该含义的前提下,不要随便复制该配置 -->
<property name="params" value="pageNum=start;pageSize=limit;"/>
<!-- 支持通过Mapper接口参数来传递分页参数 -->
<property name="supportMethodsArguments" value="true"/>
<!-- always总是返回PageInfo类型,check检查返回类型是否为PageInfo,none返回Page -->
<property name="returnPageInfo" value="check"/>
</plugin>
</plugins>
使用:
举例
应用
分表、鉴权、数据加解密
Spring集成Mybatis
官网
Spring的InitializingBean:加载Bean后执行afterPropertiesSet()
SqlSession
DefaultSqlSession线程不安全,Spring用JDK动态代理为每次请求生成SqlSession。
拿到SqlSessionTemplate:
总结
Mybatis负责对象关系映射。
SqlSessionFactoryBuilder执行build,得到SqlSessionFactory。sqlSessionFactory执行openSession,得到SqlSession。SqlSession执行getMapper,传进Mapper.class,得到Mapper。Mapper执行增删改查。
SqlSessionFactoryBuilder执行build,XMLConfigBuilder会解析mybatis-config.xml,放进Configuration,MapperRegistry解析mapper.xml,存进knownMappers,key是mapperClass,value是MapperProxyFactory。
——————————————————————————————————
sqlSessionFactory执行openSession,Configuration会执行newExecutor,实例化Executor。
Executor有SimpleExecutor、BatchExecutor、ReuseExecutor、CachingExecutor。
二级缓存没启动时,默认SimpleExecutor。
BatchExecutor可以调用Statement的executeBatch,批量操作sql。
CachingExecutor负责二级缓存。
——————————————————————————————————
一级缓存在BaseExecutor中,变量名是localCache,默认开启。localCache对应的类是PerpetualCache,里面维护一个map,key是CacheKey,value是查询结果。查询时,map里有同一个sql的结果,就走缓存。增删改时清除缓存。范围默认是SqlSession,可以改成Statement。
二级缓存默认开启,也就是cacheEnabled=“true”。针对单个查询,可以在mapper.xml里的对应的查询,使用useCache="true"可以开启二级缓存。
CachingExecutor装饰了前面的Executor。TransactionalCacheManager里找不到缓存时,委派前面的Executor执行查询。
flushCache="true"可以执行sql后清除一级缓存和二级缓存。
https://blog.csdn.net/elim168/article/details/71086613
——————————————————————————————————
ReuseExecutor里面statementMap,key是sql语句,value是statement,如果statementMap中已经有sql语句,就会复用对应的statement。
https://blog.csdn.net/elim168/article/details/70920038
SqlSession执行getMapper,会使MapperProxyFactory执行newInstance,生成JDK动态代理类,InvocationHandler的实现类是MapperProxy。
Mapper执行增删改查时,BoundSql存sql,ParameterHandler负责参数转换,StatementHandler负责Statement。ResultSetHandler负责ResultSet。
——————————————————————————————————
XMLConfigBuilder解析interceptor。Configuration在newExcutor时,InterceptorChain里的interceptor层层JDK动态代理executor。Interceptor的实现类的@Intercepts写明要代理的Executor、方法名、参数。代理类执行增删查改时,发现这个方法需要被代理,就会执行Interceptor的intercept。
分页查询工具PageHelper用了Mybatis的Plugin机制,PageInterceptor是Interceptor的实现类。