1.一级缓存
同一个SqlSession对象
MyBatis默认开启一级缓存,如果用同样的SqlSession对象查询相同的数据
则只会在第一次查询时向数据库发送SQL语句,并将查询的结果放入到SqlSession中
(作为缓存在后续再次查询该同样的对象时,则直接从缓存中查询该对象即可,即省略了数据库的访问)
public static void queryStudentByStuno() throws IOException {
Reader reader = Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
studentMapper studentmapper = session.getMapper(studentMapper.class);
Student student1 = studentmapper.queryStudentByStuno(2);
// 测试缓存
session.commit();
Student student2 = studentmapper.queryStudentByStuno(2);
System.out.println(student1);
System.out.println(student2);
session.close();
}
2.二级缓存
如果是同一个SqlSession对象进行多次相同的查询,则直接进入一级缓存查询
如果不是同一个SqlSession对象进行多次相同的查询(但是均来自同一个namespace)则进入二级缓存查询
(1)开启二级缓存
MyBatis默认情况没有开启二级缓存,需要手工打开
①conf.xml
<settings>
<!-- 开启日志,并指定使用的具体日志 -->
<setting name="logImpl" value="LOG4J"/>
<!-- 开启二级缓存 -->
<setting name="cacheEnabled" value="true"/>
</settings>
②studentMapper.xml
在具体的mapper.xml中声明开启(studentMapper.xml中)
<!-- 声明此namespace开启二级缓存 -->
<cache/>
(2)对象序列化
根据异常提示:NotSerializableException可知,MyBatis的二级缓存是将对象放入硬盘文件中
序列化:内存->硬盘
反序列化:硬盘->内存
准备缓存的对象,必须实现了序列化接口
(如果开启的缓存Namespace=“org.lanqiao.mapper.StudentMapper”)
可知序列化对象为Student,因此需要将Student序列化(序列化Student类,以及Student的级联属性、和父类)
package nuc.hzb.entity;
import java.io.Serializable;
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private int stuNo;
private String stuName;
private int stuAge;
private Address address;
public Student() {}
public int getStuNo() {
return stuNo;
}
public void setStuNo(int stuNo) {
this.stuNo = stuNo;
}
public String getStuName() {
return stuName;
}
public void setStuName(String stuName) {
this.stuName = stuName;
}
public int getStuAge() {
return stuAge;
}
public void setStuAge(int stuAge) {
this.stuAge = stuAge;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
@Override
public String toString() {
return "Student [stuNo=" + stuNo + ", stuName=" + stuName + ", stuAge=" + stuAge + ", address=" + address + "]";
}
}
(3)测试
public static void queryAllStudents() throws IOException {
Reader reader = Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionFactory.openSession();
studentMapper studentmapper = session.getMapper(studentMapper.class);
List<Student> students = studentmapper.queryAllStudents();
System.out.println(students);
// 触发将对象写入二级缓存的时机:SqlSession对象的close()方法,一次性全部反过来
session.close();
SqlSession session1 = sessionFactory.openSession();
studentMapper studentmapper1 = session1.getMapper(studentMapper.class);
List<Student> students1 = studentmapper1.queryAllStudents();
System.out.println(students1);
session1.close();
// 测试命中率
SqlSession session2 = sessionFactory.openSession();
studentMapper studentmapper2 = session2.getMapper(studentMapper.class);
List<Student> students2 = studentmapper2.queryAllStudents();
System.out.println(students2);
session2.close();
}
DEBUG [main] - ==> Preparing: select * from student
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 3
[Student [stuNo=2, stuName=黄朝博, stuAge=18, address=null], Student [stuNo=4, stuName=hhh, stuAge=20, address=null], Student [stuNo=5, stuName=黄朝博, stuAge=20, address=null]]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [oracle.jdbc.driver.T4CConnection@34e9fd99]
DEBUG [main] - Closing JDBC Connection [oracle.jdbc.driver.T4CConnection@34e9fd99]
DEBUG [main] - Returned connection 887750041 to pool.
DEBUG [main] - Cache Hit Ratio [nuc.hzb.mapper.studentMapper]: 0.5
[Student [stuNo=2, stuName=黄朝博, stuAge=18, address=null], Student [stuNo=4, stuName=hhh, stuAge=20, address=null], Student [stuNo=5, stuName=黄朝博, stuAge=20, address=null]]
DEBUG [main] - Cache Hit Ratio [nuc.hzb.mapper.studentMapper]: 0.6666666666666666
[Student [stuNo=2, stuName=黄朝博, stuAge=18, address=null], Student [stuNo=4, stuName=hhh, stuAge=20, address=null], Student [stuNo=5, stuName=黄朝博, stuAge=20, address=null]]
3.总结
MyBatis自带二级缓存
二级缓存:同一个namespace生成的mapper对象
回顾:namespace的值就是接口的全类名(包名.类名),通过接口可以产生代理对象(studentMapper对象)
namespace决定了studentMapper对象的产生
结论:只要产生的xxxMapper对象来自于同一个namespace,则这些对象共享二级缓存
注意:二级缓存的范围是同一个namespace,如果有多个xxxMapper.xml的namespace值相同,则通过这些xxxMapper.xml产生的xxxMapper对象仍然共享二级缓存