Mybatis

1. Mybatis概述

MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects, 普通的 Java 对象) 映射成数据库中的记录。

特点

  1. SQL 编写的灵活性:MyBatis 允许开发者编写原生 SQL 语句,提供了极大的灵活性。

  2. 支持映射复杂类型:MyBatis 支持将数据库结果集的一行或多行映射到 Java 对象。

  3. 支持延迟加载:MyBatis 支持延迟加载,可以配置懒加载,提高应用程序性能。

  4. 支持注解和 XML 配置:MyBatis 支持使用注解或 XML 配置 SQL 映射。

  5. 支持多数据库:MyBatis 可以与多种数据库一起使用,例如 MySQL、PostgreSQL、Oracle 等。

  6. 支持事务管理:MyBatis 提供了对事务的细粒度控制。

  7. 支持缓存:MyBatis 支持内置的一级缓存和可以配置的二级缓存。

  8. 支持动态 SQL:MyBatis 支持动态 SQL 语句,可以根据条件构建 SQL 语句。

  9. 支持批量操作:MyBatis 支持批量插入、更新和删除操作。

  10. 支持映射文件的复用:MyBatis 允许开发者在多个映射文件中复用 SQL 语句。

  11. 支持插件:MyBatis 允许开发者通过插件扩展其功能。

  12. 支持分页查询:MyBatis 可以很容易地实现分页查询。

2. Mybatis执行流程

  1. 初始化

    加载配置文件,创建 SqlSessionFactory
  2. 获取 SqlSession

    通过 SqlSessionFactory 获取 SqlSession 对象,该对象包含了对数据库的操作方法。
  3. 执行操作

    通过 SqlSession 执行增删改查操作。MyBatis 会将接口和 XML 映射文件中的 SQL 语句进行动态代理,调用对应的方法时,会执行相应的 SQL。
  4. 提交事务

    如果是自动提交事务,MyBatis 会在每次操作后自动提交事务。如果不是自动提交事务,需要手动调用 SqlSession 的 commit 方法提交事务。
  5. 获取结果

    操作数据库后,可以通过 SqlSession 获取操作结果。
  6. 关闭 SqlSession

    操作完成后,需要关闭 SqlSession 以释放数据库资源。
  7. 错误处理

    在执行过程中如果发生异常,MyBatis 会回滚事务。

3. Mybatis基础操作

3.1 快速开始

  1. 创建SpringBoot项目

  2. 引入依赖

     <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.4</version>
     </dependency>
     <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
  3. application.properties中配置数据库连接参数

    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/db01
    spring.datasource.username=root
    spring.datasource.password=root1234
  4. 创建实体类Person,并创建相应的数据库表

    public class Person {
        private Integer id;
        private String name;
        private Integer age;
        // getters and setters
    }
  5. 创建PersonController

    @Controller
    public class PersonController {
    ​
        @Autowired
        private PersonMapper personMapper;
    ​
        @GetMapping("/find")
        @ResponseBody
        public List<Person> find() {
            List<Person> list = personMapper.findAll();
            System.out.println(list);
            return list;
        }
    }
  6. 创建com.codingfuture.mapper.PersonMapper接口

    @Mapper
    public interface PersonMapper {
        List<Person> find();
    }
  7. resources中添加映射文件com.codingfuture.mapper.PersonMapper.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.codingfuture.mapper.PersonMapper">
        <select id="find" resultType="com.codingfuture.entity.Person">
            select *
            from person;
        </select>
    </mapper>
  8. 测试

3.2 statement详解

Mybatis的核心是SQL,一个statement代表着一个SQL,因此,statement的配置即是我们通过Mybatis操作数据库的核心。

statement分为四个标签:<insert><delete><update><select>,分别代表对数据的增删改查。

