mybatis缓存(上)---一级缓存

  • 什么是一级缓存

          在日常开发过程中,经常会有相同的sql执行多次查询的情况,mybatis提供了一级缓存来优化这些查询,避免多次请求数据库。

          一级缓存在mybatis中默认是开启的并且是session级别,它的作用域为一次sqlSession会话。

  • 什么是二级缓存

          相对于一级缓存,二级缓存的作用域更广泛,它不止局限于一个sqlSession,可以在多个sqlSession之间共享,事实上,它的作用域是namespace。

          mybatis的二级缓存默认也是开启的,但由于它的作用域是namespace,所以还需要在mapper.xml中开启才能生效

  • 缓存的优先级

          通过mybatis发起的查询,作用顺序为:二级缓存->一级缓存->数据库 ,其中任何一个环节查到不为空的数据,都将直接返回结果

  • 缓存失效

          当在一个缓存作用域中发生了update、insert、delete 动作后,将会触发缓存失效,下一次查询将命中数据库,从而保证不会查到脏数据。相当于缓存只是针对select有效。

 

下面通过一些示例来分别说明。(本文采用的是springboot+mybatis的方式进行测试的)

 

准备条件:

1 create table book(
2 id int auto_increment comment '书ID',
3 name varchar(50) comment '书名',
4 primary key(id));
5 
6 
7 insert into book(name) values('三国演义');
8 insert into book(name) values('红楼梦');
9 insert into book(name) values('水浒传');
View Code

默认引擎是InnoDB.

一. 一级缓存

1.实体类Book

 1 package com.example.demo.dao;
 2 
 3 public class Book {
 4     private int id;
 5     private String name;
 6 
 7     public int getId() {
 8         return id;
 9     }
10 
11     public void setId(int id) {
12         this.id = id;
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public void setName(String name) {
20         this.name = name;
21     }
22 }
View Code

2.Controller: BookController

 1 package com.example.demo.controller;
 2 
 3 import com.example.demo.dao.Book;
 4 import com.example.demo.service.BookService;
 5 import org.springframework.beans.factory.annotation.Autowired;
 6 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 7 import org.springframework.web.bind.annotation.GetMapping;
 8 import org.springframework.web.bind.annotation.RequestMapping;
 9 import org.springframework.web.bind.annotation.RestController;
10 
11 @RestController
12 @EnableAutoConfiguration
13 @RequestMapping("/book")
14 public class BookController {
15     @Autowired
16     BookService bookService;
17 
18     @GetMapping("/selectBookById")
19     Book selectBookById(){
20       return bookService.selectBookById();
21     }
22 }
View Code

3.Service:BookService/Impl

1 package com.example.demo.service;
2 
3 import com.example.demo.dao.Book;
4 
5 
6 public interface BookService {
7   Book selectBookById();
8 }
View Code
 1 package com.example.demo.service.impl;
 2 
 3 import com.example.demo.dao.Book;
 4 import com.example.demo.dao.mapper.BookMapper;
 5 import com.example.demo.service.BookService;
 6 import org.springframework.beans.factory.annotation.Autowired;
 7 import org.springframework.stereotype.Service;
 8 
 9 
10 @Service
11 public class BookServiceImpl implements BookService {
12 
13     @Autowired
14     BookMapper bookMapper;
15     @Override
16 
17     public Book selectBookById() {
18         int id=1;
19         Book book1=bookMapper.selectBookById(id);
20         Book book2=bookMapper.selectBookById(id);
21         System.out.println("***********start***********");
22         System.out.println(book1==book2);
23         System.out.println("***********end***********");
24         return bookMapper.selectBookById(id);
25     }
26 }
View Code

4.Mapper接口:BookMapper

1 package com.example.demo.dao.mapper;
2 
3 import com.example.demo.dao.Book;
4 import org.apache.ibatis.annotations.Mapper;
5 
6 @Mapper
7 public interface BookMapper {
8     Book selectBookById(int id);
9 }
View Code

 5.mapper.xml文件:BookMapper.xml

 

1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
3 <mapper namespace="com.example.demo.dao.mapper.BookMapper">
4 
5     <select id="selectBookById" resultType="com.example.demo.dao.Book">
6         select id,name from book where id=#{id}
7     </select>
8 
9 </mapper>
View Code

6.application.yml配置为:

 1 server:
 2   port: 8081
 3 spring:
 4   #���ݿ���������
 5   datasource:
 6     driver-class-name: com.mysql.cj.jdbc.Driver
 7     url: jdbc:mysql://localhost:3306/mytest?characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
 8     username: root
 9     password: root
10 
11 #mybatis���������
12 mybatis:
13   #mapper�����ļ�
14   mapper-locations: classpath:mapper/*.xml
15   #type-aliases-package: com.example.demo.dao
16   #�����շ�����
17   configuration:
18     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
19 
20   type-handlers-package: com.example.demo.dao.mapper.MyEnumHandler
View Code

 浏览器中输入:http://localhost:8081/book/selectBookById

查看控制台:

 从控制台可以看见创建了3次SqlSession会话,一级缓存失效。并且得到的book1和book2两个对象不等。实际上,默认是开启的一级缓存,只是我们没有添加事务管理,从而拿到了我们以为错误的结果。

修改Service如下:

@Transactional
    public Book selectBookById() {
        int id=1;
        Book book1=bookMapper.selectBookById(id);
        Book book2=bookMapper.selectBookById(id);
        System.out.println("***********start***********");
        System.out.println(book1==book2);
        System.out.println("***********end***********");
        return bookMapper.selectBookById(id);
    }

仅仅是添加了@Transactional注解:

此时控制台输出为:

可以看到只创建了一次SqlSession会话,book2的值对应第一个Fetched SqlSession,方法的返回对应第二个Fetched SqlSession. 说明确实是从缓存拿到的数据。

note:增删改无论加不加这个注解,都不会存在缓存现象。它们的每一次submit就认为会关闭当前SqlSession。一级缓存是和SqlSession是绑定的,只存在于SqlSession生命周期中。增删改操作都会清空一级缓存。

note:添加@Transaction注解的原因: 由于使用了数据库连接池,默认每次查询完之后自动commit,这就导致两次查询使用的不是同一个sqlSessioin,根据一级缓存的原理,它将永远不会生效。
当我们开启了事务,两次查询都在同一个sqlSession中,从而让第二次查询命中了一级缓存。

note: mybatis默认的session级别一级缓存,由于springboot中默认使用了hikariCP,所以基本没用,需要开启事务才有用。但一级缓存作用域仅限同一sqlSession内,无法感知到其他sqlSession的增删改,所以极易产生脏数据

posted @ 2020-11-25 15:03  Hermioner  阅读(368)  评论(0编辑  收藏  举报