美文网首页spring boot
MyBatis学习笔记

MyBatis学习笔记

作者: zhglance | 来源:发表于2019-04-22 11:30 被阅读0次

    Mybatis架构图


    Mybatis.png

    图中可能有错误,欢迎评论指正。

    1.# Mybatis 中$与#的区别

    Mybatis中#是预编译处理,将传入的值替换为字符串(如果data_id = 112233,则使用where id = #{data_id},如果为where id = '112233'),Mybatis处理时会将#{}替换为?,调用PreparedStatement的set方法来赋值,防止了sql注入攻击。
    Mybatis中$是字符串替换,将传入的值替换成变量(如果data_id = 112233,则where id = ${data_id}替换为where id = 112233),不能有效的防止sql注入攻击。

    一般尽量使用#,当#不可用时,才考虑使用$。

    2.Mybatis缓存

    Mybatis缓存.png

    2.1一级缓存,SqlSession隔离

    一级缓存存在于SqlSession生命周期中,同一个SqlSession中查询时,会把执行的方法和参数通过算法生成缓存的key值,然后将key值和查询结果保存到一个map中。当多次调用同一个Mapper的同一个方法和同样的参数时,只会在进行第一次DB查询,以后直接从缓存中获取数据,而不需要查询DB。
    另外一级缓存默认是开启的,不同的SqlSession的缓存是隔离的,任何Insert、update和delete操作都会清空缓存。

    2.2 二级缓存,SqlSession共享

    二级缓存可以理解为存在于SqlSessionFactory的生命周期中,故SqlSession之间是缓存数据共享的,二级缓存可以通过XXXMapper.xml或者XXXMapper.java配置(可以指定回收策略(LRU/FIFO/SOFT/WEAK等),刷新间隔,引用数目,只读或者可读可写(只读缓存只可读,不可写,而可读可写的缓存要求对象实现Serializable接口,通过序列化返回对象的复制))。
    当调用 close 方法关闭 SqlSession 时, SqlSession 才会保存查询数据到 级缓存中在这之后二级缓存才有了缓存数据。

    2.3 Mybatis外部缓存

    由于Mybatis的缓存是基于Map的内存缓存实现,对缓存大量数据的情况支持性有限,但目前Mybatis已经支持Redis或者EhCache作为其二级缓存,来支持大数据量的缓存。

    2.4 二级缓存脏数据

    通常情况下每个 Mapper 映射文件都拥有自己的二级缓存,不同的Mapper二级缓存互不影响 。但是当遇到多表联合查询时,二级缓存会把查询结果放到某个命名空间下的Mapper映射文件中,但是涉及这几张表的增/删/改可能不在该Mapper映射文件中,即在不同的命名空间,当数据发生变更时,多表查询的缓存有可能会没有清空,导致脏数据的产生。当多个表适用同一个二级缓存时,可以解决脏数据问题,但是并不是所有的关联都可以通过这种方式解决。

    2.5 二级缓存适用场景

    a.查询为主的业务,很少使用增删改操作;
    b.绝大多是单表操作,很少使用关联操作。

    3.Mybatis分页方式

    a.使用limit
    即物理分页,实用性强;
    b.使用拦截器分页
    如PageHelper,将page信息保存到ThreadLocal中,利用interceptor接口拦截器,获取ThreadLocal中的Page变量,重新拼装sql,即 new_sql = select * from ( select tmp_page.*, rownum row_id from ( old_sql ) where rownum <= ? ) where row_id > ?。
    c.RowBounds分页
    即逻辑分页,需要将所有结果读入到内存,不适合数据量很大时。

    4.Mybatis延迟加载

    针对数据库多表查询的情况,先从单表查询、需要时再从关联表去关联查询,由于单表查询效率比多表关联查询效率高很多,故大大提高数据库查询性能。

    5.Mybatis的Executor执行器

    a.ExecutorType.SIMPLE
    为每个语句创建一个PreparedStatement.
    b.ExecutorType.REUSE
    重复使用PreparedStatements.
    b.ExecutorType.BATCH
    批量更新.

    6.Mybatis Interceptor拦截器

    Mybatis Interceptor拦截器支持的类有Executor,ParameterHandler ,ResultSetHandler ,StatementHandler共四个类,并不是这些类的所有方法都支持,具体支持的方法如下:

    6.1 org.apache.ibatis.executor.Executor 类的9个方法:
    package org.apache.ibatis.executor;
    
    import java.sql.SQLException;
    import java.util.List;
    
    import org.apache.ibatis.cache.CacheKey;
    import org.apache.ibatis.cursor.Cursor;
    import org.apache.ibatis.mapping.BoundSql;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.reflection.MetaObject;
    import org.apache.ibatis.session.ResultHandler;
    import org.apache.ibatis.session.RowBounds;
    import org.apache.ibatis.transaction.Transaction;
    
    /**
     * @author Clinton Begin
     */
    public interface Executor {
    
      ResultHandler NO_RESULT_HANDLER = null;
      int update(MappedStatement ms, Object parameter) throws SQLException;
    
      <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
    
      <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
    
      List<BatchResult> flushStatements() throws SQLException;
    
      void commit(boolean required) throws SQLException;
    
      void rollback(boolean required) throws SQLException;
    
      Transaction getTransaction();
    
      void close(boolean forceRollback);
    
      boolean isClosed();
    
    /** 
         其他不支持的方法省略
    **/
    
      ....
    }
    
    6.2 org.apache.ibatis.executor.parameter.ParameterHandler类的2个方法:
     
    package org.apache.ibatis.executor.parameter;
    
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    
    /**
     * A parameter handler sets the parameters of the {@code PreparedStatement}.
     *
     * @author Clinton Begin
     */
    public interface ParameterHandler {
    
      Object getParameterObject();
    
      void setParameters(PreparedStatement ps)
          throws SQLException;
    
    }
    
    6.3 org.apache.ibatis.executor.resultset.ResultSetHandler 类的2个方法:
    
    package org.apache.ibatis.executor.resultset;
    
    import java.sql.CallableStatement;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.List;
    
    import org.apache.ibatis.cursor.Cursor;
    
    /**
     * @author Clinton Begin
     */
    public interface ResultSetHandler {
    
      <E> List<E> handleResultSets(Statement stmt) throws SQLException;
    
      void handleOutputParameters(CallableStatement cs) throws SQLException;
    
    /** 
         其他不支持的方法省略
    **/
      ....
    
    }
    
    
    6.4 org.apache.ibatis.executor.statement.StatementHandler 类的5个方法:
    package org.apache.ibatis.executor.statement;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.List;
    
    import org.apache.ibatis.cursor.Cursor;
    import org.apache.ibatis.executor.parameter.ParameterHandler;
    import org.apache.ibatis.mapping.BoundSql;
    import org.apache.ibatis.session.ResultHandler;
    
    /**
     * @author Clinton Begin
     */
    public interface StatementHandler {
    
      Statement prepare(Connection connection, Integer transactionTimeout)
          throws SQLException;
    
      void parameterize(Statement statement)
          throws SQLException;
    
      void batch(Statement statement)
          throws SQLException;
    
      int update(Statement statement)
          throws SQLException;
    
      <E> List<E> query(Statement statement, ResultHandler resultHandler)
          throws SQLException;
    
       /** 
         其他不支持的方法省略
    **/
      ....
    
    
    }
    
    6.5 拦截器接口 org.apache.ibatis.plugin.Interceptor
    package org.apache.ibatis.plugin;
    
    import java.util.Properties;
    
    /**
     * @author Clinton Begin
     */
    public interface Interceptor {
    
      Object intercept(Invocation invocation) throws Throwable;
    
      default Object plugin(Object target) {
        return Plugin.wrap(target, this);
      }
    
      default void setProperties(Properties properties) {
        // NOP
      }
    
    }
    
    

    三个方法的执行顺序为:

    a.default void setProperties(Properties properties);
    b. default Object plugin(Object target);
    c. Object intercept(Invocation invocation) ;

    6.6 自定义拦截器 MyInterceptor:
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Signature;
    
    import java.util.Properties;
    
    
     /**
      * 拦截器签名,告诉MyBatis当前插件用来拦截哪个对象的哪个方法
      * type:要拦截的类 StatementHandler
      * method:拦截的方法 parameterize
      * args:方法的入参 java.sql.Statement.
      * */
             @Intercepts({
             @Signature(type= StatementHandler.class,
            method="parameterize", args=java.sql.Statement.class
             )
            })
    
    public class MyInterceptor implements Interceptor {
        public Object intercept(Invocation invocation) throws Throwable {
             // 拦截的后加入的业务逻辑代码
        }
    
        public Object plugin(Object target) {
            // 调用插件
            return Plugin.wrap(target, this);
        }
    
        public void setProperties(Properties properties) {
           /** 添加属性
          * /
        }
    }
    
    

    相关文章

      网友评论

        本文标题:MyBatis学习笔记

        本文链接:https://www.haomeiwen.com/subject/uhfygqtx.html