标签中定义的便是原生的SQL语句,需要新掌握的是标签上的属性:

  • id

    每个statement都有,且为必选属性,id为statement提供唯一标识,以保证该statement可以被成功定位并执行。不能重复。

  • resultType

    只有select语句有该属性,代表SQL返回结果的类型,查询用户可以指定为entity.Person类型

    比如查询数据总条数,可以指定resultType为long类型,

    <select id="findAll" resultType="com.codingfuture.entity.Person">
            select * from person
    </select>
    ​
    <select id="total" resultType="long">
        select count(*) from person 
    </select>
  • parameterType

    如果SQL中需要传入参数,则可以通过该属性指定参数类型,如果不指定,Mybatis会自动判断,因此该属性没有实质作用。

    <select id="findAllById" parameterType="int" resultType="com.codingfuture.entity.Person">
            select * from person where id = #{id}
    </select>
  • resultMap

    只有select语句有,当SQL结果集不能自动映射到实体类属性时使用,

    比如数据库字段为person_id,而Person类中属性为personId,此时Mybatis不能自动映射,需要手动映射。

    以下为实例,id标签指定主键属性,result标签指定普通属性,column属性对应表中字段名,

    property属性对应类中属性名,autoMapping自动映射。可选

    <resultMap id="personResultMap" type="com.codingfuture.entity.Person">
            <id column="id" property="id"/>
            <!--        <result column="name" property="name"/>-->
            <!--        <result column="age" property="age"/>-->
             <result column="love_color" property="loveColor"/>
    </resultMap>
    ​
    <select id="findAll3" resultMap="personResultMap">
            select * from person
    </select>
  • insert插入

    <insert id="insert">
        insert into person(id,name,age,love_color)
         values
        (null,"zl",26,"紫色")
    </insert>
  • update更新

    <update id="updateById">
        update person
        set name ='kobe12'
        where id = 203
    </update>
  • delete删除

    <delete id="deleteById">
            delete
            from person
            where id = 204
    </delete>
    ​

3.3 注解

常见注解

  1. @Select: 用于查询操作。
  2. @Insert: 用于插入操作。
  3. @Update: 用于更新操作。
  4. @Delete: 用于删除操作。
  5. @Results: 用于定义结果映射。
  6. @Result: 用于定义单列映射。
  7. @Param: 用于指定参数名称。
  8. @SelectKey: 用于生成主键。
  9. @Options: 用于设置一些全局性的操作选项。

参数占位符

在 MyBatis 中,参数占位符通常用于 SQL 语句中,以便在执行时动态替换为实际的参数值。以下是一些常见的参数占位符:

  1. #{}: 预处理参数,防止 SQL 注入。MyBatis 会将参数转义,确保 SQL 的安全性。
  2. ${}: 直接将参数内容拼接到 SQL 中,可能会导致 SQL 注入风险。
  3. @Param: 用于指定参数名称,可以在 SQL 中通过名称引用参数。

以下是直接在Mapper层编写sql,适用于简单操作,实际使用多在xml映射文件中编写 :

@Mapper
public interface UserMapper {

    @Select("SELECT * FROM users WHERE id = #{id}")
    User selectUserById(int id);

    @Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
    int insertUser(@Param("name") String name, @Param("email") String email);

    @Update("UPDATE users SET name = #{name} WHERE id = #{id}")
    int updateUser(@Param("name") String name, @Param("id") int id);

    @Delete("DELETE FROM users WHERE id = #{id}")
    int deleteUser(int id);

    @Select("SELECT * FROM users")
    @Results({
        @Result(property = "id", column = "id"),
        @Result(property = "name", column = "name"),
        @Result(property = "email", column = "email")
    })
    List<User> findAllUsers();
}

4. 动态SQL

