Mybatis一级缓存详解

发布时间:2024年01月22日

MyBatis的一级缓存

一级缓存是SqlSession级别的,通过同一个lSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问

一级缓存失效的四种情况:

  1. 不同的SqlSession对应不同的一级缓存
  2. 同一个SqlSession但是查询条件不同
  3. 同一个SqlSession两次查询期间执行了任何一次增删改操作
  4. 同一个SqlSession两次查询期间手动清空了缓存

环境准备

  1. 普通的maven项目
    目录结构如下:
    在这里插入图片描述
  2. 导入对应的依赖
    pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.hu</groupId>
    <artifactId>RDmybatis07</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <!--Lombok在编译时不使用-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
    </dependencies>
</project>
  1. 编写需要的配置文件
    jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&rewriteBatchedStatements=true&useSSL=false
jdbc.username=root
jdbc.password=123456

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>


    <!--MyBatis 核 心 配 置 文 件 中 , 标 签 的 顺 序 :
     properties?,settings?,typeAliases?,typeHandlers?,
     objectFactory?,objectWrapperFactory?,reflectorFactory?,
     plugins?,environments?,databaseIdProvider?,mappers?
    -->

    <!--引入properties文件-->
    <properties resource="jdbc.properties"/>

    <settings>
        <!--开启驼峰转换功能-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。
         特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。
         默认时是flase
         -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--开启时,任一方法的调用都会加载该对象的所有延迟加载属性。
         否则,每个延迟加载属性会按需加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>
    <!--设置类型别名-->
    <typeAliases>
        <!--
        typeAlias:设置某个类型的别名属性:
        type:设置需要设置别名的类型
        alias:设置某个类型的别名,若不设置该属性,那么该类型拥有默认的别名,即类名
        且不区分大小写
        -->
        <!--
        <typeAlias type="com.softeem.mybatis.pojo.User"></typeAlias>
        通过typeAlias配置单个的类型别名
        通过package属性,将包下所有的类型设置默认的类型别名,即类名且不区分大小写
        -->
        <!--以包为单位,将包下所有的类型设置默认的类型别名,即类名且不区分大小写-->
        <package name="com.hu.pojo"/>
    </typeAliases>

    <!--
    environments:配置多个连接数据库的环境属性:
    default:设置默认使用的环境的id

    transactionManager:设置事务管理方式属性:
    type="JDBC|MANAGED"
    JDBC:表示当前环境中,执行SQL时,使用的是JDBC中原生的事务管理方式,
    事务的提交或回滚需要手动处理
    MANAGED:被管理,例如Spring

    dataSource:配置数据源属性:
    type:设置数据源的类型type="POOLED|UNPOOLED|JNDI"
    POOLED:表示使用数据库连接池缓存数据库连接
    UNPOOLED:表示不使用数据库连接池
    JNDI:表示使用上下文中的数据源
    -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>

        <environment id="test">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入映射文件-->

    <mappers>
        <!--
        <mapper resource="mappers/UserMapper.xml"/>
        配置mapper文件,有可能有很大xml,所以直接配置一个包下面的
        和别名一样

        以包为单位引入映射文件要求:
        1、mapper接口所在的包要和映射文件所在的包一致
        2、mapper接口要和映射文件的名字一致
        -->
        <package name="com.hu.mapper"/>
    </mappers>

</configuration>
  1. 对应的数据库
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES (1, '开发部');
INSERT INTO `dept` VALUES (2, '市场部');
INSERT INTO `dept` VALUES (3, '财务部');
INSERT INTO `dept` VALUES (4, '销售部');
INSERT INTO `dept` VALUES (5, '烟酒部');
INSERT INTO `dept` VALUES (6, '小卖部');

-- ----------------------------
-- Table structure for emp
-- ----------------------------
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `gender` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `salary` double NULL DEFAULT NULL,
  `join_date` date NULL DEFAULT NULL,
  `dept_id` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `dept_id`(`dept_id`) USING BTREE,
  CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`dept_id`) REFERENCES `dept` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 39 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of emp
