一、一级缓存和二级缓存
程序中缓存对系统效率的提升是极大的,在Mybatis中,缓存分别为一级缓存和二级缓存,其中一级缓存是默认开启的,二级缓存需要手动开启。
二、一级缓存
说明:
1.Mybatis的一级缓存是默认开启的。
2.若要停止Mybatis的一级缓存可以通过设置select标签上的flushCache属性为true,会每一次执行完查询就情况一级缓存。
3.一级缓存的生命周期是一个SqlSession,不能跨SqlSession域
4.该SqlSession下的任何增、删、改操作都会清空一级缓存
案例
mapepr映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.my.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.my.entity.User">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="user_name" jdbcType="VARCHAR" property="userName" />
<result column="real_name" jdbcType="VARCHAR" property="realName" />
<result column="sex" jdbcType="CHAR" property="sex" />
<result column="mobile" jdbcType="VARCHAR" property="mobile" />
<result column="email" jdbcType="VARCHAR" property="email" />
<result column="note" jdbcType="VARCHAR" property="note" />
<result column="position_id" jdbcType="INTEGER" property="positionId" />
</resultMap>
<sql id="Base_Column_List">
id, user_name, real_name, sex, mobile, email, note, position_id
</sql>
<!-- flushCache设置为true则为关闭一级缓存 -->
<select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap" flushCache="false">
select
<include refid="Base_Column_List" />
from user
where id = #{id,jdbcType=INTEGER}
</select>
<update id="updateByUserSelective" parameterType="com.my.entity.User">
update user
<set>
<if test="userName != null">
user_name = #{userName,jdbcType=VARCHAR},
</if>
<if test="realName != null">
real_name = #{realName,jdbcType=VARCHAR},
</if>
<if test="sex != null">
sex = #{sex,jdbcType=CHAR},
</if>
<if test="mobile != null">
mobile = #{mobile,jdbcType=VARCHAR},
</if>
<if test="email != null">
email = #{email,jdbcType=VARCHAR},
</if>
<if test="note != null">
note = #{note,jdbcType=VARCHAR},
</if>
<if test="positionId != null">
position_id = #{positionId,jdbcType=INTEGER},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
测试代码:
public class MybatisTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws IOException {
String resource = "mybatis-config.xml";
//1.使用mybatis的工具读取配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//2.创建sqlSessionFactory
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
inputStream.close();
}
/**
* 测试一级缓存,需要通过查看日志的SQL打印判断是否缓存
*/
@Test
public void testOneCache() {
//自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = userMapper.selectById(1);
//再次查询
user = userMapper.selectById(1);
user.setNote("test");
//更新
userMapper.updateByUserSelective(user);
//再次查询
user = userMapper.selectById(1);
System.out.println(user);
sqlSession.close();
}
/**
* 测试一级缓存跨Session,需要通过查看日志的SQL打印判断是否缓存
*/
@Test
public void testOneCacheSession() {
//自动提交
SqlSession sqlSession = sqlSessionFactory.openSession();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
User user = userMapper.selectById(1);
User user2 = userMapper2.selectById(1);
sqlSession.close();
sqlSession2.close();
}
}
说明:缓存效果不能通过代码直接看到,需要自己debug查看打印日志进行观察。
三、二级缓存
说明:
1.二级缓存默认是不使用的,需要手动添加。
2.二级缓存需要把Mybatis配置文件中的cacheEnabled设为true(默认是true,但设置后更直观)。
3.开启二级缓存后还需要去mapper.xml文件中通过使用cache或cache-ref开启二级缓存的使用。
4.二级缓存的生命周期是一个SqlSessionFactory(一般一个项目中只用一个)。
5.二级缓存是跨SqlSession的,但是并不跨命名空间(每个mapper.xml)。
6.同一个命名空间下的任意SqlSession的增、删、改操作都会清空二级缓存。
cache和cache-ref标签介绍:
cache标签用在mapper.xml文件中,开启该命名空间的二级缓存,和cache-ref标签冲突,常用三个属性:
1.eviction:缓存策略,一般用FIFO
2.flushInterval:多久清空一次,单位秒
3.size:缓存列表数量
cache-ref标签用在mapper.xml文件中,和其它命名空间共用一个缓存,和cache标签冲突,只有一个属性:
1.namespace:需要其共用缓存的命名空间(该值的命名空间需要开启二级缓存)
案例:
mapper映射文件:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.my.mapper.PositionMapper">
<!-- 开启二级缓存,使用默认配置 -->
<cache> </cache>
<!-- 和其它命名空间共用一个缓存 -->
<!-- <cache-ref namespace="命名空间"/>-->
<resultMap id="BaseResultMap" type="com.my.entity.Position">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="posname" jdbcType="VARCHAR" property="posname" />
<result column="note" jdbcType="VARCHAR" property="note" />
</resultMap>
<sql id="Base_Column_List">
id, posname, note
</sql>
<select id="selectById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from position
where id = #{id,jdbcType=INTEGER}
</select>
<update id="updateByPositionSelective" parameterType="com.my.entity.Position">
update position
<set>
<if test="posname != null">
posname = #{posname,jdbcType=VARCHAR},
</if>
<if test="note != null">
note = #{note,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
测试代码:
public class MybatisTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws IOException {
String resource = "mybatis-config.xml";
//1.使用mybatis的工具读取配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
//2.创建sqlSessionFactory
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
inputStream.close();
}
/**
* 测试二级缓存,需要通过查看日志的SQL打印判断是否缓存
*/
@Test
public void testTowCache() {
//自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
SqlSession sqlSession2 = sqlSessionFactory.openSession(true);
PositionMapper positionMapper = sqlSession.getMapper(PositionMapper.class);
PositionMapper positionMapper2 = sqlSession2.getMapper(PositionMapper.class);
Position position = positionMapper.selectById(1);
//再次查询
position = positionMapper.selectById(1);
position.setNote("test");
//更新
positionMapper.updateByPositionSelective(position);
//再次查询
position = positionMapper.selectById(1);
System.out.println(position);
sqlSession.close();
}
}
说明:缓存效果不能通过代码直接看到,需要自己debug查看打印日志进行观察。
四、补充说明
1.缓存之间执行的顺序,二级缓存—一级缓存—数据库
2.一般不用二级缓存,因为二级缓存可能会造成脏读,例如A命名空间开启了二级缓存,当B命名空间对A命名空间中的表进行修改时,A命名空间的缓存是无法知晓的。