spring boot

MyBatis学习笔记

2019-04-22  本文已影响0人  zhglance

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) {
       /** 添加属性
      * /
    }
}

上一篇下一篇

猜你喜欢

热点阅读