-- ----------------------------
INSERT INTO `emp` VALUES (1, '孙悟空', '男', 7200, '2013-02-24', 5);
INSERT INTO `emp` VALUES (3, '唐僧', '男', 9000, '2008-08-08', 2);
INSERT INTO `emp` VALUES (4, '白骨精', '女', 5000, '2015-10-07', 3);
INSERT INTO `emp` VALUES (5, '蜘蛛精', '女', 4500, '2011-03-14', 1);
INSERT INTO `emp` VALUES (6, '沙僧', '男', 7200, '2013-12-05', 1);
INSERT INTO `emp` VALUES (7, '白龙马', '男', 7000, '2013-12-05', 1);
INSERT INTO `emp` VALUES (8, '小明', '女', 2000, '2020-01-01', 2);
INSERT INTO `emp` VALUES (9, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (11, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (15, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (16, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (17, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (18, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (19, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (20, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (21, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (22, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (23, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (24, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (25, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (26, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (27, '小明', '女', 2000, '2020-01-01', 1);
INSERT INTO `emp` VALUES (28, '小灰灰1', '男', 3100, '2022-08-30', 5);
INSERT INTO `emp` VALUES (33, '小灰灰3', '男', 3300, '2022-08-30', 5);
INSERT INTO `emp` VALUES (34, '张三', '男', 1000, '2022-10-05', 1);

代码讲解:

1.在EmpMapper中添加一个方法

public interface EmpMapper {
    Emp findEmpById(Integer id);
}

2.配置对应的xml文件,EmpMapper.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="com.hu.mapper.EmpMapper">

    <resultMap id="empMap" type="emp">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="dept_id" property="deptId"/>
        <result column="salary" property="salary"/>
        <result column="join_date" property="joinDate"/>
        <result column="gender" property="gender"/>
    </resultMap>

    <select id="findEmpById" resultMap="empMap">
        select * from emp where id = #{id}
    </select>
</mapper>

3.测试

  • 第一个测试类EmpTest
public class EmpTest {
    SqlSession sqlSession = null;

    @Before
    public void init() throws IOException {
        // 1. 先读取我们所需要的配置类对象
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2. 创建我们所需要的SqlSessionFactoryBuilder来创建我们所需要的SqlSessionFactory
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 3. 获取SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
        // 4. 通过工厂对象来获取SqlSession对象 , 并且设置为自动提交
        //sqlSession = sqlSessionFactory.openSession();
        sqlSession = sqlSessionFactory.openSession(true);
    }

    @Test
    public void test() {
        // 1. 通过SqlSession对象来获取Mapper对象
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        // 2. 通过Mapper对象来执行查询操作
        Emp emp = mapper.findEmpById(1);
        System.out.println(emp);
        Emp emp2 = mapper.findEmpById(1);
        System.out.println(emp2);
    }
}

测试结果如图:
在这里插入图片描述
可以看到,我们在执行同一个查询语句的时候,第二次查询的时候没有显示执行sql的语句,而是直接显示结果,这表示,我们是从缓存中直接读取数据,提高我们的查询速度.

  • 第二个测试EmpTest
	@Test
    public void test() {
        // 1. 通过SqlSession对象来获取Mapper对象
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        // 2. 通过Mapper对象来执行查询操作
        Emp emp = mapper.findEmpById(1);
        System.out.println(emp);
        Emp emp2 = mapper.findEmpById(2);
        System.out.println(emp2);
    }

测试结果如下:
在这里插入图片描述
由此可知,当我们查询条件不同,我们的一级缓存会失效.

  • 第三个测试EmpTest
@Test
    public void test() {
        // 1. 通过SqlSession对象来获取Mapper对象
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        // 2. 通过Mapper对象来执行查询操作
        Emp emp = mapper.findEmpById(1);
        System.out.println(emp);
        sqlSession.clearCache();
        Emp emp2 = mapper.findEmpById(1);
        System.out.println(emp2);
    }

在这里插入图片描述
由此可见,当我们主动清除缓存的时候,一级缓存也会失效

  • 第四个测试EmpTest
    在EmpMapper中添加一个方法
public interface EmpMapper {
    Emp findEmpById(Integer id);

    int deleteEmpById(Integer id);
}

编写对应的xm文件EmpMapper.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="com.hu.mapper.EmpMapper">

    <resultMap id="empMap" type="emp">
        <id column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="dept_id" property="deptId"/>
        <result column="salary" property="salary"/>
        <result column="join_date" property="joinDate"/>
        <result column="gender" property="gender"/>
    </resultMap>


    <select id="findEmpById" resultMap="empMap">
        select * from emp where id = #{id}
    </select>

    <delete id="deleteEmpById">
        delete from emp where id = #{id}
    </delete>
</mapper>

EmpTest.java

@Test
    public void test() {
        // 1. 通过SqlSession对象来获取Mapper对象
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        // 2. 通过Mapper对象来执行查询操作
        Emp emp = mapper.findEmpById(1);
        System.out.println(emp);

        mapper.deleteEmpById(2);

        Emp emp2 = mapper.findEmpById(1);
        System.out.println(emp2);
    }

在这里插入图片描述
由此可见,当我们在俩次查询语句中进行了增删改操作,也会导致一级缓存失效

  • 第五个测试EmpTest
    编写一个通过名字查询的方法
    EmpMapper.java
public interface EmpMapper {
    Emp findEmpById(Integer id);

    int deleteEmpById(Integer id);

    Emp findEmpByName(String name);
}

编写对应的EmpMapper.xml

<select id="findEmpByName" resultMap="empMap">
        select * from emp where name = #{name}
    </select>

测试类EmpTest

@Test
    public void test() {
        // 1. 通过SqlSession对象来获取Mapper对象
        EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
        // 2. 通过Mapper对象来执行查询操作
        Emp emp = mapper.findEmpById(1);
        System.out.println(emp);
        mapper.deleteEmpById(2);
        Emp emp2 = mapper.findEmpByName("孙悟空");
        System.out.println(emp2);
    }

测试结果如下:
在这里插入图片描述
如图我们可以看出,当我们查询同一个对象,但是是不同的条件时,一级缓存也会失效.

  • 第五个测试EmpTest
    修改测试代码,使用俩个SqlSession进行测试一级缓存
public class EmpTest {
    SqlSession sqlSession1 = null;
    SqlSession sqlSession2 = null;
    @Before
    public void init() throws IOException {
        // 1. 先读取我们所需要的配置类对象
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2. 创建我们所需要的SqlSessionFactoryBuilder来创建我们所需要的SqlSessionFactory
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        // 3. 获取SqlSessionFactory对象
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
        // 4. 通过工厂对象来获取SqlSession对象 , 并且设置为自动提交
        //sqlSession = sqlSessionFactory.openSession();
        sqlSession1 = sqlSessionFactory.openSession(true);
        sqlSession2 = sqlSessionFactory.openSession(true);
    }

    @Test
    public void test() {
        // 1. 通过SqlSession对象来获取Mapper对象
        EmpMapper mapper = sqlSession1.getMapper(EmpMapper.class);
        // 2. 通过Mapper对象来执行查询操作
        Emp emp = mapper.findEmpById(1);
        System.out.println(emp);
        mapper.deleteEmpById(2);
        System.out.println("_____________________________");
        EmpMapper mapper1 = sqlSession2.getMapper(EmpMapper.class);
        Emp emp2 = mapper1.findEmpById(1);
        System.out.println(emp2);
    }
}

运行结果如下:
在这里插入图片描述
由此可见,但我们使用俩个SqlSession时,俩个SqlSession对应着不同的一级缓存.

文章来源:https://blog.csdn.net/m0_72926194/article/details/135743189
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。