06—mybatis缓存机制

MyBatis缓存分为一级缓存和二级缓存

一级缓存
MyBatis的一级缓存指的是在一个Session域内,session为关闭的时候执行的查询会根据SQL为key被缓存(跟mysql缓存一样,修改任何参数的值都会导致缓存失效)

1.创建表
CREATE TABLE `tb_user` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(18) DEFAULT NULL,
  `sex` CHAR(2) DEFAULT NULL,
  `age` INT(11) DEFAULT NULL,
  PRIMARY KEY  (`id`)
)
INSERT INTO `mybatis`.`tb_user` (`name`, `sex`, `age`) VALUES('1111', '男', 20) ;
INSERT INTO `mybatis`.`tb_user` (`name`, `sex`, `age`) VALUES('2222', '女', 22) ;
INSERT INTO `mybatis`.`tb_user` (`name`, `sex`, `age`) VALUES('3333', '女', 23) ;
INSERT INTO `mybatis`.`tb_user` (`name`, `sex`, `age`) VALUES('4444', '男', 24) ;
INSERT INTO `mybatis`.`tb_user` (`name`, `sex`, `age`) VALUES('5555', '女', 25) ;

 2.创建model

 User.java

package org.fkit.domain;
import java.io.Serializable;
public class User implements Serializable{
    private Integer id;
    private String name;
    private String sex;
    private Integer age;
    public User() {
        super();
    }
    public User( String name, String sex, Integer age) {
        super();
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", sex=" + sex + ", age="
                + age + "]";
    }
}

3.mapper.java和mapper.xml
UserMapper.java

package org.fkit.mapper;
import java.util.List;
import org.fkit.domain.User;
public interface UserMapper {
    // 根据id查询User
    User selectUserById(Integer id);
    // 查询所有User
    List<User> selectAllUser();
    // 根据id删除User
    void deleteUserById(Integer id);
}

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">
<!-- namespace指用户自定义的命名空间。 -->
<mapper namespace="org.fkit.mapper.UserMapper">
  <!-- 根据id查询User -->
  <select id="selectUserById" parameterType="int" 
  resultType="org.fkit.domain.User">
      SELECT * FROM TB_USER WHERE id = #{id}
  </select>
  <!-- 查询所有User -->
  <select id="selectAllUser" resultType="org.fkit.domain.User">
      SELECT * FROM TB_USER 
  </select>
  <!-- 根据id删除User -->
  <delete id="deleteUserById" parameterType="int">
      DELETE FROM TB_USER WHERE id = #{id}
  </delete>
</mapper>

 4.mybatis-config.xml

db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/mybatis
username=root
password=123456

log4j.properties

# Global logging configuration
log4j.rootLogger=ERROR, stdout
# MyBatis logging configuration...
log4j.logger.org.fkit.mapper.UserMapper=DEBUG
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

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">
  <!--  XML 配置文件包含对 MyBatis 系统的核心设置 -->
<configuration>
    <properties resource="db.properties"/>
    <!-- 指定 MyBatis 所用日志的具体实现 -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    <!-- 设置别名 -->
    <typeAliases>
        <typeAlias  alias="user" type="org.fkit.domain.User"/>
    </typeAliases>
    <environments default="mysql">
    <!-- 环境配置,即连接的数据库。 -->
    <environment id="mysql">
    <!--  指定事务管理类型,type="JDBC"指直接简单使用了JDBC的提交和回滚设置 -->
      <transactionManager type="JDBC"/>
      <!--  dataSource指数据源配置,POOLED是JDBC连接对象的数据源连接池的实现。 -->
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <!-- mappers告诉了MyBatis去哪里找持久化类的映射文件 -->
  <mappers>
      <mapper resource="org/fkit/mapper/UserMapper.xml"/>
  </mappers>
</configuration>

5.测试

(1).封装factory

