MyBatis知识点

MyBatis的优缺点

优点:

  1. 原生sql
  2. sql语句与代码解耦合
  3. 简单易学
  4. 原生的sql语句,sql调优灵活

缺点:

  1. 半自动ORM,不能自动生成sql语句
  2. sql语句依赖于具体数据库,数据性移植差

MyBatis缓存机制

一级缓存

  1. 作用域是SqlSession,会对同一个SqlSession中执行语句的结果进行缓存,来提高再次执行的效率。SqlSession关闭或者有新增更新删除操作,则这个SqlSession缓存的数据将会被清空。
  2. MyBatis内部定义了PerpetualCache类,在这个内中定义了一个成员变量map,这个map就是用来实现一级缓存的。所以MyBatis一级缓存本质上是通过HashMap实现。
  3. MyBatis默认开启一级缓存。通过设置Configuration里的boolean型的变量cacheEnabled来表示一级缓存是否开启。
  4. MyBatis默认一级缓存的作用域为Session,通过Configuration里的boolean型变量localCacheScope来设置一级缓存的作用域。可选SESSION或STATEMENT

注意:
1. 有多个SqlSession或者分布式的环境下,有操作数据库写的话,会引起脏数据,建议是把一级缓存的默认级别设定为Statement。2. Spring使用MyBatis时会出现一级缓存失效,只有结合事务时一级缓存才会生效。

  • 在未开启事物的情况之下,每次查询,Spring都会关闭旧的SqlSession,因此此时的一级缓存是没有启作用的。
  • 在开启事物的情况之下,Spring使用ThreadLocal获取当前资源绑定同一个SqlSession,因此此时一级缓存是有效的。

当结合Spring使用Mybatis时会出现一级缓存失效的问题。这里

二级缓存

  1. 作用域是Mapper(namespace),在同一个namespace中查询sql时,可以从缓存中获取数据
  2. 二级缓存可以跨SqlSession生效,并且可以自定义存储源,比如:ehcache等缓存实现,由于二级缓存可以跨SqlSession,因此也有很大的弊端,比如当在不同的SqlSession中对同一个表进行操作,就会出现脏读、幻读、不可重复读等问题。
  3. 可以通过在mapper接口上使用@CacheNamespace使用或者在mapper.xml文件中使用标签,可配置剔除策略、刷新时间、缓存容量等进行优化

问:请介绍一下Mybatis的缓存机制

根据MyBatis缓存思考系统中查询的缓存实现:

  1. MyBatis的一级缓存作用范围太小,当数据量较大的时候,并不能提升不同用户的查询体验。

  2. MyBastis的二级缓存虽然作用范围扩大,但是缓存更新的粒度控制得不好,不同的SqlSession容易出现脏读幻读不可重复读等问题,因此弊大于利,并不实用。

  3. 而如果不使用MyBatis的缓存,而直接使用SpringCache、Redis、EhCache通过注解或在代码中进行缓存控制,虽然实现了作用范围和缓存粒度的有效控制,但是大大增加了缓存与业务系统代码的耦合度,因此这样做在后期会非常难维护,并且一旦出了问题还不好定位。

  4. 针对上面说的情况思考,可以利用MyBatis的拦截器自定义实现一个缓存拦截器插件,结合常用的缓存工具如Redis和EhCache来自定义实现MyBatis的二级缓存。简单思路如下:
    1. 实现MyBatis的拦截器Interceptor,重写intercept方法,intercept方法的参数为Invocation类型的对象,通过该参数能够拿到具体执行的MappedStatement对象,通过MappedStatement对象可以拿到具体的SQL语句,通过SQL语句分析工具可以拿到本次sql涉及的表。通过MappedStatement也可以判断本次操作的类型是否是update\insert\delete
    2. 自定义规则,根据查询涉及的表和MappedStatement的id来生成缓存key
    3. 在该拦截器中需要判断每次操作的类型,如果是更新、删除、插入操作,就根据生成key的规则来找到需要清除的缓存的key。由此可以做到对MyBatis实现自定义缓存。

