mybatis的有两种缓存,一级缓存和二级缓存。两个缓存的不一样点和相同点总结以下html
不一样点:java
相同点:mysql
一级缓存sql
一级缓存是mybatis自带的缓存,mybatis每次在查询后,会将语句和参数相同的查询SQL的结果集存放进缓存,待下一次有相同的语句和参数时,mybatis直接将缓存内的结果集返回,而再也不查询数据库。若是对于缓存的数据对应的表有增删改操做的话,缓存自动清空。数据库
经过实际的代码测试来看,在上一次一个简单的mybatis入门demo的基础上改造工程apache
增长BaseMaperTest.java类,用以进行SqlSession的获取缓存
package cn.mybatis.xml; import java.io.IOException; import java.io.Reader; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.BeforeClass; /** * 设置mapper测试父类 * 用以进行数据库链接,获取SqlSession * @author PC */ public class BaseMapperTest { private static SqlSessionFactory sqlSessionFactory; /** * 进行数据库链接 */ @BeforeClass public static void init() { try { Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); reader.close(); } catch (IOException e) { e.printStackTrace(); } } /** * 获取SqlSession * @return */ public SqlSession getSqlSession() { return sqlSessionFactory.openSession(); } }
在CountryMapper.java中增长根据id查询方法,增长一个addCountry方法session
/** * 查询国家/地区 * @param id 查询id * @return 查询到的国家/地区 */ Country selectCountryById(Long id); /** * 添加国家/地区 * @param country * @return 影响的数据条数 */ int addCountry(Country country);
对应CountryMapper.xml配置文件mybatis
<select id="selectCountryById" resultType="cn.mybatis.xml.model.Country"> select id, countryname, countrycode from country where id = #{id} </select> <insert id="addCountry"> insert into country(id, countryname, countrycode) values(#{id}, #{countryname}, #{countrycode}) </insert>
经过Junit来测试,三种场景,分别来测试app
/** * 一级缓存测试 * 测试缓存后,再查询 */ @Test public void testCache1() { SqlSession sqlSession = getSqlSession(); try { // 第一次查询 Country country = sqlSession.selectOne("selectCountryById", 2l); System.out.println(country.getCountryname() + ":" + country.getCountrycode()); // 经过日志能够发现,第二次查询并未到数据库查数据,说明第二次走的是缓存 Country country2 = sqlSession.selectOne("selectCountryById", 2l); System.out.println(country2.getCountryname() + ":" + country2.getCountrycode()); } finally { sqlSession.close(); } }
执行后,能够看到日志
Opening JDBC Connection Created connection 1291113768. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] ==> Preparing: select id, countryname, countrycode from country where id = ? ==> Parameters: 2(Long) <== Columns: id, countryname, countrycode <== Row: 2, 美国, US <== Total: 1 美国:US 美国:US Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Returned connection 1291113768 to pool.
反馈了两次查询结果,可是只查询了一次数据库,说明第二次的查询是取得缓存结果
/** * 一级缓存测试 * 测试缓存后,增删改查,再查询 */ @Test public void testCache2() { SqlSession sqlSession = getSqlSession(); try { // 第一次查询 Country country = sqlSession.selectOne("selectCountryById", 2l); System.out.println(country.getCountryname() + ":" + country.getCountrycode()); Country country2 = new Country(); country2.setId(7); country2.setCountrycode("TW"); country2.setCountryname("中国台湾"); int result = sqlSession.insert("addCountry", country2); if (result == 1) { System.out.println("** insert success **"); } // 因为进行了insert操做,第二次查询没有走缓存,直接走的数据库查询 Country country3 = sqlSession.selectOne("selectCountryById", 2l); System.out.println(country3.getCountryname() + ":" + country3.getCountrycode()); } finally { sqlSession.commit(); sqlSession.close(); } }
执行结果
Opening JDBC Connection Created connection 1291113768. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] ==> Preparing: select id, countryname, countrycode from country where id = ? ==> Parameters: 2(Long) <== Columns: id, countryname, countrycode <== Row: 2, 美国, US <== Total: 1 美国:US ==> Preparing: insert into country(id, countryname, countrycode) values(?, ?, ?) ==> Parameters: 7(Integer), 中国台湾(String), TW(String) <== Updates: 1 ** insert success ** ==> Preparing: select id, countryname, countrycode from country where id = ? ==> Parameters: 2(Long) <== Columns: id, countryname, countrycode <== Row: 2, 美国, US <== Total: 1 美国:US Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Returned connection 1291113768 to pool.
经过日志,能够印证,在缓存后,若是执行增删改操做,以前缓存的数据会自动清空。
此时,咱们须要在查询语句的标签中增长 flushCache="true" ,意为查询结果不缓存。
增长后的SQL标签为
<select id="selectCountryById" flushCache="true" resultType="cn.mybatis.xml.model.Country"> select id, countryname, countrycode from country where id = #{id} </select>
测试代码
/** * 一级缓存测试 * 测试select查询,不存入缓存,再查询 */ @Test public void testCache3() { SqlSession sqlSession = getSqlSession(); try { // 第一次查询,可是SQL设置了flushCache="true",即查询结果不会缓存 Country country = sqlSession.selectOne("selectCountryById", 2l); System.out.println(country.getCountryname() + ":" + country.getCountrycode()); // 经过日志能够发现,第二次查询依然查询了数据库,查询出来的结果依然不会缓存 Country country2 = sqlSession.selectOne("selectCountryById", 2l); System.out.println(country2.getCountryname() + ":" + country2.getCountrycode()); } finally { sqlSession.close(); } }
执行后的日志
Opening JDBC Connection Created connection 1291113768. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] ==> Preparing: select id, countryname, countrycode from country where id = ? ==> Parameters: 2(Long) <== Columns: id, countryname, countrycode <== Row: 2, 美国, US <== Total: 1 美国:US ==> Preparing: select id, countryname, countrycode from country where id = ? ==> Parameters: 2(Long) <== Columns: id, countryname, countrycode <== Row: 2, 美国, US <== Total: 1 美国:US Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Returned connection 1291113768 to pool.
能够看出,两次查询都走了数据库查询,缘由就在于flushCache="true" 上面。flushCache标签,意为查询结果后是否缓存,默认为false,即默认存入缓存,方便后续查询。若是不想存缓存,则须要手动的设置为true,查询结果不会存缓存。通常不推荐这么作,这么作会增长数据库的负担,增长一些没必要要的查询。
经过上面的测试,一级缓存的场景能够总结以下:
直接查,存缓存;增删改,清缓存;flushCache不缓存。
这里的flushCache值得是手动设置flushCache为true的情形。
二级缓存
相较于一级缓存的自动默认开启,二级缓存须要手动开启。一级缓存在同一个SqlSession内,以SqlSession为缓存单位;二级缓存在不一样的SqlSession间,以mapper为单位,即不一样的SqlSession间能够共享相同的mapper下接口查询的数据。
准备测试环境
增长SysUser.java
package cn.mybatis.xml.model; import java.io.Serializable; import java.util.Date; /** * 用户表 * @author PC */ public class SysUser implements Serializable{ /** * */ private static final long serialVersionUID = 1L; /** * 用户ID */ private Long id; /** * 用户名 */ private String userName; /** * 密码 */ private String userPassword; /** * 邮箱 */ private String userEmail; /** * 简介 */ private String userInfo; /** * 头像 */ private byte[] headImg; /** * 建立时间 */ private Date createTime; getter and setter... }
mybatis-config.xml中增长UserMapper.xml配置
<mappers> <mapper resource="cn/mybatis/xml/mapper/CountryMapper.xml" /> <mapper resource="cn/mybatis/xml/mapper/UserMapper.xml" /> </mappers>
UserMapper.xml内容
<?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"> <!-- 定义当前XML的命名空间 --> <mapper namespace="cn.mybatis.xml.mapper.UserMapper"> <!-- 启用mybatis二级缓存,不设置具体数值,均为默认项 --> <cache/> <!-- resultMap 设置返回值的类型和映射关系 --> <resultMap id="userMap" type="cn.mybatis.xml.model.SysUser"> <id property="id" column="id"/> <result property="userName" column="user_name"/> <result property="userPassword" column="user_password"/> <result property="userEmail" column="user_email"/> <result property="userInfo" column="user_info"/> <result property="headImg" column="head_img" jdbcType="BLOB"/> <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/> </resultMap> <!-- ID属性,定义当前select查询的惟一ID;resultType,定义当前查询的返回值类型,上面设置了resultMap的别名,这里引用id便可 --> <select id="selectUserByUserId" resultMap="userMap"> select id,user_name,user_password,user_email,user_info,head_img,create_time from sys_user where id = #{id} </select> <!-- 增长user --> <insert id="addUser"> insert into sys_user(id, user_name,user_password,user_email,user_info) values(#{id}, #{userName}, #{userPassword}, #{userEmail}, #{userInfo}); </insert> <delete id="deleteUser"> delete from sys_user where id = #{id} </delete> </mapper>
上面,看到了<cache/>,这个标签标示启用二级缓存,二级缓存有一系列的参数策略,这里不配置任何内容,表示均使用默认值。
经过Junit来测试,主要测试三种不一样的场景
/** * 二级缓存测试 * 不一样SqlSession之间的查询 */ @Test public void testCache1() { SqlSession sqlSession = getSqlSession(); try { SysUser user = sqlSession.selectOne("selectUserByUserId", 1001l); System.out.println(user.getUserName()); } finally { sqlSession.close(); } // 二级缓存,在第一个session关闭时,数据存入二级缓存中 SqlSession sqlSession2 = getSqlSession(); try { SysUser user = sqlSession2.selectOne("selectUserByUserId", 1001l); System.out.println(user.getUserName()); } finally { sqlSession.close(); } }
执行后,经过日志可看到
Cache Hit Ratio [cn.mybatis.xml.mapper.UserMapper]: 0.0 Opening JDBC Connection Created connection 1291113768. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] ==> Preparing: select id,user_name,user_password,user_email,user_info,head_img,create_time from sys_user where id = ? ==> Parameters: 1001(Long) <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time <== Row: 1001, test, 123456, test@1.com, <<BLOB>>, <<BLOB>>, 2017-04-01 12:00:01.0 <== Total: 1 test Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Returned connection 1291113768 to pool. Cache Hit Ratio [cn.mybatis.xml.mapper.UserMapper]: 0.5 test
执行了一次数据库查询,第二次查询时直接经过获取缓存的值返回,证实二级缓存生效。
/** * 二级缓存测试 * 不一样SqlSession之间查询,增删改,再查询 */ @Test public void testCache2() { SqlSession sqlSession = getSqlSession(); try { SysUser user = sqlSession.selectOne("selectUserByUserId", 1001l); System.out.println(user.getUserName()); } finally { sqlSession.close(); } SqlSession sqlSession2 = getSqlSession(); try { int result = sqlSession2.delete("deleteUser", 2001l); System.out.println(result); } finally { sqlSession2.commit(); sqlSession2.close(); } // 第二次查询 SqlSession sqlSession3 = getSqlSession(); try { SysUser user = sqlSession3.selectOne("selectUserByUserId", 1001l); System.out.println(user.getUserName()); } finally { sqlSession.close(); } }
执行后,看日志
Cache Hit Ratio [cn.mybatis.xml.mapper.UserMapper]: 0.0 Opening JDBC Connection Created connection 1291113768. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] ==> Preparing: select id,user_name,user_password,user_email,user_info,head_img,create_time from sys_user where id = ? ==> Parameters: 1001(Long) <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time <== Row: 1001, test, 123456, test@1.com, <<BLOB>>, <<BLOB>>, 2017-04-01 12:00:01.0 <== Total: 1 test Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Returned connection 1291113768 to pool. Opening JDBC Connection Checked out connection 1291113768 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] ==> Preparing: delete from sys_user where id = ? ==> Parameters: 2001(Long) <== Updates: 1 1 Committing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] Returned connection 1291113768 to pool. Cache Hit Ratio [cn.mybatis.xml.mapper.UserMapper]: 0.0 Opening JDBC Connection Checked out connection 1291113768 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4cf4d528] ==> Preparing: select id,user_name,user_password,user_email,user_info,head_img,create_time from sys_user where id = ? ==> Parameters: 1001(Long) <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time <== Row: 1001, test, 123456, test@1.com, <<BLOB>>, <<BLOB>>, 2017-04-01 12:00:01.0 <== Total: 1 test
能够发现,在执行了delete语句后,缓存被清空了,待第二次查询时,又查了数据库。
其实上面在说一级缓存时有说到,任何的增删改的语句,都会清空一级缓存,二级缓存天然会被清空了。
上面的两个场景测试,都是在mapper文件中设置使用二级缓存,二级缓存其实还有一个总开关,在mybatis-config.xml文件的setting配置中,为什么以前并无去配这个开关呢,这个开关默认打开的,只须要在mapper.xml文件中配置二级缓存开关便可。
<settings> <!-- mybatis 二级缓存总开关,总开关默认为true --> <!-- 总开关打开后,而后在每一个mapper中设置本身的二级缓存开关;若总开关关闭,则后续mapper设置均无效 --> <setting name="cacheEnabled" value="false"/> <!-- 设置驼峰匹配 --> <setting name="mapUnderscoreToCamelCase" value="true" /> <!-- 配置指定使用log4j输出日志 --> <setting name="logImpl" value="STDOUT_LOGGING" /> </settings>
该标签为cacheEnabled,默认值为true,因此,默认的二级缓存的总开关是打开的,不须要手动设置。
咱们如今将其设置为false,再次执行二级缓存场景一的测试语句
能够看到日志
Opening JDBC Connection Created connection 345281752. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@149494d8] ==> Preparing: select id,user_name,user_password,user_email,user_info,head_img,create_time from sys_user where id = ? ==> Parameters: 1001(Long) <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time <== Row: 1001, test, 123456, test@1.com, <<BLOB>>, <<BLOB>>, 2017-04-01 12:00:01.0 <== Total: 1 test Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@149494d8] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@149494d8] Returned connection 345281752 to pool. Opening JDBC Connection Checked out connection 345281752 from pool. Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@149494d8] ==> Preparing: select id,user_name,user_password,user_email,user_info,head_img,create_time from sys_user where id = ? ==> Parameters: 1001(Long) <== Columns: id, user_name, user_password, user_email, user_info, head_img, create_time <== Row: 1001, test, 123456, test@1.com, <<BLOB>>, <<BLOB>>, 2017-04-01 12:00:01.0 <== Total: 1 test
两次查询,均走了数据库查询。
综上:二级缓存能够做为一级缓存的补充,一级缓存在同一个SqlSession之间,二级缓存将缓存扩大到不一样的SqlSession之间。相同的点是,一旦有增删改的操做,缓存均会清空。
以上是本人学习mybatis缓存的简单认知,记录下来,供初学的童鞋予以参考。若是内容有错误或疏漏部分,还望批评指正。谢谢。
附 工程源代码路径:mybatis一级缓存和二级缓存简单示例