MyBatis 是一个优秀的持久层框架,它支持动态 SQL,允许开发者在 SQL 语句中根据条件动态地拼接不同的 SQL 片段。

  1. <where>

    <where> 标签用于动态地构建 SQL 的 WHERE 条件部分。它会自动处理语句的开头(不添加 "WHERE"),并且会智能地处理 AND 或 OR 的添加,以避免在生成的 SQL 中出现多余的 AND 或 OR。
  2. <if>

    <if> 标签用于条件判断,只有当测试表达式的值为 true 时,包含的 SQL 片段才会被包含在最终的 SQL 中。它常用于简单的条件判断。
  3. <foreach>

    <foreach> 标签用于遍历一个集合,并为每个元素生成 SQL 片段。它通常用于 IN 子句或批量操作(如批量插入、更新、删除)。
  4. <set>

    <set> 标签用于动态构建 UPDATE 语句中的 SET 部分。它会智能地处理逗号和生成的 SQL,确保不会出现多余的逗号。
  5. <choose><when><otherwise>

    这三个标签一起使用,提供了类似于编程语言中的 if-else 语句的功能。<choose> 标签表示一个条件选择块的开始。<when> 标签表示一个条件分支,只有当测试表达式的值为 true 时,包含的 SQL 片段才会被包含在最终的 SQL 中。<otherwise> 标签表示其他情况下的 SQL 片段,类似于编程中的 else 分支。
  6. <sql>

    <sql> 标签用于定义可重用的 SQL 片段,可以在不同的映射器中通过 <include> 标签引用。
  7. <include>

    <include> 标签用于引入定义在其他 XML 文件中的 SQL 片段,这有助于代码的重用和模块化。
  8. <bind>

    <bind> 标签用于创建一个变量,通常用于复杂的表达式,以提高 SQL 的可读性和维护性。
  9. <trim>

    <trim> 标签用于去除 SQL 片段前后的指定字符,如空格,或者用于拼接多个 SQL 片段,同时去除前后的分隔符。

代码示例

  1. <where> 示例:

    <select id="selectUsers" resultType="User">
      SELECT * FROM users
      <where>
        <if test="id != null">
          id = #{id}
        </if>
        <if test="username != null and username != ''">
          AND username = #{username}
        </if>
        <if test="status != null">
          AND status = #{status}
        </if>
      </where>
    </select>
  2. <if> 示例:

    <select id="selectUsersIf" resultType="User">
      SELECT * FROM users
      WHERE
      <if test="id != null">
        id = #{id}
      </if>
      <if test="username != null and username != ''">
        AND username = #{username}
      </if>
    </select>
  3. <foreach> 示例:

    <select id="selectUsersIn" resultType="User">
      SELECT * FROM users
      WHERE id IN
      <foreach item="id" collection="list" open="(" separator="," close=")">
        #{id}
      </foreach>
    </select>
  4. <set> 示例:

    <update id="updateUser">
      UPDATE users
      <set>
        <if test="username != null">username = #{username},</if>
        <if test="password != null">password = #{password},</if>
        <if test="email != null">email = #{email}</if>
      </set>
      WHERE id = #{id}
    </update>
  5. <choose><when><otherwise> 示例:

    <select id="selectUser" resultType="User">
      SELECT * FROM users
      WHERE
      <choose>
        <when test="id != null">
          id = #{id}
        </when>
        <when test="username != null and username != ''">
          username = #{username}
        </when>
        <otherwise>
          active = 1
        </otherwise>
      </choose>
    </select>
  6. <sql><include> 示例:

    <!-- UserMapper.xml -->
    <sql id="userColumns">
      id, username, password, email
    </sql>
    
    <select id="selectUsers" resultType="User">
      SELECT
      <include refid="userColumns"/>
      FROM users
    </select>
  7. <bind> 示例:

    <select id="selectUsersBind" resultType="User">
      SELECT * FROM users
      WHERE
      <bind name="sortedUsername" value="'%' + username + '%'"/>
      username LIKE #{sortedUsername}
    </select>
  8. <trim> 示例:

    <select id="selectUsersTrim" resultType="User">
      SELECT * FROM users
      WHERE
      <trim suffixOverrides="AND">
        <if test="id != null">
          id = #{id} AND
        </if>
        <if test="username != null and username != ''">
          username = #{username} AND
        </if>
      </trim>
    </select>

5. 多表映射

5.1 需求一

需求:查询订单信息,关联查询购买该订单的用户信息。