package org.fkit.factory;
import java.io.InputStream;
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 FKSqlSessionFactory {
    private static SqlSessionFactory sqlSessionFactory = null;
    // 初始化创建SqlSessionFactory对象
    static{
        try {
            // 读取mybatis-config.xml文件
            InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder()
                    .build(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    // 获取SqlSession对象的静态方法
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
    // 获取SqlSessionFactory的静态方法
    public static SqlSessionFactory getSqlSessionFactory() {
        return sqlSessionFactory;
    }
}

(2).测试类

package org.fkit.test;
import org.apache.ibatis.session.SqlSession;
import org.fkit.domain.User;
import org.fkit.factory.FKSqlSessionFactory;
import org.fkit.mapper.UserMapper;

public class TestOneLevelCache {

    public static void main(String[] args) throws Exception {
        TestOneLevelCache t = new TestOneLevelCache();    
        t.testCache1(); //sql只被执行一次
//        t.testCache2();
//        t.testCache3();
    }
     /*
      * 一级缓存: 也就Session级的缓存(默认开启)
      */
    public void testCache1 (){
        // 使用工厂类获得SqlSession对象
        SqlSession session = FKSqlSessionFactory.getSqlSession();
        // 获得UserMapping对象
        UserMapper um = session.getMapper(UserMapper.class);
        // 查询id为1的User对象,会执行select语句
        User user = um.selectUserById(1);
        System.out.println(user);
        // 再次查询id为1的User对象,因为是同一个SqlSession,所以会从之前的一级缓存中查找数据
        User user2 = um.selectUserById(1);
        System.out.println(user2);
        // 关闭SqlSession对象
        session.close();
    }
    public void testCache2 (){
        // 使用工厂类获得SqlSession对象
        SqlSession session = FKSqlSessionFactory.getSqlSession();
        // 获得UserMapping对象
        UserMapper um = session.getMapper(UserMapper.class);
        // 查询id为1的User对象,会执行select语句
        User user = um.selectUserById(1);
        System.out.println(user);
        // 执行delete操作
        um.deleteUserById(5);
        // commit提交
        session.commit();
        // 再次查询id为1的User对象,因为DML操作会清空SqlSession缓存,所以会再次执行select语句
        User user2 = um.selectUserById(1);
        System.out.println(user2);
        // 关闭SqlSession对象
        session.close();
    }
    public void testCache3 (){
        // 使用工厂类获得SqlSession对象
        SqlSession session = FKSqlSessionFactory.getSqlSession();
        // 获得UserMapping对象
        UserMapper um = session.getMapper(UserMapper.class);
        // 查询id为1的User对象,会执行select语句
        User user = um.selectUserById(1);
        System.out.println(user);
        // 关闭一级缓存
        session.close();
        // 再次访问,需要再次获取一级缓存,然后才能查找数据,否则会抛出异常
        session = FKSqlSessionFactory.getSqlSession();
        // 再次获得UserMapping对象
        um = session.getMapper(UserMapper.class);
        // 再次访问,因为现在使用的是一个新的SqlSession对象,所以会再次执行select语句
        User user2 = um.selectUserById(1);
        System.out.println(user2);
        // 关闭SqlSession对象
        session.close();
    }
}
知识点分析:
testCache1结果
DEBUG [main] - ==>  Preparing: SELECT * FROM TB_USER WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, name=1111, sex=男, age=20]
User [id=1, name=1111, sex=男, age=20]
这里只执行了一条sql语句
 
testCache2结果
DEBUG [main] - ==>  Preparing: SELECT * FROM TB_USER WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, name=1111, sex=男, age=20]
DEBUG [main] - ==>  Preparing: DELETE FROM TB_USER WHERE id = ? 
DEBUG [main] - ==> Parameters: 5(Integer)
DEBUG [main] - <==    Updates: 1
DEBUG [main] - ==>  Preparing: SELECT * FROM TB_USER WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, name=1111, sex=男, age=20]
这里用了session.commit();已经提交了操作,所以sqlsession缓存被清空,所以会执行sql语句
 
testCache3结果
DEBUG [main] - ==>  Preparing: SELECT * FROM TB_USER WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, name=1111, sex=男, age=20]
DEBUG [main] - ==>  Preparing: SELECT * FROM TB_USER WHERE id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [id=1, name=1111, sex=男, age=20]
这里关闭了sqlsession(session.close())所以缓存是一个新的对象。
 
二级缓存
二级缓存就是global caching,它超出session范围之外,可以被所有sqlSession共享,它的实现机制和mysql的缓存一样,开启它只需要在mybatis的配置文件开启settings里的
<setting name="cacheEnabled" value="true"/>

1.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">
  <!--  XML 配置文件包含对 MyBatis 系统的核心设置 -->
<configuration>
    <properties resource="db.properties"/>
    <!-- 指定 MyBatis 所用日志的具体实现 -->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
        <!-- 开启二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    <!-- 设置别名 -->
    <typeAliases>
        <typeAlias  alias="user" type="org.fkit.domain.User"/>
    </typeAliases>
    <environments default="mysql">
    <!-- 环境配置,即连接的数据库。 -->
    <environment id="mysql">
    <!--  指定事务管理类型,type="JDBC"指直接简单使用了JDBC的提交和回滚设置 -->
      <transactionManager type="JDBC"/>
      <!--  dataSource指数据源配置,POOLED是JDBC连接对象的数据源连接池的实现。 -->
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <!-- mappers告诉了MyBatis去哪里找持久化类的映射文件 -->
  <mappers>
      <mapper resource="org/fkit/mapper/UserMapper.xml"/>
  </mappers>
</configuration>

2.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">
<!-- namespace指用户自定义的命名空间。 -->
<mapper namespace="org.fkit.mapper.UserMapper">
    <!-- 开启二级缓存 
       回收策略为先进先出
       自动刷新时间60s
       最多缓存512个引用对象
       只读
