一: 查詢緩存
Mybatis提供查詢緩存,用於減輕數據壓力,提高數據庫壓力。
Mybatis提供一級緩存和二級緩存。
在操作數據庫時需要構造SqlSession對象,在對象中有一個數據結構(HashMap)用於緩存數據。
不同的SqlSession之間的緩存數據區域是互相不影響的。
Mybatis一級緩存的作用域是同一個SqlSession,在同一個sqlSession中兩次執行相同的sql語句,第一次執行完畢會將數據庫中查詢的數據寫到緩存(內存),第二次會從緩存中獲取數據將不再從數據庫查詢,從而提高查詢效率。當一個sqlSession結束后該sqlSession中的一級緩存也就不存在了。Mybatis默認開啟一級緩存。
Mybatis二級緩存是多個SqlSession共享的,其作用域是mapper的同一個namespace,不同的sqlSession兩次執行相同namespace下的sql語句且向sql中傳遞參數也相同即最終執行相同的sql語句,第一次執行完畢會將數據庫中查詢的數據寫到緩存(內存),第二次會從緩存中獲取數據將不再從數據庫查詢,從而提高查詢效率。Mybatis默認沒有開啟二級緩存需要在setting全局參數中配置開啟二級緩存。
1、一級緩存
第一次發起查詢,先去找緩存中是否有id為1的用戶信息,如果沒有,從數據庫中查詢用戶信息。
得到用戶信息,將用戶信息存儲到一級緩存中。
第二次發起查詢用戶id為1的用戶信息,先去緩存中是否有id為1的用戶信息,緩存中有,直接從緩存中獲取用戶信息。
如果SqlSession去執行commit操作(執行插入、更新、刪除),清空SqlSession中的一級緩存。目的是為了讓緩存中存儲的是最新的信息,避免臟讀。
Mybatis默認支持一級緩存,不需要在配置文件中配置。
Mybatis內部存儲緩存使用一個HashMap,key為hashCode+sqlId+Sql語句。value為從查詢出來映射生成的java對象。
應用場景:
2、二級緩存
SqlSession1去查詢用戶id為1的用戶信息,查詢到用戶信息會將查詢數據存儲到二級緩存中。
SqlSession2去查詢用戶id為1的用戶信息,去緩存中找是否存在數據,如果存在直接從緩存中取出數據。
二級緩存區域是根據mapper的namespace划分的,相同namespace的mapper查詢數據放在同一個區域,如果使用mapper代理方法每個mapper的namespace都不同,此時可以理解為二級緩存區域是根據mapper划分。
每次查詢會先從緩存區域找,如果找不到從數據庫查詢,查詢到數據將數據寫入緩存。
Mybatis內部存儲緩存使用一個HashMap,key為hashCode+sqlId+Sql語句。value為從查詢出來映射生成的java對象
sqlSession執行insert、update、delete等操作commit提交后會清空緩存區域。
開啟二級緩存:
1.在核心配置文件mybatis-config.xml中加入
<setting name="cacheEnabled" value="true"/>
描述 |
允許值 |
默認值 |
|
cacheEnabled |
對在此配置文件下的所有cache 進行全局性開/關設置。 |
true false |
true |
2. 要在你的Mapper映射文件中添加一行: <cache /> ,表示此mapper開啟二級緩存。
3.二級緩存需要查詢結果映射的pojo對象實現java.io.Serializable接口實現序列化和反序列化操作,注意如果存在父類、成員pojo都需要實現序列化接口。
為了將緩存數據取出執行反序列化,因為二級緩存存儲介質多種多樣,不一定在內存。
禁用二級緩存:
在statement中設置useCache=false可以禁用當前select語句的二級緩存,即每次查詢都會發出sql去查詢,默認情況是true,即該sql使用二級緩存。
<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
刷新緩存(就是清空緩存):
設置statement配置中的flushCache="true" 屬性,默認情況下為true即刷新緩存,如果改成false則不會刷新。使用緩存時如果手動修改數據庫表中的查詢數據會出現臟讀。
<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">
應用場景:
對於訪問多的查詢請求且用戶對查詢結果實時性要求不高,此時可采用mybatis二級緩存技術降低數據庫訪問量,提高訪問速度,業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等。
實現方法如下:通過設置刷新間隔時間,由mybatis每隔一段時間自動清空緩存,根據數據變化頻率設置緩存刷新間隔flushInterval,比如設置為30分鍾、60分鍾、24小時等,根據需求而定。
局限性:
mybatis二級緩存對細粒度的數據級別的緩存實現不好,比如如下需求:對商品信息進行緩存,由於商品信息查詢訪問量大,但是要求用戶每次都能查詢最新的商品信息,此時如果使用mybatis的二級緩存就無法實現當一個商品變化時只刷新該商品的緩存信息而不刷新其它商品的信息,因為mybaits的二級緩存區域以mapper為單位划分,當一個商品信息變化會將所有商品信息的緩存數據全部清空。解決此類問題需要在業務層根據需求對數據有針對性緩存。
二:案例
源碼介紹:
1.Student.java