需求:查询订单信息,关联查询购买该订单的用户信息。

SELECT * FROM `order` o, `user` u WHERE o.user_id = u.id;

多表查询涉及到重名的字段,可以通过sql中的别名解决:

SELECT
    o.id order_id,
    o.create_time create_time,
    o.user_id user_id,
    u.username username,
    u.PASSWORD PASSWORD 
FROM
    `order` o,
    `user` u 
WHERE   o.user_id = u.id

查询出正确且合适的结果集后,就可以进行映射的配置了:

User类。用户信息

public class User {
    private Integer id;
    private String username;
    private String password;
        //getter setter
}

Order类。订单

public class Order {
    private Integer id;
    private Date createTime;
    private Integer userId;
    //private User user;//后加 代表订单所属用户
        //getter setter
}

Item类。商品

public class Item {
    private Integer id;
    private String name;
    private Double price;
   //getter setter
}

OrderItem类。订单明细

public class OrderItem {
    private Integer id;
    private Integer orderId;
    private Integer itemId;
    private Integer num;
    //getter setter
}

创建OderMapper接口和OrderMapper.xml

public interface OrderMapper {
    List<Order> findOrdersWithUser();
}
 <resultMap id="OrderUserMap" type="order" autoMapping="true">
        <id column="order_id" property="id"/>
        <result column="create_time" property="createTime"/>
        <result column="user_id" property="userId"/>
        <association property="user" javaType="user" autoMapping="true">
            <id column="user_id" property="id"/>
<!--            <result column="username" property="username"/>-->
<!--            <result column="password" property="password"/>-->
        </association>
    </resultMap>
​
​
    <select id="findOrdersWithUser" resultMap="OrderUserMap">
         SELECT
            o.id order_id,
            o.create_time create_time,
            o.user_id user_id,
            u.username username,
            u.PASSWORD PASSWORD
        FROM
            `order` o,
            `user` u
        WHERE
            o.user_id = u.id
</select>

<association>代表单一的关联,在多对一. 或. 一对一的关系中使用,注意需要指定javaType属性。

测试类:

    OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
    List<Order> ordersWithUser = mapper.findOrdersWithUser();
    for (Order order : ordersWithUser) {
        System.out.println(order);
    }

注意:

我们想偷个懒,标注红线的地方的映射我们想省略,

那就需要加一个配置,并添加autoMapping属性设置为true

mybatis.configuration.map-underscore-to-camel-case=true # 下划线转驼峰

5.2 需求二

需求:查询订单信息,关联查询它的订单明细信息。

SELECT * FROM`order` o,order_item oi WHERE o.id = oi.order_id

整理之后的sql语句:

SELECT
    o.id  order_id,
    o.create_time create_time,
    o.user_id user_id,
    oi.id order_item_id,
    oi.item_id item_id,
    oi.num num
FROM
    `order` o,
    order_item oi
WHERE 
    o.id = oi.order_id

Order

public class Order {
    private Integer id;
    private Date createTime;
    private Integer userId;
    private User user;// 代表订单所属用户
    private List<OrderItem> orderItems;//代表订单明细信息
    //getter setter toString
}

OrderMapper.xml映射文件配置

<resultMap id="OrderOrderItemMap" type="order" autoMapping="true">
        <id column="order_id" property="id"/>
<!--        <result column="create_time" property="createTime"/>-->
<!--        <result column="user_id" property="userId"/>-->
        <collection property="orderItems" ofType="orderItem" autoMapping="true">
             <id column="order_item_id" property="id"/>
<!--              <result column="order_id" property="orderId"/>-->
<!--              <result column="item_id" property="itemId"/>-->
<!--              <result column="num" property="num"/>-->
        </collection>
    </resultMap>
​
    <select id="findOrdersWithOrderItems" resultMap="OrderOrderItemMap">
    SELECT
        o.id  order_id,
        o.create_time create_time,
        o.user_id user_id,
        oi.id order_item_id,
        oi.item_id item_id,
        oi.num num
    FROM
        `order` o,
        order_item oi
    WHERE
        o.id = oi.order_id
    </select>
