Mybatis源码分析_风铃峰顶的博客-程序员宅基地

技术标签: Mybatis  

实例

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的实现类。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/haoranhaoshi/article/details/112605496

智能推荐

webpack4 学习时打包图片时遇到的问题_webmazha的博客-程序员宅基地

const path = require('path');const uglify = require('uglifyjs-webpack-plugin');//代码压缩插件const htmlPlugin = require('html-webpack-plugin');//html 打包工具const extractTextWebpackPlugin = require('extract...

使用k8s command + args 参数调试容器_shida_csdn的博客-程序员宅基地

有时候,我们自己制作的镜像通过 k8s启动时就不明原因地立即退出了,很难调试的说哇!这时候,我们可以通过更改容器的启动入口命令,使其不退出,比如改成 tail -f /etc/profile下面粘贴一个例子:apiVersion: v1kind: Podmetadata: name: command-demo labels: purpose: demonstrate

mysql 1118错误_mysql 5.7 建表时报错:ERROR 1118 (42000)_weixin_39542340的博客-程序员宅基地

该楼层疑似违规已被系统折叠隐藏此楼查看此楼mysql 5.7.20当我在建表时,完整报错信息如下:ERROR 1118 (42000): Row size too large (&gt; 8126). Changing some columns to TEXT or BLOB may help. In current row format, BLOB prefix of 0 bytes is s...

网络编程之TCP/UDP及其流程比较_imxiangzi的博客-程序员宅基地

TCP与UDP的区别基于连接与无连接对系统资源的要求(TCP较多,UDP少)UDP程序结构较简单流模式与数据报模式TCP保证数据正确性,UDP可能丢包TCP保证数据顺序,UDP不保证具体编程时的区别socket()的参数不同UDP Server不需要调用listen和acceptUDP收发数据用sendto/recvfrom函数TCP:地址信息在conn

Tomcat配置虚拟目录映射_yangymy的博客-程序员宅基地

Tomcat配置虚拟目录映射 路径:apache-tomcat-7.0.56\conf\server.xml 在Server.xml中进行配置 在标签中添加子标签 并重启服务器即可; path表示虚拟目录,docBase表示真实的web应用所在目录; 注意:这种方法需要重启服务器才能够生效。

codeforce-------Wrong Subtraction_王文波~的博客-程序员宅基地

Little girl Tanya is learning how to decrease a number by one, but she does it wrong with a number consisting of two or more digits. Tanya subtracts one from a number by the following algorithm:if th...

随便推点

wpa_supplicant(转)_一朵时光_bobo的博客-程序员宅基地

wpa_supplicant代码初探收藏 这几天在尝试把wpa_supplicant移植到windows ce上,替换微软的WZC。先把源代码down下来,了解了一下大致的结构。  wpa_supplicant运行的整个核心就是eloop_run函数。这个函数负责处理应用程序的请求和数据链路层发来的EAPOL数据。eloop的针对不同的平台有好几个实现版本,我这里只讨论针对WIN32的

特征变换(3)小波变换_指尖热度的博客-程序员宅基地

笔记-印象笔记->小波变换篇   存在着大量的小波变换,每个适合不同的应用。完整的列表参看小波相关的变换列表,常见的如下:连续小波变换(CWT)离散小波变换(DWT)快速小波转换(FWT)小波包分解(Wavelet packet decomposition) (WPD)离散小波Beylkin(18)Coiflet(6, 12, 18, 24, 3

Vue源码阅读(24):v-on 指令的源码解析_纷飞丿的博客-程序员宅基地_v-on原理

今天讲讲 v-on 指令的底层实现原理。在 Vue 中,v-on 指令有两种用法,第一种是将 v-on 指令使用在自定义组件上,例如:&lt;my-component v-on:myEvent="doSomething"&gt;&lt;/my-component&gt;,使用 v-on 指令监听了组件的 myEvent 事件,回调函数是doSomething,当在组件中执行 this.$emit('myEvent') 时,会触发执行 doSomething 函数,有没有发现这和我上一篇文章中的 vm.$o.

ARM裸板开发:07_IIC 通过IIC总线接口读写时钟芯片时间参数实现的总结_weixin_34024034的博客-程序员宅基地

问题一:程序直接在iRAM内部可正常执行,而程序搬移(Nand -&gt;SDRAM)之后,就不能正常运行了#define NAND_SECTOR_SIZE 2048 /* 读函数 */void nand_read(unsigned char *buf, unsigned long start_addr, int size){ int i, j; ...

剑指Offer——8.二叉树的下一个节点_BlackMaBa的博客-程序员宅基地

题目:给定一颗二叉树和其中一个节点,如何找出中序遍历序列的下一个节点?树中的节点除了有两个分别指向左、右子节点的指针,还有一个指向父节点的指针。二叉树节点定义如下: class BinaryTreeNode { int data; BinaryTreeNode left; BinaryTreeNode right; B...

springboot--支付宝条码支付的实现_Edwina414的博客-程序员宅基地

这几天一直在调支付宝的条码支付的接口,遇到不少问题,想跟大家分享一下。我还是建议大家在官网下载的接口先调通了,再放入自己的项目中。我的小伙伴做的是微信条码支付,不得不说,支付宝的接口文档比微信的详细多了,此外支付宝还附带一个样例demo,非常便于新手开发与学习       所谓的条码支付,就是商家扫用户的付款二维码进行结账,用户只需展示付款二维码即可。而扫码支付是用户扫商家的二维码,然后输入金