package cn.zhang.entity; import java.util.Date; /** * 學生實體類 * */ public class Student { private Integer stuno; private String stuname; private Integer stuage; private Date studate; public String toString() { return "Student [stuno=" + stuno + ", stuname=" + stuname + ", stuage=" + stuage + ", studate=" + studate + "]"; } public Integer getStuno() { return stuno; } public void setStuno(Integer stuno) { this.stuno = stuno; } public String getStuname() { return stuname; } public void setStuname(String stuname) { this.stuname = stuname; } public Integer getStuage() { return stuage; } public void setStuage(Integer stuage) { this.stuage = stuage; } public Date getStudate() { return studate; } public void setStudate(Date studate) { this.studate = studate; } }
2.MybatisUtil.java

package cn.zhang.util; 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; /** * 工具類 * */ public class MybatisUtil { private static String config = "mybatis-config.xml"; static Reader reader; static { try { reader = Resources.getResourceAsReader(config); } catch (IOException e) { e.printStackTrace(); } } private static SqlSessionFactory factory = new SqlSessionFactoryBuilder() .build(reader); // 提供一個可以獲取到session的方法 public static SqlSession getSession() throws IOException { SqlSession session = factory.openSession(); return session; } }
3.StudentDao.java

package cn.zhang.dao; import java.io.IOException; import cn.zhang.entity.Student; public interface StudentDao { /** * @return * @throws IOException */ public Student findAll(Integer sid) throws IOException; public int insertStudent(Student stu); }
4.StudentDAO.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"> <mapper namespace="cn.zhang.dao.StudentDao"> <!-- 查詢所有 --> <select id="findAll" resultType="Student"> select * from student where stuno=#{stuno} </select> <!-- 新增 --> <insert id="insertStudent"> insert into student(stuname,stuage,studate) value(#{stuname},#{stuage},#{studate}) </insert> </mapper>
5.mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 配置別名 --> <typeAliases> <!--方式一: 按類型名定制別名 --> <typeAlias type="cn.zhang.entity.Student" alias="Student" /> <!--方式二: 拿當前指定包下的簡單類名作為別名 --> <!-- <package name="cn.zhang.entity"/> --> </typeAliases> <environments default="mysql"> <environment id="oracle"> <!-- 使用jdbc的事務 --> <transactionManager type="JDBC" /> <!-- 使用自帶的連接池 --> <dataSource type="POOLED"> <!-- 我用的Oracle數據庫 --> <property name="driver" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@localhost:1521:orcl" /> <property name="username" value="zhangzong" /> <property name="password" value="123" /> </dataSource> </environment> <environment id="mysql"> <!-- 使用jdbc的事務 --> <transactionManager type="JDBC" /> <!-- 使用自帶的連接池 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/y2161" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <mappers> <mapper resource="cn/zhang/dao/StudentDAO.xml" /> </mappers> </configuration>
6.MyTest.java

package cn.zhang.test; import java.io.IOException; import java.util.Date; import org.apache.ibatis.session.SqlSession; import org.junit.Before; import org.junit.Test; import cn.zhang.dao.StudentDao; import cn.zhang.entity.Student; import cn.zhang.util.MybatisUtil; public class MyTest { StudentDao dao; @Before public void initData() throws IOException { SqlSession session = MybatisUtil.getSession(); dao = session.getMapper(StudentDao.class);// 使用了getMapper()方法獲得dao實例 } /** * 查詢所有學生 * * @throws IOException */ @Test public void findAll() throws IOException { //查詢 Student stu = dao.findAll(1); System.out.println(stu); //新增--執行commit操作(執行插入、更新、刪除),清空SqlSession中的一級緩存 /* Student student=new Student(); student.setStuage(12); student.setStuname("垃圾"); student.setStudate(new Date()); dao.insertStudent(student); */ //查詢---一級緩存中 Student stu2 = dao.findAll(1); System.out.println(stu2); } }