目录
一级缓存也叫本地缓存:(SqlSession的创建到SqlSession的销毁之间有效)
一级缓存失效的四种情况(需要再向数据库中发起一次查询请求):
一、动态SQL
<!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
-
if,where
-
<select id="queryBlogIF" parameterType="map" resultType="blog"> select * from blog <where> <if test="title !=null"> title=#{title} </if> <if test="author !=null"> and author=#{author} </if> </where> </select>
where能够实现sql语句的自动拼接
-
where,choose ,when, otherwise
-
<select id="queryBlogChoose" parameterType="map" resultType="blog"> select * from blog <where> <choose> <when test="title !=null"> title=#{title} </when> <when test="author !=null"> and author=#{author} </when> <otherwise> and views=#{views} </otherwise> </choose> </where> </select>
当title!=null时,执行title=#{title} 当title==null,author!=null时,执行author=#{author} 当title==null,author==null时,执行views=#{views} 当 title!=null,author!=null时,执行title=#{title}
-
if,set
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title !=null">
title=#{title},
</if>
<if test="author !=null">
author=#{author}
</if>
</set>
where id=#{id}
</update>
set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号
set中一定要有一个可以执行,否则sql语句错误,会报错
-
foreach
<!--
select * from blog where 1=1 and (id=1 or id=2 or id=3)
传递一个map,这个map中存在一个集合
collection:表示一个名为ids的集合
item:表示集合中的每一项名为id
-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>
@Test
public void queryBlogForEach(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.commit();
sqlSession.close();
}
SQL片段
<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where> <!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="if-title-author">
</include>
<!-- 在这里还可以引用其他的 sql 片段 -->
</where>
</select>
二、缓存
1、简介
2、Mybatis缓存
3、一级缓存(默认开启)
一级缓存也叫本地缓存:(SqlSession的创建到SqlSession的销毁之间有效)
步骤:
<setting name="logImpl" value="STDOUT_LOGGING"/>
//根据id查询用户
User queryUserById(@Param("id") int id);
<select id="queryUserById" resultType="User">
select * from mybatis.user where id=#{id}
</select>
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
System.out.println("++++++++++++++++++++++++++++++++");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
sqlSession.commit();
sqlSession.close();
}
一级缓存失效的四种情况(需要再向数据库中发起一次查询请求):
(1)、sqlSession不同 ,每个sqlSession中的缓存相互独立
(2)、sqlSession相同,查询条件不同
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
System.out.println("++++++++++++++++++++++++++++++++");
User user2 = mapper.queryUserById(2);
System.out.println(user2);
sqlSession.commit();
sqlSession.close();
}
(3)、sqlSession相同,两次查询之间执行了增删改操作!
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
// 进行增删改时会更新缓存
int user1 = mapper.updateUser(new User(2, "小张", "010426"));
System.out.println("++++++++++++++++++++++++++++++++");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
sqlSession.commit();
sqlSession.close();
}
(4)、sqlSession相同,手动清除一级缓存
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
//手动清理缓存
sqlSession.clearCache();
System.out.println("++++++++++++++++++++++++++++++++");
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
sqlSession.commit();
sqlSession.close();
}
4、二级缓存
使用步骤:
(1)显示的开启全局缓存
<!--显示的开启全局缓存-->
<setting name="cacheEnabled" value="true"/>
(2)在mapper.xml中配置使用二级缓存
<!--在当前Mapper.xml中使用二级缓存-->
<!--方式一-->
<cache/>
<!--方式二-->
<!--
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
默认的清除策略是 LRU
-->
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
(3)代码测试
所有的实体类先实现序列化接口(public class User implements Serializable{})
@Test
public void test(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
SqlSession sqlSession2 = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.queryUserById(1);
System.out.println(user);
sqlSession.close();
System.out.println("++++++++++++++++++++++++++++++++");
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);
sqlSession.close();
}
结论:
5、Mybatis缓存原理
6、自定义缓存Ehcache
使用步骤:
(1)引入jar包
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
(2)在Mapper.xml中配置使用
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
(3)编写ehcache.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位 置。参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径 -->
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
<!--defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策 略。只能定义一个。 -->
<!--name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。
仅当 eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建 时间和失效时间之间。
仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存 活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine.
The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默 认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将 会根据指定的策略去清理内存。
默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先 出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、 FIFO(先进先出)、LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以 来最少被使用的。
如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容 量满了,
而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的 元素将被清出缓存。 -->
</ehcache>
"http://ehcache.org/ehcache.xsd"爆红解决:
File --> Settings --> Languages & Frameworks --> Schemas and DTDs
7、补充
select语句:
flushCache默认为false,表示任何时候语句被调用,都不会去清空本地缓存和二级缓存。
useCache默认为true,表示会将本条语句的结果进行二级缓存。
insert、update、delete语句:
flushCache默认为true,表示任何时候语句被调用,都会导致本地缓存和二级缓存被清空。
useCache属性在该情况下没有。