emp_mapper.xml
<mapper namespace="cn.et.EmpMapper">
<!--<cache></cache> 启用二级缓存 属性默认 type 指定使用哪个一个缓存类 -->
<cache type="cn.et.JedisCache"></cache>
<select id="selectEmp" resultType="cn.et.Emp">
select * from emp where empno = #{0}
</select>
</mapper>
JedisCache
package cn.et;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Set;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.apache.ibatis.cache.Cache;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
* 当在查询时会自动实例化JedisCache类并且传入mapper查询的id;
* @author Ma-PC
*
*/
public class JedisCache implements Cache {
/**
* 用于序列化和反序列化的操作类
* @author Ma-PC
*/
static class SeqUtils{
/**
* 字节数组反序列化为对象的方法
* @param by
* @return Object
* @throws IOException
* @throws ClassNotFoundException
*/
public static Object deSer(byte[] by) throws IOException, ClassNotFoundException{
ByteArrayInputStream bais = new ByteArrayInputStream(by);
ObjectInputStream ois = new ObjectInputStream(bais);
return ois.readObject();
}
/**
* 对象序列化成字节数组的方法
* @param obj
* @return byte[]
* @throws IOException
*/
public static byte[] ser(Object obj) throws IOException{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream obos = new ObjectOutputStream(baos);
obos.writeObject(obj);
return baos.toByteArray();
}
}
private static JedisPool jp = null;
private String id ;
public JedisCache(String id) {
if(jp == null){
//当redis出现异常时,将连接池设为null;
//目的是将redis与数据库完全分离达到高可用性;
try {
//设置连接池属性的类
GenericObjectPoolConfig gopc = new GenericObjectPoolConfig();
jp = new JedisPool(gopc, "localhost");
jp.getResource();
} catch (Exception e) {
//e.printStackTrace();
jp=null;
}
}
this.id = id;
}
public String getId() {
return id;
}
/**
* 第一次查询数据后 会调用putObject将数据写入到缓存中;
*/
public void putObject(Object key, Object value) {
//到连接池为null时,不写入数据
if(jp==null){
return ;
}
Jedis jedis = jp.getResource();
try {
jedis.set(SeqUtils.ser(key), SeqUtils.ser(value));
//将缓存中的数据保存到本地文件;
//jedis.save();
//将redis对象回收到连接池中;
jp.returnResourceObject(jedis);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 自动调用该方法 当返回为null时自动查询数据库
* 否则直接使用返回的对象
* key 其实就是 oId;
*/
public Object getObject(Object key) {
//当连接池为null时,直接查询数据库
if(jp==null){
return null;
}
Jedis jedis = jp.getResource();
try {
byte[] be =jedis.get(SeqUtils.ser(key));
if(be != null){
Object obj = SeqUtils.deSer(be);
//将redis对象回收到连接池中;
jp.returnResourceObject(jedis);
return obj;
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 调用了例如 mybatis 的 flush方法 清空缓存
* 就会自动调用removeObject清空该元素
*/
public Object removeObject(Object key) {
Jedis jedis = jp.getResource();
try {
jedis.del(SeqUtils.ser(key));
//将redis对象回收到连接池中;
jp.returnResourceObject(jedis);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
*清空所有缓存
*/
public void clear() {
Jedis jedis = jp.getResource();
jedis.flushAll();
//将redis对象回收到连接池中;
jp.returnResourceObject(jedis);
}
/**
* 用于获取redis中缓存了多少元素
*/
public int getSize() {
Jedis jedis = jp.getResource();
//*表示所有
Set<String> allElemebts = jedis.keys("*");
return allElemebts.size();
}
/**
* 非阻塞式io non-blocking io 简单可以理解成 异步调用的io流
* 处理读写相同数据时的线程安全问题
*/
public ReadWriteLock getReadWriteLock() {
return new ReentrantReadWriteLock();
}
}
Test
/**
* 二级缓存
* @param args
*/
public static void main(String[] args) {
SqlSessionFactory factory = getSqlSessionFactory();
SqlSession session = factory.openSession();
EmpMapper dm = session.getMapper(EmpMapper.class);
Emp emp = dm.selectEmp("7369");
//开启二级缓存后 关闭session时
//session中一级缓存的数据会自动发往二级缓存
session.close();
//打开第二个SqlSession
SqlSession session2 = factory.openSession();
//通过sqlSession2获取EmpMapper实例
EmpMapper em = session2.getMapper(EmpMapper.class);
Emp selectEmp = em.selectEmp("7369");
System.out.println(emp==selectEmp);
}
Run
第一次运行:第一次到数据库查询后,写入缓存中,往后相同的查询直接使用缓存中的数据
2017-07-13 10:35:34 DEBUG [cn.et..EmpMapper.selectEmp] ==> Preparing: select * from emp where empno = ?
2017-07-13 10:35:34 DEBUG [cn.et.EmpMapper.selectEmp] ==> Parameters: 7369(String)
2017-07-13 10:35:34 DEBUG [cn.et.EmpMapper.selectEmp] <== Total: 1
2017-07-13 10:35:34 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@dc85d20]
2017-07-13 10:35:34 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@dc85d20]
2017-07-13 10:35:34 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] Returned connection 231234848 to pool.
2017-07-13 10:35:34 DEBUG [cn.et.EmpMapper] Cache Hit Ratio [cn.et.lesson05.EmpMapper]: 0.5
第二次运行:
2017-07-13 10:37:35 DEBUG [cn.et.EmpMapper] Cache Hit Ratio [cn.et.EmpMapper]: 1.0
2017-07-13 10:37:35 DEBUG [cn.et.EmpMapper] Cache Hit Ratio [cn.et.EmpMapper]: 1.0
第三次关闭Redis服务器后运行 :
2017-07-13 10:40:09 DEBUG [cn.et.EmpMapper.selectEmp] ==> Preparing: select * from emp where empno = ?
2017-07-13 10:40:09 DEBUG [cn.et.EmpMapper.selectEmp] ==> Parameters: 7369(String)
2017-07-13 10:40:09 DEBUG [cn.et.EmpMapper.selectEmp] <== Total: 1
2017-07-13 10:40:09 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@fd1c5f4]
2017-07-13 10:40:09 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@fd1c5f4]
2017-07-13 10:40:09 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] Returned connection 265405940 to pool.
2017-07-13 10:40:09 DEBUG [cn.et.EmpMapper] Cache Hit Ratio [cn.et.EmpMapper]: 0.0
2017-07-13 10:40:09 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] Opening JDBC Connection
2017-07-13 10:40:09 DEBUG [org.apache.ibatis.datasource.pooled.PooledDataSource] Checked out connection 265405940 from pool.
2017-07-13 10:40:09 DEBUG [org.apache.ibatis.transaction.jdbc.JdbcTransaction] Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@fd1c5f4]
2017-07-13 10:40:09 DEBUG [cn.et.EmpMapper.selectEmp] ==> Preparing: select * from emp where empno = ?
2017-07-13 10:40:09 DEBUG [cn.et.EmpMapper.selectEmp] ==> Parameters: 7369(String)
2017-07-13 10:40:09 DEBUG [cn.et.EmpMapper.selectEmp] <== Total: 1