目录
[1. mybatis的运行](#1. mybatis的运行)
[1.1 引言](#1.1 引言)
[1.2 具体运行:](#1.2 具体运行:)
[1.3 sqlSession](#1.3 sqlSession)
[介绍local catch](#介绍local catch)
[2. 缓存](#2. 缓存)
[2.1 概念](#2.1 概念)
[2.2 使用缓存的原因](#2.2 使用缓存的原因)
[2.3 什么样的数据能使用缓存](#2.3 什么样的数据能使用缓存)
[3. Mybatis缓存](#3. Mybatis缓存)
[3.1 一级缓存](#3.1 一级缓存)
[3.1.1 测试一级缓存](#3.1.1 测试一级缓存)
[3.1.2 缓存失效的四种情况](#3.1.2 缓存失效的四种情况)
[$1 sqlSession不同](#$1 sqlSession不同)
[$2 sqlSession相同,查询条件不同](#$2 sqlSession相同,查询条件不同)
[$3 sqlSession相同,两次查询之间执行了增删改操作](#$3 sqlSession相同,两次查询之间执行了增删改操作)
[$4 sqlSession相同,手动清除一级缓存](#$4 sqlSession相同,手动清除一级缓存)
[3.2 二级缓存](#3.2 二级缓存)
[3.2.1 二级缓存开启的条件](#3.2.1 二级缓存开启的条件)
[3.2.2 二级缓存失效的情况](#3.2.2 二级缓存失效的情况)
[3.2.3 开启二级缓存](#3.2.3 开启二级缓存)
[3.2.4 分析](#3.2.4 分析)
[3.2.5 重要结论](#3.2.5 重要结论)
[3.3 Catch参数的具体细节](#3.3 Catch参数的具体细节)
[4. Mybatis缓存查询顺序](#4. Mybatis缓存查询顺序)
1. mybatis的运行
1.1 引言
当我们想运行这个selectUser,运行之前有**@Before和@After**, 在执行方法 之前 之后 运行( init()在执行方法之前执行,selectUser()方法执行,destroy()在执行方法之后执行)。
java
@Before //前置通知, 在方法执行之前执行
public void init() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
session = factory.openSession();
//通过Session创建UserDao接口代理对象
mapper = session.getMapper(UserDao.class);
}
@After //@After: 后置通知, 在方法执行之后执行 。
public void destory() throws IOException {
//释放资源
session.close();
in.close();
}
1.2 具体运行:
①。首先,是sqlMapConfig文件,配置信息,所以第一步,读取sqlMapConfig配置文件
-- sqlMapConfig文件里有sql配置文件 ,以及关于数据库 和mybatis的配置信息(数据库:用户名,密码以及连接的url,mybatis配置信息中有是否开启懒加载,开启日志)。
②。 下一步,生成SqlSessionFactory对象,对象类型是SqlSessionFactory。
③。 又生成session对象,对象类型是sqlsession。(整个程序的执行也就到sqlsession,sqlsession是用来干活的)
④。通过session调用getMapper创建代理对象。(这里不做过多阐述)
1.3 sqlSession
sqlsession会执行sql语句
Executor:执行器就是执行程序的,执行sql语句,所有的增删改查。
对象是堆里面的一块内存空间。
介绍local catch
比如
要对数据库进行查询:select * from A
因为真正执行的是sqlsession,用sqlsession对他进行查询,查询到的数据会缓存到local catch本地缓存中,下一次再查询,就不用查询数据库了,直接从本地缓存中拿取。
XML
<select id="FindStudentById" parameterType="int" resultType="com.qcby.entity.Student">
select * from student where id=#{id}
</select>
java
Student FindStudentById(int id);
$1. 首次进行:
java@Test public void FindStudentById(){ mapper.FindStudentById(1); }
结果:
$2. 执行两次:
java@Test public void FindStudentById(){ mapper.FindStudentById(1); mapper.FindStudentById(1); }
结果:
执行两次查询,但是sql语句只执行一次。
为什么?
因为第二次查询从本地缓存查数据。
java
@Test
public void FindStudentById1() {
Student student1=mapper.FindStudentById(1);
Student student2=mapper.FindStudentById(1);
System.out.println(student2==student1);
}
注!!
值是true, 表明指向的是同一个地址空间,指向的是同一个对象。
这里只执行一次的sql语句与一级缓存有关,也叫sqlSession级别的缓存。
2. 缓存
2.1 概念
- 存储在内存当中的数据
- 将用户常使用的数据存放在缓存(内存)当中,用户查询数据就不用从磁盘(关系型数据库文件)当中查询,从缓存当中查询,从而提高查询效率,解决了高并发系统的性能问题
2.2 使用缓存的原因
减少和数据库的交互次数,减少系统开销,提高系统效率。
2.3 什么样的数据能使用缓存
经常查询并且不常改变的数据。
3. Mybatis缓存
mybatis包含了一个非常强大的查询缓存特性,他可以非常方便的定制和配置缓存。缓存可以极大的提高查询的效率
mybatis系统当中默认定义了两级缓存:一级缓存 和二级缓存
- 默认情况之下,只有一级缓存开启(sqlSession级别的缓存)
- 二级缓存需要手动开启配置,需要局域namespace级别的缓存。
3.1 一级缓存
一级缓存也叫本地缓存
与数据库同一次会话期间查询到的数据会放入的本地缓存当中。
如果以后需要获取相同的数据直接去缓存当中拿,没必要再去查询数据库。
3.1.1 测试一级缓存
java
public class UserTest {
private InputStream in = null;
private SqlSession session = null;
private UserDao mapper = null;
/**
* 测试查询所有的方法
*/
@Test
public void findById() throws IOException {
//加载主配置文件,目的是为了构建SqlSessionFactory对象
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//创建SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//通过SqlSessionFactory工厂对象创建SqlSesssion对象
session = factory.openSession();
//通过Session创建UserDao接口代理对象
mapper = session.getMapper(UserDao.class);
User user1 = mapper.findById(1);
System.out.println(user1.toString());
System.out.println("-----------------");
User user2 = mapper.findById(1);
System.out.println(user2.toString());
System.out.println(user1 == user2);
//释放资源
session.close();
in.close();
}
}
3.1.2 缓存失效的四种情况
- sqlSession不同
- sqlSession相同,查询条件不同
- sqlSession相同,两次查询之间执行了增删改操作!
- sqlSession相同,手动清除一级缓存
$1 sqlSession不同
$2 sqlSession相同,查询条件不同
多次查询不同的情况,不会导致缓存失效。
$3 sqlSession相同,两次查询之间执行了增删改操作
$4 sqlSession相同,手动清除一级缓存
3.2 二级缓存
二级缓存默认情况下是关闭的。
想要开启的话,涉及到四个条件。
二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactroy创建SqlSession查询结果会被缓存;此后若再次执行相同的查询语句,结果会从一个缓存中获取。
3.2.1 二级缓存开启的条件
①:在核心配置文件中,设置全局属性caheEnable="true"。
②:在映射件中置
③:查询数据所转换的实体类类型必须实现序列化接口
④:二级缓存必须在SqlSession关闭或提交之后有效
3.2.2 二级缓存失效的情况
两次查询之间行了任意的增删改,会使得一级二级缓存同时失效。
3.2.3 开启二级缓存
$1 . 在SqlMapConfig.xml配置文件中开启二级缓存
XML
<!‐‐ 开启二级缓存 ‐‐>
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
$2.. 在UserDao.xml配置文件声明使用二级缓存
java
<!--使用二级缓存-->
<cache/>
$3: 查询数据所转换的实体类类型必须实现序列化接口
java
public class User implements Serializable {
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;
// get set方法 .....
}
$4 : 二级缓存必须在SqlSession关闭或提交之后有效
java
@Test
public void findById() throws IOException {
// 1.加载SqlMapConfig配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建sqlSessionFactory工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//3.sqlSessionFactory创建sqlSession
SqlSession sqlSession = factory.openSession();
SqlSession sqlSession2 = factory.openSession();
//4.通过Session创建UserDao接口代理对象
UserDao mapper = sqlSession.getMapper(UserDao.class);
User user1 = mapper.findById(1);
System.out.println(user1.toString());
// 将其一级缓存的数据放进二级缓存中,并清空一级缓存
sqlSession.close();
System.out.println("-----------------");
UserDao mapper2 = sqlSession2.getMapper(UserDao.class);
User user2 = mapper2.findById(1);
System.out.println(user2.toString());
// 将其一级缓存的数据放进二级缓存中,并清空一级缓存
sqlSession2.close();
System.out.println(user1 == user2);
resourceAsStream.close();
}
结果:
3.2.4 分析
$1.
java
@Test
public void FindStudentById1() {
Student student1=mapper1.FindStudentById(1);
session1.close();//一级缓存失效或提交
Student student2=mapper1.FindStudentById(1);//这里用的是sqlsession2
System.out.println(student2==student1);
}
这样写不对,因为此时session1已经关闭,如果下面继续用session1会报错。
$2.
java
@Test
public void FindStudentById1() {
Student student1=mapper1.FindStudentById(1);
session1.close();
Student student2=mapper2.FindStudentById(1);//这里用的是sqlsession2
System.out.println(student2==student1);
}
用session2去查询能够命中数据,因为session1查询到的数据提交到了sqlsessionFactory对象中,对象是堆里的 一段内存空间,里有catch,关闭sqlsession1之后,数据会放到sqlSessionFactory当中的catch,当执行sqlsession2时,从catch中取,**0.**5说明缓存命中率是一半(即存入的sqlSession1结果)。
3.2.5 重要结论
打印发现2个对象的地址值不一样,但是确实只发送了一次SQL语句的查询,二级缓存中存储的是数据,不是对象。
一级缓存缓存的是对象,就是查询出的对象,所以前用student1和student2是true,是同一个对象。
3.3 Catch参数的具体细节
1. eviction(收回策略)--catch满了,回收数据
- LRU(最近最少使用的):移除最长时间不被使用的对象,这是默认值。
- FIFO(先进先出):按对象进入缓存的顺序来移除它们。
- SOFT(软引用):移除基于垃圾回收器状态和软引用规则的对象。
- WEAK(弱引用):更积极地移除基于垃圾收集器状态和弱引用规则的对象。
2. flushinterval(刷新间隔) 可以被设置为任意的正整数,而且它们代表一个合理的毫秒形式的时间段。
- 默认情况不设置,即没有刷新间隔,缓存仅仅在调用语句时刷新。
原因:
数据库里的数据和缓存里的数据保持一致,故要间隔一段时间取访问数据库。
3. size(引用数目)--缓存的大小
- 可以被设置为任意正整数,要记住缓存的对象数目和运行环境的可用内存资源数目。默认值是1024 。
4. readOnly(只读) 属性可以被设置为 true / false。
-
- true:只读缓存:会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改, 这提供了很重要的性能优势。
- false读写缓存: 通过序列化返回缓存对象的拷贝,这种方式会慢一些,但是安全,因此默认是 false。
配置结果
XML
<cache eviction="FIFO"
flushInterval="6000"
size="512"
readOnly="true"/>
4. Mybatis缓存查询顺序
先查询二级缓存,因为二级缓存中可能会有其他程序查询出来的数据,可以直接拿来使用
如果二级缓存未命中,再查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession关闭之后,一级缓存的数据会写入二级缓存