eviction:收回策略,默认为LRU。
              LRU:最近最少使用的策略,移除最长时间不被使用的对像。
    FIFO:先进先出策略,按对像进入缓存的顺序来移除它们。
    SOFT:软引用策略,移除基于垃圾回收器的状态和软引用规则的对像。
    WEAK:弱引用策略,更积极地移除基于垃圾收集器的状态和弱引用规则的对像。
flushInterval:刷新间隔,毫秒为单位。
size:缓存数目。默认值1024。
readOnly:只读,默认是false。
   -->
    <cache 
    eviction="LRU"  
    flushInterval="60000" 
    size="512" 
    readOnly="true"/> 
  <!-- 根据id查询User -->
  <select id="selectUserById" parameterType="int" 
  resultType="org.fkit.domain.User" useCache='true'>
      SELECT * FROM TB_USER WHERE id = #{id}
  </select>
  <!-- 查询所有User -->
  <select id="selectAllUser" resultType="org.fkit.domain.User">
      SELECT * FROM TB_USER 
  </select>
  <!-- 根据id删除User -->
  <select id="deleteUserById" parameterType="int">
      DELETE FROM TB_USER WHERE id = #{id}
  </select>
</mapper>

3.测试

TestTwoLevelCache.java

package org.fkit.test;
import org.apache.ibatis.session.SqlSession;
import org.fkit.domain.User;
import org.fkit.factory.FKSqlSessionFactory;
import org.fkit.mapper.UserMapper;
public class TestTwoLevelCache {
    public static void main(String[] args) throws Exception {
        TestTwoLevelCache t = new TestTwoLevelCache();
        //t.testCache1();
        t.testCache2();
    }
    
    public void testCache1 (){
        // 使用工厂类获得SqlSession对象
        SqlSession session = FKSqlSessionFactory.getSqlSession();
        // 获得UserMapping对象
        UserMapper um = session.getMapper(UserMapper.class);
        // 查询id为1的User对象,会执行select语句
        User user = um.selectUserById(1);
        System.out.println(user);
        // 执行delete操作
        um.deleteUserById(5);
        // commit提交
        session.commit();
        // 再次查询id为1的User对象,因为DML操作会清空SqlSession缓存,所以会再次执行select语句
        User user2 = um.selectUserById(1);
        System.out.println(user2);
        // 关闭SqlSession对象
        session.close();
    }
    
    public void testCache2 (){
        // 使用工厂类获得SqlSession对象
        SqlSession session = FKSqlSessionFactory.getSqlSession();
        // 获得UserMapping对象
        UserMapper um = session.getMapper(UserMapper.class);
        // 查询id为1的User对象,会执行select语句
        User user = um.selectUserById(2);
        System.out.println(user);
        // 关闭一级缓存
        session.close();
        // 再次访问,需要再次获取一级缓存,然后才能查找数据,否则会抛出异常
        session = FKSqlSessionFactory.getSqlSession();
        // 再次获得UserMapping对象
        um = session.getMapper(UserMapper.class);
        // 再次访问,因为现在使用的是一个新的SqlSession对象,所以会再次执行select语句
        User user2 = um.selectUserById(2);
        System.out.println(user2);
        // 关闭SqlSession对象
        session.close();
    }
}
知识点分析:
testCache1结果
DEBUG [main] - Cache Hit Ratio [org.fkit.mapper.UserMapper]: 0.0
DEBUG [main] - ==>  Preparing: SELECT * FROM TB_USER WHERE id = ? 
DEBUG [main] - ==> Parameters: 2(Integer)
DEBUG [main] - <==      Total: 1
User [id=2, name=22222, sex=2, age=19]
DEBUG [main] - Cache Hit Ratio [org.fkit.mapper.UserMapper]: 0.0
DEBUG [main] - ==>  Preparing: DELETE FROM TB_USER WHERE id = ? 
DEBUG [main] - ==> Parameters: 5(Integer)
DEBUG [main] - Cache Hit Ratio [org.fkit.mapper.UserMapper]: 0.3333333333333333
User [id=2, name=22222, sex=2, age=19]
testCache2结果
DEBUG [main] - Cache Hit Ratio [org.fkit.mapper.UserMapper]: 0.0
DEBUG [main] - ==>  Preparing: SELECT * FROM TB_USER WHERE id = ? 
DEBUG [main] - ==> Parameters: 2(Integer)
DEBUG [main] - <==      Total: 1
User [id=2, name=22222, sex=2, age=19]
DEBUG [main] - Cache Hit Ratio [org.fkit.mapper.UserMapper]: 0.5
User [id=2, name=22222, sex=2, age=19]

 

 

posted @ 2017-03-10 15:49  码大坑  阅读(333)  评论(0编辑  收藏  举报