mybatis缓存机制

MyBatis 提供了查询缓存来缓存数据,以提高查询的性能。MyBatis 的缓存分为一级缓存二级缓存

  • 一级缓存是 SqlSession 级别的缓存
  • 二级缓存是 mapper 级别的缓存,多个 SqlSession 共享

这里写图片描述

一级缓存

一级缓存是 SqlSession 级别的缓存,是基于 HashMap 的本地缓存。不同的 SqlSession 之间的缓存数据区域互不影响。

一级缓存的作用域是 SqlSession 范围,当同一个 SqlSession 执行两次相同的 sql 语句时,第一次执行完后会将数据库中查询的数据写到缓存,第二次查询时直接从缓存获取不用去数据库查询。当 SqlSession 执行 insert、update、delete 操做并提交到数据库时,会清空缓存,保证缓存中的信息是最新的。

MyBatis 默认开启一级缓存。

二级缓存

二级缓存是 mapper 级别的缓存,同样是基于 HashMap 进行存储,多个 SqlSession 可以共用二级缓存,其作用域是 mapper 的同一个 namespace。不同的 SqlSession 两次执行相同的 namespace 下的 sql 语句,会执行相同的 sql,第二次查询只会查询第一次查询时读取数据库后写到缓存的数据,不会再去数据库查询。

MyBatis 默认没有开启二级缓存,开启只需在配置文件中写入如下代码:

<settings>  
      <setting name="cacheEnabled" value="true"/>  
</settings>
  • 1
  • 2
  • 3

缓存测试

#### 环境 
- JDK1.7 
- MySQL 5.5.50 
- mybatis 3.3.0

注:MyBatis 下载地址https://github.com/mybatis/mybatis-3/releases

需要的 jar 包

  • MyBatis jar 包: mybatis-3.3.0.jar
  • MySQL 驱动 jar 包: mysql-connector-java-xxx.jar
  • 日志记录 jar 包: log4j-xxx.jar

1.数据库准备

登录 MySQL 后新建一个数据库并取名 mybatis ,创建表 user ,并插入数据:

mysql> create table user(
    -> id int primary key auto_increment,
    -> name varchar(20),
    -> sex varchar(10),
    -> age int);

insert into user(name,sex,age) values('Tom','male',20),('Jack','male',19),('Rose','female',18),('Lucy','female',19);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.新建工程项目

(1)首先在 Eclipse 里新建一个动态 Web 工程(Dynamic Web Project),命名为 CacheTest 。 
(2)将前面提到的三个 jar 包拷贝到工程的/WebContent/WEB-INF/lib/ 目录下,如下: 
这里写图片描述

3.实体类

在 Java Resources/src 的包 cn.itcast.mybatis.po 下新建类 User.Java,一个用户具有 id、name、sex、age,address属性。

User.java 的代码如下:

package cn.itcast.mybatis.po;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
 * <p>Title: User</p>
 * <p>Description: 用户po类</p>
 * <p>Company: www.itcast.com</p> 
 * @author	
 * @date	2018-4-16上午10:00:07
 * @version 1.0
 */
public class User implements Serializable {
	private int id;
	private String username;// 用户姓名
	private String sex;// 性别
	private Date birthday;// 生日
	private String address;// 地址
	
