(四)mybatis缓存、事务、插件的基本知识

mybatis缓存、事务、插件的基础

一、缓存

(一)一级缓存与二级缓存

  一级缓存

  为了获得更好的性能,最重要的就是一级缓存。每个session对象维持一个一级缓存,session对象创建时缓存创建,session对象释放时缓存销毁。

  缓存是一个哈希表。哈希表根据唯一键存储值,值可以根据唯一键检索。一个实体由它的ID唯一标识,如果两个实体类型相同,ID也相等,那么这两个实体是相等的。

   session对象从数据库中加载指定ID的实体,然后放到一级缓存中,访问该实体的键是它的ID值。当系统再次从数据库中加载同一个实体时,session对象首先检查它的缓存,如果实体已经存在于缓存中,就返回缓存的实例。只有实体不在缓存时,session对象才从数据库中加载实体。

  二级缓存

  一级缓存绑定到session对象,也就是说每次session被释放,所有的缓存数据就会丢失。

  二级缓存定义在session工厂级别的,只要session工厂没有被释放缓存就一直存在。一旦实体加载,二级缓存就被激活,实体对所有的session(同session工厂的)都可用。这样,只要实体在二级缓存中,对象就不会从数据库加载实体,直到它从缓存中移除。

(二)mybatis缓存

  mybatis默认开启一级缓存,即一个缓存相对于同一个SqlSession而言。使用同一个session查询时,mybatis会将其放在缓存中,以后再查询时,如果没有声明需要刷新,在缓存未超时的情况下,SQLSession都只会取出当前缓存数据。若使用不同的SQLSession对象,调用相同的mapper、参数和方法,均需要到数据库执行。

  二级缓存的开启需要配置,并且要求返回的POJO是可序列化的,即实体类实现Serializable接口;在映射XML文件配置<cache/>就可以开启缓存了。

  详细配置:<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>

    eviction:缓存回收策略

      1.LRU,最近最少使用的,移除最长时间不用的对象

      2.FIFO,先进先出,按对象进入缓存的顺序来移除

      3.SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象

      4.WEAK,弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。

    flushInterval:刷新时间间隔,单位毫秒,默认SQL被执行时刷新

    size:引用数目,最大存储对象数目

    readOnly:只读,意味着缓存数据只能读取而不能修改。优点:可以快速读取缓存。缺点:没办法修改缓存,默认值为false

  自定义缓存

    1.实现org.apache.ibatis.cache.Cache接口

    2.配置自定义缓存:<cache type="自定义cache实现类"/>

    3.(可选)设置自定义缓存参数

      <cache type="">

        <property name="host" value="localhost"/>

      </cache>

    4.(可选)映射器上可以配置缓存规则

      eg. <select ... flushCache="flase" useCache="true"/>

二、事务

(一)spring事务管理

  spring事务管理是通过springAOP实现的。

  1.事务传播机制(propagation)

    (1)REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。

    (2)SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同。

    (3)NOT_SUPPORTED:总是非事务地执行,并挂起任何存在的事务。

    (4)REQUIRESNEW:总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。

    (5)MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。

    (6)NEVER:总是非事务地执行,如果存在一个活动事务,则抛出异常

    (7)NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。

  2.事务隔离级别

    ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.

  另外四个与JDBC的隔离级别相对应:

    (1)ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。

    (2)ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另一个事务读取。

    (3)ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。

    (4)ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。

(二)mybatis和spring整合管理事务(使用springAOP)

  applicationContext.xml配置

1 <!--  配置jdbc事务管理器,完成数据的完整性和一致性 -->
2     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
3         <property name="dataSource" ref="dataSource"></property>
4     </bean>
5     <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>

  使用注解方式添加事务:

  @Transactional(value="transactionManager",propagation=Propagation.REQUIRES_NEW,isolation=Isolation.READ_COMMITTED)

属性

类型

描述

value String

可选的限定描述符,指定使用的事务管理器

propagation

enum: Propagation

可选的事务传播行为设置

isolation

enum: Isolation

可选的事务隔离级别设置

readOnly

boolean

读写或只读事务,默认读写

timeout

int (in seconds granularity)

事务超时时间设置

rollbackFor

Class对象数组,必须继承自Throwable

导致事务回滚的异常类数组

rollbackForClassName

类名数组,必须继承自Throwable

导致事务回滚的异常类名字数组

noRollbackFor

Class对象数组,必须继承自Throwable

不会导致事务回滚的异常类数组

noRollbackForClassName

类名数组,必须继承自Throwable

不会导致事务回滚的异常类名字数组


  注意:

  1.在需要事务管理的地方加@Transactional 注解。@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。

  2.通过元素的 “proxy-target-class” 属性值来控制是基于接口的还是基于类的代理被创建。如果 “proxy-target-class” 属值被设置为 “true”,那么基于类的代理将起作用(这时需要CGLIB库cglib.jar在CLASSPATH中)。如果 “proxy-target-class” 属值被设置为 “false” 或者这个属性被省略,那么标准的JDK基于接口的代理将起作用。

  3.Spring团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。在接口上使用 @Transactional 注解,只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。

  4.@Transactional 的事务开启,或者是基于接口的或者是基于类的代理被创建。所以在同一个类中一个方法调用另一个方法有事务的方法,事务是不会起作用的。

三、插件

  使用插件会修改mybatis的底层封装,需要去掌握mybatis的四大对象协作过程和插件的实现原理

   插件开发过程:

    1.确定需要拦截的签名:

      确定要拦截的对象:Executor、StatementHandler、ParameterHandler、ResultHandler;

      拦截方法和参数;

    eg.拦截修改需要执行的SQL语句

1 @Intercepts({@Signature(
2         type=StatementHandler.class,
3         method="prepare",
4         args={Connection.class,Integer.class}
5         )})
6 public class MyPlugin implements Interceptor{
7   ...      
8 }

    @Intercepts 说明它是拦截器

    @Signature 注册拦截器签名的地方:

      type:是四大对象中的一个

      method:要拦截四大对象的某一种接口方法

      args:表示该参数的方法

    2.实现拦截方法

 1 @Intercepts({@Signature(
 2                 type=StatementHandler.class,
 3                 method="prepare",
 4                 args={Connection.class,Integer.class}
 5                 )})
 6 public class MyPlugin implements Interceptor{
 7     Properties props=null;
 8     @Override
 9     public Object intercept(Invocation invocation) throws Throwable {
10         //如果当前代理的是一个非代理对象,那么它就会调用真实拦截对象的方法,若不是它会调用下个插件代理对象的invoke方法
11         Object obj=invocation.proceed();
12         return obj;
13     }
14 
15     /*
16      * 生成对象的代理,这里常用Mybatis提供的Plugin类的wrap方法
17      * target:被代理的对象
18      */
19     @Override
20     public Object plugin(Object target) {
21         //使用mybatis提供的plugin类生成代理对象
22         return Plugin.wrap(target,this);
23     }
24 
25     /*
26      * 获取插件配置的属性,我们在mybatis的配置文件里去配置
27      */
28     @Override
29     public void setProperties(Properties properties) {
30         // TODO Auto-generated method stub
31         this.props=properties;
32     }
33 }

    3.配置和运行

<plugins>
    <plugin interceptor="xxx.MyPlugin">
        <property name="dbType" value="derby"/>
    </plugin>
</plugins>    

 

原文地址:https://www.cnblogs.com/zuzZ/p/8107909.html