public interface OrderMapper {
   List<Order> findOrdersWithOrderItems();
}

测试:

    OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
    List<Order> ordersWithUser = mapper.findOrdersWithOrderItems();
    for (Order order : ordersWithUser) {
        System.out.println(order);
    }   

<collection>代表集合的关联,在一对多的关系中使用,注意需要通过ofType属性指定集合泛型的类型。

6. 缓存

6.1 缓存简介

Mybatis包含一个非常强大的查询缓存特性,它可以非常方便配置和定制。缓存可以极大的提升查询效率。

例如:每个用户登入的页面的菜单功能选项都是固定的,点击每个选项都需要去数据库中查询数据,那么对于所有用户来说,数据都是一样的,那么我们就没必要每次点击菜单功能选项都去查询数据库,那样效率会很低,用户很多的时候,数据库服务器负担就会很严重。

所以我们就需要用到缓存。

Mybatis的查询分为:

  • 一级缓存指的是sqlsession级别的。(本地缓存)

    • 一级缓存只会在同一个sqlsession之间共享,多个sqlsession之间的一级缓存是互相独立的,默认一直开启,没法关闭。

    • 与数据库同一次会话期间查询到的数据会放在本地缓存。以后如果需要获取相同数据,直接从缓存中拿,没必要再去查数据库。

  • 二级缓存指的是mapper(namespace)级别的。(全局缓存)

    • 二级缓存只会在同一个namespace下的mapper映射文件之间共享。

6.2 一级缓存

Mybatis默认支持一级缓存。

  • 第一次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。

  • 如果sqlSession去执行commit操作(执行插入、更新、删除),清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。

  • 第二次发起查询用户id为1的用户信息,先去找缓存中是否有id为1的用户信息,缓存中有,直接从缓存中获取用户信息。

@Configuration
public class SqlSessionConfig {
​
    @Autowired
    private DataSource dataSource;
​
    @Bean
    @Primary
    public SqlSessionFactory sqlSession() {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        SqlSessionFactory factory = null;
        try {
            sqlSessionFactoryBean.setMapperLocations(
                    new PathMatchingResourcePatternResolver().
                            getResources("classpath:mapper/*Mapper.xml"));//设置对应的xml文件地址
            factory = sqlSessionFactoryBean.getObject();
        } catch (Exception e1) {
            e1.printStackTrace();
        }
        return factory;
    }
}
    //一级缓存
    OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
  
    Order order1 = mapper.findByIdWithUserLazy(1);//查询用户id为1的用户信息
    System.out.println(order1.getUser());//获取User的用户信息
​
    Order order2 = mapper.findByIdWithUserLazy(1);
    System.out.println(order2.getUser());

结果:

一级缓存失效:

  1. sqlSession不同:相同数据在sqlSession域中会被共享,不同sqlSession域之间数据不会共享

  2. sqlSession相同:查询条件不同,(一级缓存中还没有这个要查询的数据)

  3. sqlSession相同:两次查询之间,增加了 CRUD操作(有可能CRUD会影响当前数据)

  4. sqlSession相同:手动清除了一级缓存数据,(缓存清空)

代码:

 @Autowired
 private SqlSessionFactory sqlSessionFactory;