	//多个订单
	private List<Orders> orderlist;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getSex() {
		return sex;
	}
	public void setSex(String sex) {
		this.sex = sex;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	
	
	public List<Orders> getOrderlist() {
		return orderlist;
	}
	public void setOrderlist(List<Orders> orderlist) {
		this.orderlist = orderlist;
	}
	@Override
	public String toString() {
		return "User [id=" + id + ", username=" + username + ", sex=" + sex
				+ ", birthday=" + birthday + ", address=" + address + "]";
	}
	
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

4.创建方法接口和定义映射文件

新建包 cn.itcast.mybatis.mapper,并在包下新建方法接口 UserMapper.java,代码如下:

package cn.itcast.mybatis.mapper;
import java.util.List;
import cn.itcast.mybatis.po.User;
import cn.itcast.mybatis.po.UserQueryVo;


/**
 * <p>Title: UserDao</p>
 * <p>Description: 用户mapper</p>
 * <p>Company: www.itcast.com</p> 
 * @author	龙哥
 * @date	2018-4-16下午2:47:51
 * @version 1.0
 */
public interface UserMapper {
	
	//根据用户id查询用户信息
	public User findUserById(int id) throws Exception;
	
	//根据用户名称  查询用户信息
	public List<User> findUserByName(String username) throws Exception;
	
	//自定义查询条件查询用户信息
	public List<User> findUserList(UserQueryVo userQueryVo) throws Exception;
	
	//查询用户,使用resultMap进行映射
	public List<User> findUserListResultMap(UserQueryVo userQueryVo)throws Exception;
	//查询用户,返回记录个数
	public int findUserCount(UserQueryVo userQueryVo) throws Exception;
	
	//插入用户
	public void insertUser(User user)throws Exception;
	//删除用户
	public void deleteUser(int id) throws Exception;
	//修改用户
	public void updateUser(User user) throws Exception;
	
	
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

同样在包下新建映射文件 UserMapper.xml,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="gler.cache.mapper.UserMapper">
    <delete id="deleteUser" parameterType="int">
        delete from user where
        id=#{id}
    </delete>

    <select id="selectUserById" parameterType="int" resultType="User">
        select * from user where id=#{id}
    </select>
</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

5.配置文件 SqlMapConfig.xml

在目录 Java Resources/config 下新建 MyBatis 配置文件 /SqlMapConfig.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>
        <package name="cn.itcast.mybatis.po" />
    </typeAliases> 
    <!-- 配置mybatis运行环境 -->
    <environments default="development">
        <environment id="development">
           <!-- type="JDBC" 代表直接使用 JDBC 的提交和回滚设置 -->
            <transactionManager type="JDBC" />

            <!-- POOLED 表示支持JDBC数据源连接池 -->
            <!-- 数据库连接池,由 Mybatis 管理,数据库名是 mybatis,MySQL 用户名 root,密码为 root -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis" />
                <property name="username" value="root" />
                <property name="password" value="root" />
            </dataSource>
        </environment>
    </environments> 
    <mappers>
        <!-- 通过 mapper 接口包加载整个包的映射文件 -->
        <package name="cn.itcast.mybatis.mapper" />
    </mappers>
</configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

6.日志记录 log4j.properties

在项目目录 Java Resources/src 下新建 MyBatis 日志记录文件 log4j.properties,在里面添加如下内容:

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

7.缓存测试

(1)创建一个获取 SqlSession 的辅助类 SqlSessionFac

在 Java Resources/src 的包 cn.itcast.mybatis.mapper 下新建辅助类 SqlSessionFac,代码如下:

package cn.itcast.mybatis.mapper;
import java.io.IOException;
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 SqlSessionFac {
    private static SqlSessionFactory sqlSessionFactory = null;
    static{
        String resource = "vSqlMapConfig.xml";
        InputStream inputStream = null;
        try {
            inputStream = Resources.getResourceAsStream(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

(2)一级缓存测试程序 
在 Java Resources/src 的包 cn.itcast.mybatis.mapper下新建一级缓存测试类 OneCacheTest,代码如下:

package cn.itcast.mybatis.mapper;import cn.itcast.mybatis.mapper.SqlSessionFac;import cn.itcast.mybatis.mapper.UserMapper;import cn.itcast.mybatis.po.User;import org.apache.ibatis.session.SqlSession;
public class OneCacheTest {
    public static void main(String[] args) throws Exception {
        OneCacheTest ot = new OneCacheTest();
        ot.test1();
        //ot.test2();
    }

    // 普通测试
    public void test1() throws Exception{
        SqlSession session = SqlSessionFac.getSqlSession();
        // 第一次查询
        User user = userMapper.selectUserById(1);
        System.out.println(user.toString());
        // 第二次查询
        User user2 = userMapper.selectUserById(1);
        System.out.println(user2.toString());
        session.close();
    }

    // 有delete操作和commit提交的测试
    public void test2() throws Exception{
        SqlSession session = SqlSessionFac.getSqlSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        // 第一次查询
        User user = userMapper.selectUserById(1);
        System.out.println(user.toString());
        // delete 和 commit
        userMapper.deleteUser(3);
        session.commit();//commit 会清空缓存
        // 第二次查询
        User user2 = userMapper.selectUserById(1);
        System.out.println(user2.toString());
        session.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

结果:

(1)运行 tes1() 方法

这里写图片描述

可以看到只有第一次查询时访问了数据库,第二次查询直接从缓存读取数据。

在这里我们将 test1() 中的代码改为下面的代码:

    public void test1() throws Exception{
        // 获取 SqlSession 对象
        SqlSession session = SqlSessionFac.getSqlSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        User user = userMapper.selectUserById(1);
        System.out.println(user.toString());
        // 关闭
        session.close();
        // 再次获取 SqlSession 对象
        session = SqlSessionFac.getSqlSession();
        userMapper = session.getMapper(UserMapper.class);
        User user2 = userMapper.selectUserById(1);
        System.out.println(user2.toString());
        session.close();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这里写图片描述

同样,由于是不同的 SqlSession,它们之间的缓存数据区域互不影响,因此两次均访问了数据库。

(2)运行test2() 方法

这里写图片描述

可以看到两次查询均访问了数据库,那是因为有 delete 操作,并把操作提交到了数据库,MyBatis 清空了缓存。

mybatis先是从sqlsession缓存中找,sqlsession缓存没有就会去数据库找。

(3)二级缓存测试程序

首先我们先在配置文件中开启二级缓存

注:settings 元素在 configuration 标签元素中书写顺序排在第二位

<settings>  
      <setting name="cacheEnabled" value="true"/>  
</settings>
  • 1
  • 2
  • 3

接着在 UserMapper.xml 映射文件中开启当前 mapper 的 namespace 下的二级缓存

<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"></cache>
  • 1

注:cache 元素书写顺序排在第一位

在 Java Resources/test 的包 cn.itcast.mybatis.mapper 下新建一级缓存测试类 TwoCacheTest,代码如下:

package cn.itcast.mybatis.mapper;

import cn.itcast.mybatis.mapper.SqlSessionFac;
import cn.itcast.mybatis.mapper.UserMapper;
import cn.itcast.mybatis.po.User;

import org.apache.ibatis.session.SqlSession;

public class TwoCacheTest {
    public static void main(String[] args) throws Exception {
        TwoCacheTest tt = new TwoCacheTest();
        tt.test();
    }

    public void test() throws Exception{
        // 获取 SqlSession 对象
        SqlSession session = SqlSessionFac.getSqlSession();
        UserMapper userMapper = session.getMapper(UserMapper.class);
        User user = userMapper.selectUserById(1);
        System.out.println(user.toString());
        // 关闭
        session.close();

        // 再次获取 SqlSession 对象
        session = SqlSessionFac.getSqlSession();
        userMapper = session.getMapper(UserMapper.class);
        User user2 = userMapper.selectUserById(1);
        System.out.println(user2.toString());
        session.close();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

这里写图片描述

可以看到只有第一次查询时访问了数据库,第二次查询直接从二级缓存读取数据。

参考链接

  • 《Spring+MyBatis 企业应用实战》

猜你喜欢

转载自blog.csdn.net/ywl470812087/article/details/79959671