(四)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>