Mybatis的具体使用相关

  1. Mybatis是如何预防sql注入的?利用变量占位符:#{},进行sql预编译,这里要注意#和$两种方式,$是无法防止sql注入的
  2. Mybatis怎么获取自增id?如:

        INSERT INTO user_info(name,password,role_id) VALUES(#{user.name},#{user.password},#{user.roleInfo.roleId})
  3. 动态sql标签:如何配置?有哪些?if、choose、where、set、foreach、trim

Mybatis的主要对象及其相应的作用

  1. SqlSessionFactory是用来创建SqlSession的工厂类,对应配置文件中的数据库配置
  2. SqlSession:对数据库的操作是在SqlSession中进行的,SqlSession非线程安全,每次调用数据库后都需要调用close进行关闭
  3. Executor:SqlSession通过内部的Executor来进行增删改查操作
  4. StatementHandler:用来处理sql语句的预编译,设置参数
  5. ParameterHandler:用来设置预编译参数
  6. ResultSetHandler:用来处理结果集
  7. TypeHandler:进行数据库类型和JavaBean类型的互相映射

Mybatis工作原理

1. 读取配置文件,解析配置信息写入到Configuration类里,
   读取映射文件信息,解析映射文件信息,根据mapper文件生成对应的MappedStatement对象,
   存放在Configuration的Map类型的成员变量mappedStatments里,key就是Mapper.xml中每个标签的id(MappedStatement类就是对应的Mapper.xml中的一个sql语句)
2. 根据配置信息,构建SqlSessionFactory
3. 通过SqlSessionFactory开启SqlSession
4. SqlSession内部的Executor负责根据MappedStatement实现SQL执行,Executor有三种实现,SimpleExecutor、ReuseExecutor、BatchExecutor,如果没有指定默认的Executor,MyBatis会使用SimpleExecutor作为默认的执行器。
   *SimpleExecutor是一种常规执行器,每次执行都会创建一个statement,用完后关闭。
   *ReuseExecutor是可重用执行器,将statement存入map中,操作map中的statement而不会重复创建statement。
   *BatchExecutor是批处理型执行器,doUpdate预处理存储过程或批处理操作,doQuery提交并执行过程
   SqlSession对象完成和数据库的交互:(这个过程会用到StatementHandler、ParameterHandler、ResultSetHandler、TypeHandler)
   a、用户程序调用mybatis接口层api(即Mapper接口中的方法)
   b、SqlSession通过调用api的Statement ID找到对应的MappedStatement对象
   c、通过Executor(负责动态SQL的生成和查询缓存的维护)将MappedStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbc Statement对象
   d、JDBC执行sql。

MyBatis的拦截器

MyBatis的拦截器实现原理是使用了JDK的动态代理,具体原理类似于AOP的原理。在Configuration生成Executor、StatementHandler、ParameterHandler、ResultSetHandler时会调用Interceptor的plugin方法,这个方法的具体实现需要我们通过编写具体的Interceptor实现类来重写该方法,实现的时候通过Plugin的wrap方法生成代理类,被代理类就是具体的Executor、StatmentHandler等实例对象。调用的时候,具体的Interceptor拦截器就好比AOP里面的前置后置拦截器。

Mybatis的插件机制

  1. 插件机制是通过拦截器组成责任链对Executor、StatementHandler、ParameterHandler、ResultSetHandler四个作用点进行定制化处理
  2. 基于插件机制实现的常用插件:mybatis-pagehelper 实现原理,具体还要通过结合debug断点调试查看PageInterceptor分页拦截器。
    其实PageInterceptor就是拦截Executor类的query方法,PageInterceptor里的Dialect变量默认会在setProperties方法里初始化为com.github.pagehelper.PageHelper类型的变量。
    PageHelper继承了PageMethod的静态变量:
    protected static final ThreadLocal LOCAL_PAGE = new ThreadLocal();
    PageHelper里的还有PageAutoDialect类型的变量,PageAutoDialect里:
     private ThreadLocal dialectThreadLocal = new ThreadLocal();
    dialectThreadLocal变量在通过initDelegateDialect方法初始化后,会存放当前系统使用的数据库方言如:MySqlDialect(AbstractHelperDialect的子类),由此可见,在拦截器拦截到具体的sql查询命令后,LOCAL_PAGE变量会提供分页参数,dialectThreadLocal会提供具体的数据库方言如:MysqlDialect,MysqlDialect方言对象会将分页参数按照Mysql的分页查询语句将sql格式化。类似如果使用的是Oracle数据库,dialectThreadLocal会存放OracleDialect。

Mybatis的执行流程 这里

问题

  1. 简单说一下Mybatis的优缺点,随便说
  2. Mybatis的缓存机制:一级,二级,结合Spring失效的问题
  3. 怎么防止sql注入?#和PreparedStatement
  4. 怎么获取自增ID
  5. 动态sql标签?if、foreach、choose、where、set、trim
  6. Mybatis的主要对象及其作用
  7. Mybatis的工作原理
  8. Mybatis拦截器的原理
  9. Mybatis中Executor的类型有哪几种
  10. 怎么一对多,怎么多对一

你可能感兴趣的:(JavaWeb,关于Java开发知识点系统整理)