​
@Test
public void test4() throws IOException {
  
    SqlSession sqlSession = sqlSessionFactory.openSession(true);//true自动提交
    SqlSession sqlSession1 = sqlSessionFactory.openSession(true);//true自动提交
        
    //1.分别获取不同的sqlSession对象
    OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
    Order order1 = mapper.findByIdWithUserLazy(1);
    System.out.println(order1.getUser());//获取User的用户信息
​
    OrderMapper mapper2 = sqlSession1.getMapper(OrderMapper.class);
    Order order2 = mapper.findByIdWithUserLazy(1);
    System.out.println(order2.getUser());//获取User的用户信息
  
  
   //2.
    OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
    Order order1 = mapper.findByIdWithUserLazy(1);//sql语句相同,但是 查询条件不同,
    Order order2 = mapper.findByIdWithUserLazy(3);//域中没有id为3的 用户信息
    System.out.println(order1.getUser());//获取User的用户信息
    System.out.println(order2.getUser());//获取User的用户信息
  
    //3.
    OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
    Order order1 = mapper.findByIdWithUserLazy(1);//执行操作是一样的
    System.out.println(order1.getUser());//获取User的用户信息
​
    //执行修改操作
    //mapper.updateOrdersWithUser(1);
​
    Order order2 = mapper.findByIdWithUserLazy(1);//执行操作是一样的
    System.out.println(order2.getUser());//获取User的用户信息 就是修改之后的
  
    //4
   OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
     Order order1 = mapper.findByIdWithUserLazy(1);//执行操作是一样的
   System.out.println(order1.getUser());//获取User的用户信息
​
   sqlSession.clearCache();//清空一级缓存
​
   Order order2 = mapper.findByIdWithUserLazy(1);//执行操作是一样的
   System.out.println(order2.getUser());//获取User的用户信息
​
}

1.

2.

3.

4.

6.3 二级缓存

二级缓存指的是mapper(namespace)级别的。一个namespace对应一个二级缓存

工作机制:

  1. 一个会话,查询一条数据,这个数据会被放在当前会话的一级缓存中

  2. 如果会话关闭,一级缓存中的数据会被保存到二级缓存中,新的会话查询信息,可以参考二级缓存

  3. sqlSession--->

    不同namespace查出的数据会放在自己的对应缓存中(map)

使用:

  1. 开启全局二级缓存配置:

    <!--默认是开启的-->
    mybatis.configuration.cache-enabled=true
  2. 开启mapper级别的缓存开关,在对应的OrderMapper.xml中添加缓存开关

    <cache></cache>
    <!-- 里面的参数属性 作为了解,如果什么都不配置,就是默认如下配置-->
    <!-- <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="5"></cache> -->
    <!--
    eviction:缓存的回收策略:
        • LRU – 最近最少使用的:移除最长时间不被使用的对象。
        • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
        • SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
        • WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
        • 默认的是 LRU。
    flushInterval:缓存刷新间隔
        缓存多长时间清空一次,默认不清空,设置一个毫秒值
    readOnly:是否只读:
        true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
                 mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
        false:非只读:mybatis觉得获取的数据可能会被修改。
                mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
    size:缓存存放多少元素;
    -->
  3. Pojo类需要序列化,并定义UID

    不是说因为学习了缓存我们才实现序列化,是因为POJO类都是实现数据持久化交互的

    public class Order implements Serializable {
        private static final long serialVersionUID = 4829831994525772316L;
    }
  4. 测试:我们把二级缓存配置注释。测试

    @Test
    public void test5() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession(true);//
        SqlSession sqlSession1 = sqlSessionFactory.openSession(true);//
    ​
        OrderMapper mapper = sqlSession.getMapper(OrderMapper.class);
        OrderMapper mapper1 = sqlSession1.getMapper(OrderMapper.class);
    ​
        Order order = mapper.findByIdWithUserLazy(1);
        System.out.println(order.getUser());//获取User的用户信息
      
        sqlSession.close();//关闭会话
    ​
        Order order1 = mapper1.findByIdWithUserLazy(1);
        System.out.println(order1.getUser());//获取User的用户信息
        sqlSession1.close();//关闭会话
    ​
    }

    结果:会发现我们当关闭一个sqlSession时,另一个sqlSession就需要在此请求sql语句,发送2次请求

  5. 我们把二级缓存配置 激活,测试

    会发现提示缓存命中率。Cache Hit Ratio。

    证明我们发送二次请求的时候,是从二级缓存中拿的数据,并没有再次发送sql

    注意:只有会话关闭了,该sqlSession数据才会跑到二级缓存中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值