MyBatis的关联映射和缓存机制

news2024/10/2 20:30:21

学习目标:

  • 了解数据表之间的三种关联关系
  • 了解对象之间的三种关系
  • 熟悉关联关系中的嵌套查询和嵌套结果
  • 掌握一对一关联映射
  • 掌握—对多关联映射
  • 掌握多对多关联映射
  • 熟悉Mybatis的缓存机制

 文章概述:

前面几章介绍了MyBatis的基本用法、关联映射和动态SQL等重要知识,但这些知识只是针对单表实现进行操作的,在实际开发中,对数据库的操作常常会涉及到多张表,针对多表之间的操作,MyBatis提供了关联映射,通过关联映射可以很好地处理表与表、对象与对象之间的关联关系。此外,在实际开发中经常需要合理地利用MyBatis缓存来加快数据库查询,进而有效地提升数据库性能。本章将对MyBatis的关联映射以及MyBatis缓存机制进行详细讲解。

关联映射关系 

数据库中表和表的关系:

  • 一对—关系:
    • 一个数据表中的一条记录最多可以和另一个数据表中的一条记录相关。例如现实生活中学生与校园卡就属于一对一的关系,一个学生只能拥有一张校园卡,一张校园卡只能属于一个学生。
  • —对多关系:
    • 主键数据表中的一条记录可以和另外一个数据表的多条记录相关。但另外一个数据表中的记录只能与主键数据表中的某一条记录相关。例如,现实中班级与学生的关系就属于一对多的关系,一个班级可以有很多学生,但一个学生只能属于一个班级。
  • 多对多关系:
    • 一个数据表中的一条记录可以与另外一个数据表任意数量的记录相关,另外一个数据表中的一条记录也可以与本数据表中任意数量的记录相关。例如,现实中学生与教师属于多对多的关系,一名学生可以由多名教师授课,一名教师可以为多名学生授课。

Java对象关联映射关系:

数据表之间的关系实质上描述的是数据之间的关系,除了数据表,在Java中,还可以通过对象来描述数据之间的关系。通过Java对象描述数据之间的关系,其实就是使对象的属性与另一个对象的属性相互关联。

 一对一:

  • 就是在本类中定义与之关联的类的对象作为属性,例如,A类中定义B类对象作为属性,在B类中定义A类对象a作为属性。

一对多:

  • 就是一个A类对象对应多个B类对象的情况,例如,定义在A类中,定义一个类对象的集合作为A类的属性;在B类中,定义A类对象a作为B类的属性。

多对多:

  • 在两个相互关联的类中,都可以定义多个与之关联的类的对象。例如,在A类中定义B类类型的集合作为属性,在B类中定义A类类型的集合作为属性。

一对一查询 

<association>元素

在MyBatis中,通过<association>元素来处理一对一关联关系。<association>元素提供了一系列属性用于维护数据表之间的关系。

<association>元素属性

<association>元素的配置方式 

强烈建议先看一下这个association元素用法

<association>元素是<resultMap>元素的子元素,它有两种配置方式,嵌套查询方式和嵌套结果方式,下面对这两种配置方式分别进行介绍。

  1. 嵌套查询方式(//如果第一张表有外键,column="外键列名"。//如果第一张表没有外键,column="javaType对应表中的主键名"
  2. 嵌套结果方式(不用关注column的值)

 代码示例:

1.引入依赖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>org.example</groupId>
    <artifactId>_20230417</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.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>

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

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

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.26</version>
        </dependency>
    </dependencies>
</project>

2.配置数据库连接信息:(db.properties)

driver.driver=com.mysql.cj.jdbc.Driver
driver.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
driver.username=root
driver.password=666666

3.配置slf4j日志打印

log4j.appender.a=org.apache.log4j.ConsoleAppender
log4j.appender.a.Target=System.out
log4j.appender.a.layout=org.apache.log4j.PatternLayout
log4j.appender.a.layout.ConversionPattern=%-d{HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
log4j.appender.b=org.apache.log4j.FileAppender
log4j.appender.b.File=f://travel.log
log4j.appender.b.layout=org.apache.log4j.PatternLayout
log4j.appender.b.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
log4j.rootLogger=debug,a,b

3.配置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>
    <!-- 环境配置 -->
    <!-- 加载类路径下的属性文件 -->
    <properties resource="db.properties"/>

    <settings>
        <!--        打开延迟加载的开关-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--        将积极加载改为消息加载,即按需加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

    <!-- 数据库连接相关配置 ,db.properties文件中的内容-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver.driver}"/>
                <property name="url" value="${driver.url}"/>
                <property name="username" value="${driver.username}"/>
                <property name="password" value="${driver.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="cn.hdc.mapper"/>
    </mappers>
</configuration>

4.创建数据库,创建表(tb_idcard,tb_person)

 5.根据数据库中的表创建实体类IdCard

package cn.hdc.pojo;

public class IdCard {
    private int id;
    private String code;

    @Override
    public String toString() {
        return "IdCard{" +
                "id=" + id +
                ", code='" + code + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

6. 根据数据库中的表创建实体类Person

package cn.hdc.pojo;

public class Person {
    private int id;
    private String name;
    private int age;
    private String sex;
    private int card_id;
    private IdCard card;

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", card_id=" + card_id +
                ", card=" + card +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getCard_id() {
        return card_id;
    }

    public void setCard_id(int card_id) {
        this.card_id = card_id;
    }

    public IdCard getCard() {
        return card;
    }

    public void setCard(IdCard card) {
        this.card = card;
    }
}

7.创建工具类MyBatisUtils

package cn.hdc.utils;
 
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 
import java.io.IOException;
import java.io.Reader;
 
public class MybaitsUtils {
    private static SqlSessionFactory sqlSessionFactory = null;
 
    static {
        try {
            Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
 
    public static SqlSession getSession() {
        return sqlSessionFactory.openSession();
    }
}

8.创建接口IdCardMapper,声明要实现的方法

package cn.hdc.mapper;

import cn.hdc.pojo.IdCard;

import java.util.List;

public interface IdCardMapper {
    public IdCard findCodeById(Integer id);
}

9.创建接口PersonMapper,声明要实现的方法(findPersonById是嵌套查询方式,findPersonById2是嵌套结果方式)

package cn.hdc.mapper;

import cn.hdc.pojo.Person;

import java.util.List;

public interface PersonMapper {
    public Person findPersonById(Integer id);

    public Person findPersonById2(Integer id);
}

10.创建IdCardMapper.xml,编写sql语句

<?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="cn.hdc.mapper.IdCardMapper">
    <select id="findCodeById" parameterType="integer" resultType="cn.hdc.pojo.IdCard">
        select *
        from tb_idcard
        where id = #{id}
    </select>
</mapper>

11.创建PersonMapper.xml,编写sql语句

<?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="cn.hdc.mapper.PersonMapper">
    <!--    第一种方式嵌套查询方式完成一对一结果配置-->
    <select id="findPersonById" parameterType="integer" resultMap="IdCardWithPersonResult">
        select *
        from tb_person
        where id = #{id}
    </select>

    <resultMap id="IdCardWithPersonResult" type="cn.hdc.pojo.Person">
        <id property="id" column="id"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="card_id" column="card_id"></result>
        <association property="card" column="card_id" javaType="cn.hdc.pojo.IdCard"
                     select="cn.hdc.mapper.IdCardMapper.findCodeById">

        </association>
    </resultMap>

    <!--    第二种方式:嵌套结果方式完成一对一结果配置-->
    <select id="findPersonById2" parameterType="integer" resultMap="IdCardWithPersonResult2">
        select *,
               p.id pid,
               c.id cid
        from tb_person p,
             tb_idcard c
        where p.card_id = c.id
          and p.id = #{id}
    </select>
    <resultMap id="IdCardWithPersonResult2" type="cn.hdc.pojo.Person">
        <id property="id" column="pid"></id>
        <result property="name" column="name"></result>
        <result property="age" column="age"></result>
        <result property="sex" column="sex"></result>
        <result property="card_id" column="card_id"></result>
        <association property="card" javaType="cn.hdc.pojo.IdCard">
            <id property="id" column="id"></id>
            <result property="code" column="code"></result>
        </association>
    </resultMap>
</mapper>

12.编写测试类

package cn.hdc.mapper;

import cn.hdc.pojo.Person;
import cn.hdc.utils.MybaitsUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class PersonMapperTest {

    private SqlSession session;

    @Before
    public void setUp() throws Exception {
        this.session = MybaitsUtils.getSession();
    }

    @After
    public void tearDown() throws Exception {
        this.session.commit();
        this.session.close();
    }

    @Test
    public void findPersonById() {
        Person person = session.selectOne("cn.hdc.mapper.PersonMapper.findPersonById", 1);
        System.out.println(person.getName());
        System.out.println("需要使用另外一张表的数据");
        System.out.println(person.getCard());
        System.out.println(person);
    }

    @Test
    public void findPersonById2() {
        Person person = session.selectOne("cn.hdc.mapper.PersonMapper.findPersonById2", 1);
        System.out.println(person);
    }
}

13.项目结构

14.findPersonById测试结果说明:

 

 我们不使用懒加载,把settings注释掉

编写测试类以及运行结果:

明明没用到tb_idcard却执行了俩句sql,这就造成资源浪费,改进方案,配置懒加载

运行结果:

对比上边的结果:(只有用到另一张表,才会去执行另一张表的sql)

在使用MyBatis嵌套查询方式进行MyBatis关联映射查询时,使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。MyBatis默认没有开启延迟加载,需要在mybatis-config.xml中的<settings>元素内进行配置。

 15.findPersonById2测试结果说明:

虽然使用嵌套查询的方式比较简单,但是MyBatis嵌套查询的方式要执行多条SQL语句,这对于大型数据集合和列表展示不是很好,因为这样可能会导致成百上千条关联的SQL语句被执行,从而极大地消耗数据库性能并且会降低查询效率,这并不是开发人员所期望的。为此,我们可以使用MyBatis提供的嵌套结果方式进行关联查询。

 

运行结果:

一对多查询

<collection>元素

在MyBatis中,通过<collection>元素来处理一对多关联关系。<collection>元素的属性大部分与<association>元素相同,但具还包含一个特殊属性--ofType。ofType属性与javaType属性对应,它用于指定实体类对象中集合类属性所包含的元素的类型。

<collection>元素的配置方式 

<collection>元素是<resultMap>元素的子元素,<collection >元素有嵌套查询和嵌套结果两种配置方式。

a.嵌套查询方式(和上边association标签的用法基本一致)

b.嵌套结果方式

代码示例:

1.创建表

2.根据表字段,编写实体类 Orders

package cn.hdc.pojo;

public class Orders {
    private int id;
    private String number;
    private int userId;

    @Override
    public String toString() {
        return "Orders{" +
                "id=" + id +
                ", number='" + number + '\'' +
                ", userId=" + userId +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }
}

 3. 根据表字段,编写实体类User

package cn.hdc.pojo;

import java.util.List;

public class User {
    private int id;
    private String username;
    private String address;
    private List<Orders> ordersList;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", ordersList=" + ordersList +
                '}';
    }

    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 getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public List<Orders> getOrdersList() {
        return ordersList;
    }

    public void setOrdersList(List<Orders> ordersList) {
        this.ordersList = ordersList;
    }
}

4.根据实体类创建接口,声明要实现的方法OrdersMapper接口

package cn.hdc.mapper;

import cn.hdc.pojo.Orders;

import java.util.List;

public interface OrdersMapper {
    public Orders findOrdersById(Integer id);
}

5.根据实体类创建接口,声明要实现的方法UserMapper接口

package cn.hdc.mapper;

import cn.hdc.pojo.Orders;
import cn.hdc.pojo.User;

import java.util.List;

public interface UserMapper {
    public User findUserWithOrdersById(Integer id);

    public User findUserWithOrdersById2(Integer id);
}

6.使用嵌套结果方式查询,编写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" >
<mapper namespace="cn.hdc.mapper.UserMapper">
        <!--    1.嵌套结果方式  -->
    <select id="findUserWithOrdersById" parameterType="integer" resultMap="userWithOrdersResult">
        select *,
               u.id uid,
               o.id oid
        from tb_user u,
             tb_orders o
        where u.id = o.user_id
          and u.id = #{id}
    </select>
    <resultMap id="userWithOrdersResult" type="cn.hdc.pojo.User">
        <id property="id" column="uid"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <collection property="ordersList"
                    ofType="cn.hdc.pojo.Orders">
            <id property="id" column="oid"></id>
            <result property="number" column="number"></result>
            <result property="userId" column="user_id"></result>
        </collection>
    </resultMap>
</mapper>

7.编写测试类:

package cn.hdc.mapper;

import cn.hdc.pojo.User;
import cn.hdc.utils.MybaitsUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class UserMapperTest {

    private SqlSession session;

    @Before
    public void setUp() throws Exception {
        this.session = MybaitsUtils.getSession();
    }

    @After
    public void tearDown() throws Exception {
        this.session.commit();
        this.session.close();
    }

    @Test
    public void findUserWithOrdersById() {
        User user = session.selectOne("cn.hdc.mapper.UserMapper.findUserWithOrdersById", 1);
        System.out.println(user);
    }
}

8.运行结果:

9.使用嵌套查询的方式,编写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" >
<mapper namespace="cn.hdc.mapper.UserMapper">
    <!--    1.嵌套结果方式  -->
    <select id="findUserWithOrdersById" parameterType="integer" resultMap="userWithOrdersResult">
        select *,
               u.id uid,
               o.id oid
        from tb_user u,
             tb_orders o
        where u.id = o.user_id
          and u.id = #{id}
    </select>
    <resultMap id="userWithOrdersResult" type="cn.hdc.pojo.User">
        <id property="id" column="uid"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <collection property="ordersList"
                    ofType="cn.hdc.pojo.Orders">
            <id property="id" column="oid"></id>
            <result property="number" column="number"></result>
            <result property="userId" column="user_id"></result>
        </collection>
    </resultMap>
    <!--    2.嵌套查询方式  -->
    <select id="findUserWithOrdersById2" parameterType="integer" resultMap="userWithOrdersResult2">
        select *
        from tb_user
        where id = #{id}
    </select>
    <!--                    因为User实体类没有外键,所以column=‘ofType对应数据库表中的主键’-->
    <resultMap id="userWithOrdersResult2" type="cn.hdc.pojo.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="address" column="address"></result>
        <collection property="ordersList"
                    column="id"
                    ofType="cn.hdc.pojo.Orders"
                    select="cn.hdc.mapper.OrdersMapper.findOrdersById">

        </collection>
    </resultMap>
</mapper>

10.编写ordersMapper.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="cn.hdc.mapper.OrdersMapper">
    <select id="findOrdersById" resultMap="OrdersMap" parameterType="integer">
        select *
        from tb_orders
        where user_id = #{id}
    </select>
    <resultMap id="OrdersMap" type="cn.hdc.pojo.Orders">
        <id property="id" column="id"></id>
        <result property="number" column="number"></result>
        <result property="userId" column="user_id"></result>
    </resultMap>
</mapper>

11.编写测试类:

package cn.hdc.mapper;

import cn.hdc.pojo.User;
import cn.hdc.utils.MybaitsUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class UserMapperTest {

    private SqlSession session;

    @Before
    public void setUp() throws Exception {
        this.session = MybaitsUtils.getSession();
    }

    @After
    public void tearDown() throws Exception {
        this.session.commit();
        this.session.close();
    }

    @Test
    public void findUserWithOrdersById() {
        User user = session.selectOne("cn.hdc.mapper.UserMapper.findUserWithOrdersById", 1);
        System.out.println(user);
    }

    @Test
    public void findUserWithOrdersById2() {
        User user = session.selectOne("cn.hdc.mapper.UserMapper.findUserWithOrdersById2", 1);
        System.out.println(user);
    }
}

12.findUserWithOrdersById2运行结果:

多对多查询 

订单和商品多对多关系图

在实际项目开发中,多对多的关联关系非常常见。以订单和商品为例,一个订单可以包含多种商品,而一种商品又可以属于多个订单,订单和商品属于多对多关联关系,订单和商品之间的关联关系如图。 

在数据库中,多对多的关联关系通常使用一个中间表来维护,中间表中的订单id作为外键关联订单表的id,中间表中的商品id作为外键关联商品表的id。这三个表之间的关系如图。

案例:实现多对多查询 

1.引入依赖

<?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>org.example</groupId>
    <artifactId>_20230417</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.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>

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

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

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.26</version>
        </dependency>
    </dependencies>
</project>

2.创建数据库,创建表tb_product,tb_ordersitem,tb_orders

USE mybatis;
# 创建一个名称为tb_product的表
CREATE TABLE tb_product (
id INT(32) PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32),
price DOUBLE
);
# 插入3条数据
INSERT INTO tb_product VALUES ('1', 'Java基础入门', '44.5');
INSERT INTO tb_product VALUES ('2', 'Java Web程序开发入门', '38.5');
INSERT INTO tb_product VALUES ('3', 'SSM框架整合实战', '50');
# 创建一个名称为tb_ordersitem 的中间表
CREATE TABLE tb_ordersitem (
id INT(32) PRIMARY KEY AUTO_INCREMENT,
orders_id INT(32),
product_id INT(32),
FOREIGN KEY(orders_id) REFERENCES tb_orders(id),
FOREIGN KEY(product_id) REFERENCES tb_product(id)
);
# 插入3条数据
INSERT INTO tb_ordersitem VALUES ('1', '1', '1');
INSERT INTO tb_ordersitem VALUES ('2', '1', '3');
INSERT INTO tb_ordersitem VALUES ('3', '3', '3');

4.根据表字段创建对应实体Orders

package cn.hdc.pojo;

import java.util.List;

public class Orders {
    private int id;
    private String number;
    private int userId;

    private List<Product> productList;

    public List<Product> getProductList() {
        return productList;
    }

    public void setProductList(List<Product> productList) {
        this.productList = productList;
    }

    @Override
    public String toString() {
        return "Orders{" +
                "id=" + id +
                ", number='" + number + '\'' +
                ", userId=" + userId +
                ", productList=" + productList +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }
}

 5.根据表字段创建对应实体Product

package cn.hdc.pojo;

import java.util.List;

public class Product {
    private int id;
    private String name;
    private double price;
    private List<Orders> ordersList;

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", ordersList=" + ordersList +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public List<Orders> getOrdersList() {
        return ordersList;
    }

    public void setOrdersList(List<Orders> ordersList) {
        this.ordersList = ordersList;
    }
}

6.创建接口OrdersMapper,声明要实现的方法

package cn.hdc.mapper;

import cn.hdc.pojo.Orders;

import java.util.List;

public interface OrdersMapper {
    public Orders findOrdersById(Integer id);

    public Orders findOrdersById2(Integer id);

    public Orders findOrdersById3(Integer id);
}

7.创建接口ProductMapper,声明要实现的方法

package cn.hdc.mapper;

import cn.hdc.pojo.Product;

public interface ProductMapper {
    public Product findProductById(Integer id);
}

8.创建OrdersMapper.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="cn.hdc.mapper.OrdersMapper">
    <!--    嵌套查询方式-->
    <select id="findOrdersById2"
            parameterType="integer"
            resultMap="orderWithProductResults">
        select *
        from tb_orders
        where id = #{id}
    </select>
    <resultMap id="orderWithProductResults" type="cn.hdc.pojo.Orders">
        <id property="id" column="id"></id>
        <result property="number" column="number"></result>
        <collection property="productList"
                    javaType="list"
                    ofType="cn.hdc.pojo.Product"
                    column="id"
                    select="cn.hdc.mapper.ProductMapper.findProductById">

        </collection>
    </resultMap>

    <!--    嵌套结果方式-->
    <select id="findOrdersById3"
            parameterType="integer"
            resultMap="orderWithProductResults2">
        select o.*,
               p.*,
               o.id oid,
               p.id pid
        from tb_orders o,
             tb_product p,
             tb_ordersitem oi
        where
            oi.orders_id = o.id
        and
            oi.product_id = p.id
        and
            o.id=#{id}
    </select>
    <resultMap id="orderWithProductResults2" type="cn.hdc.pojo.Orders">
        <id property="id" column="oid"></id>
        <result property="number" column="number"></result>
        <collection property="productList"
                    javaType="list"
                    ofType="cn.hdc.pojo.Product">
            <id property="id" column="pid"></id>
            <result property="name" column="name"></result>
            <result property="price" column="price"></result>
        </collection>
    </resultMap>

    <select id="findOrdersById" resultMap="OrdersMap" parameterType="integer">
        select *
        from tb_orders
        where user_id = #{id}
    </select>
    <resultMap id="OrdersMap" type="cn.hdc.pojo.Orders">
        <id property="id" column="id"></id>
        <result property="number" column="number"></result>
        <result property="userId" column="user_id"></result>
    </resultMap>
</mapper>

9.创建ProguctMapper.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="cn.hdc.mapper.ProductMapper">
    <select id="findProductById"
            parameterType="integer"
            resultType="cn.hdc.pojo.Product">
        select *
        from tb_product
        where id in
              (select product_id from tb_ordersitem where orders_id = #{id});
    </select>
</mapper>

10.创建OderMapperTest测试类:

package cn.hdc.mapper;

import cn.hdc.pojo.Orders;
import cn.hdc.utils.MybaitsUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

import static org.junit.Assert.*;

public class OrdersMapperTest {

    private SqlSession session;

    @Before
    public void setUp() throws Exception {
        this.session = MybaitsUtils.getSession();
    }

    @After
    public void tearDown() throws Exception {
        this.session.commit();
        this.session.close();
    }

    @Test
    public void findOrdersById() {
        List<Orders> list = session.selectList("cn.hdc.mapper.OrdersMapper.findOrdersById", 1);
        System.out.println(list);
    }

    @Test
    public void findOrdersById2() {
        Object order = session.selectOne("cn.hdc.mapper.OrdersMapper.findOrdersById2", 1);
        System.out.println(order);
    }

    @Test
    public void findOrdersById3() {
        Object order = session.selectOne("cn.hdc.mapper.OrdersMapper.findOrdersById3", 1);
        System.out.println(order);
    }
}

11.findOrderById2运行结果(嵌套查询方式)

12.findOrderById3运行结果(嵌套结果查询方式)

 

13.项目结构:

MyBatis缓存机制 

MyBatis的一级缓存级别

MyBatis的一级缓存是SqlSession级别的缓存。如果同一个SqlSession对象多次执行完全相同的SQL语句时,在第一次执行完成后,MyBatis会将查询结果写入到一级缓存中,此后,如果程序没有执行插入、更新、删除操作,当第二次执行相同的查询语句时,MyBatis会直接读取一级缓存中的数据,而不用再去数据库查询,从而提高了数据库的查询效率。

举例说明MyBatis的一级缓存级别

 查询过程:

 

代码示例:

1.创建数据库,创建表

USE mybatis;
# 创建一个名称为tb_book的表
CREATE TABLE tb_book(
id INT PRIMARY KEY AUTO_INCREMENT,
bookName VARCHAR(255),
price double,
author VARCHAR(40)
);
# 插入3条数据
INSERT INTO tb_book(bookName,price,author) VALUES('Java基础入门',45.0,' 传智播客高教产品研发部');
INSERT INTO tb_book(bookName,price,author) VALUES('Java基础案例教程',
48.0,'黑马程序员');
INSERT INTO tb_book(bookName,price,author) VALUES('JavaWeb程序设计任务教
程',50.0,'黑马程序员');

2.根据表中字段创建实体Book

package cn.hdc.pojo;

public class Book {
    private int id;
    private String bookName;
    private double price;
    private String author;

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", bookName='" + bookName + '\'' +
                ", price=" + price +
                ", author='" + author + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}

3.创建BookMapper接口声明要实现的方法

package cn.hdc.mapper;

import cn.hdc.pojo.Book;

public interface BookMapper {
    public Book findBookById(Integer id);

    public Integer updateBook(Book book);
}

4.创建BookMapper.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="cn.hdc.mapper.BookMapper">
    <select id="findBookById" resultType="cn.hdc.pojo.Book">
        select * from tb_book where id = #{id}
    </select>
    
    <update id="updateBook" parameterType="cn.hdc.pojo.Book">
        update tb_book set bookName = #{bookName},price=#{price} where id=#{id}
    </update>
</mapper>

5.db.properties

driver.driver=com.mysql.cj.jdbc.Driver
driver.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
driver.username=root
driver.password=666666

6.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>
    <!-- 环境配置 -->
    <!-- 加载类路径下的属性文件 -->
    <properties resource="db.properties"/>

    <settings>
        <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。默认:true  -->
         <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性 . 默认:true-->
         <setting name="aggressiveLazyLoading" value="false"/>
    </settings>


    <!-- 数据库连接相关配置 ,db.properties文件中的内容-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver.driver}"/>
                <property name="url" value="${driver.url}"/>
                <property name="username" value="${driver.username}"/>
                <property name="password" value="${driver.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="cn.hdc.mapper"/>
    </mappers>
</configuration>

7.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>org.example</groupId>
    <artifactId>_20230417</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.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>

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

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

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.26</version>
        </dependency>
    </dependencies>
</project>

8.创建单元测试

package cn.hdc.mapper;

import cn.hdc.pojo.Book;
import cn.hdc.utils.MybaitsUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class BookMapperTest {

    private SqlSession session;

    @Before
    public void setUp() throws Exception {
        this.session = MybaitsUtils.getSession();
    }

    @After
    public void tearDown() throws Exception {
        this.session.commit();
        this.session.close();
    }

    @Test
    public void findBookByIdTest1() {
        System.out.println("第一次查询");
        Book book = session.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book);
        System.out.println("第二次查询");
        Book book2 = session.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book2);
    }

    @Test
    public void findBookByIdTest2() {
        System.out.println("第一次查询");
        Book book = session.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book);

        //更新表
        Book updatebook = new Book();
        updatebook.setId(2);
        updatebook.setBookName("三国演义");
        session.update("cn.hdc.mapper.BookMapper.updateBook.updateBook", updatebook);

        System.out.println("第二次查询");
        Book book2 = session.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book2);
    }
}

9.log4j.properties

#log4j.appender.a=org.apache.log4j.ConsoleAppender
#log4j.appender.a.Target=System.out
#log4j.appender.a.layout=org.apache.log4j.PatternLayout
#log4j.appender.a.layout.ConversionPattern=%-d{HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
#log4j.appender.b=org.apache.log4j.FileAppender
#log4j.appender.b.File=f://travel.log
#log4j.appender.b.layout=org.apache.log4j.PatternLayout
#log4j.appender.b.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
#log4j.rootLogger=debug,a,b

#全局日志配置
log4j.rootLogger=DEBUG,Console
#控制台输出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] -%m%n
#日志输出级别,只展示了一个
log4j.logger.java.sql.PreparedStatement=DEBUG

10.项目结构

 

11.findBookByIdTest1 运行结果说明:

很明显看到sql只执行了一次,第二次从缓存里边拿。

12. findBookByIdTest2 运行结果说明:

数据库查了2次,中间有更新语句,所以不能从缓存中读取。

MyBatis如何防止程序误读

当程序对数据库执行了插入、更新、删除操作后,MyBatis会清空一级缓存中的内容,以防止程序误读。MyBatis一级缓存被清空之后,再次使用SQL查询语句访问数据库时,
MyBatis会重新访问数据库。

二级缓存

通过学习一级缓存的内容可知,相同的Mapper类,相同的SQL语句,如果SqlSession不同,则两个SqlSession查询数据库时,会查询数据库两次,这样也会降低数据库的查询效率。为了解决这个问题,就需要用到MyBatis的二级缓存。MyBatis的二级缓存是Mapper级别的缓存,与一级缓存相比,二级缓存的范围更大,多个SqlSession可以共用二级缓存,并且二级缓存可以自定义缓存资源。

MyBatis二级缓存的执行过程

在MyBatis中,一个Mapper.xml文件通常称为一个Mapper,MyBatis以namespace区分Mapper,如果多个SqlSession对象使用同一个Mapper的相同查询语句去操作数据库,在第一个SqlSession对象执行完后,MyBatis会将查询结果写入二级缓存,此后,如果程序没有执行插入、更新、删除操作,当第二个SqlSession对象执行相同的查询语句时,MyBatis会直接读取二级缓存中的数据。

二级缓存与一级缓存的不同点 

与MyBatis的一级缓存不同的是,MyBatis的二级缓存需要手动开启,开启二级缓存通常要完成以下两个步骤。

  1. 开启二级缓存的全局配置
  2. 开启当前Mapper的namespace下的二级缓存 

默认状态的二级缓存可实现的功能 

 <cache>元素的属性

 

代码示例:

1.Book实体类必须实现Serializable 接口

package cn.hdc.pojo;

import java.io.Serializable;

public class Book implements Serializable {
    private int id;
    private String bookName;
    private double price;
    private String author;

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", bookName='" + bookName + '\'' +
                ", price=" + price +
                ", author='" + author + '\'' +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}

2.BookMapper

package cn.hdc.mapper;

import cn.hdc.pojo.Book;

public interface BookMapper {
    public Book findBookById(Integer id);

    public Integer updateBook(Book book);
}

3.BookMapper.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="cn.hdc.mapper.BookMapper">
    <cache>

    </cache>
    <select id="findBookById" resultType="cn.hdc.pojo.Book">
        select *
        from tb_book
        where id = #{id}
    </select>

    <update id="updateBook" parameterType="cn.hdc.pojo.Book">
        update tb_book
        set bookName = #{bookName},
            price=#{price}
        where id = #{id}
    </update>
</mapper>

4.mybatis-config中通过settings标签开启二级缓存

<?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>
    <!-- 环境配置 -->
    <!-- 加载类路径下的属性文件 -->
    <properties resource="db.properties"/>

    <settings>
        <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。默认:true  -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性 . 默认:true-->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!--        开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>


    <!-- 数据库连接相关配置 ,db.properties文件中的内容-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver.driver}"/>
                <property name="url" value="${driver.url}"/>
                <property name="username" value="${driver.username}"/>
                <property name="password" value="${driver.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="cn.hdc.mapper"/>
    </mappers>
</configuration>

5.log4j.properties

#log4j.appender.a=org.apache.log4j.ConsoleAppender
#log4j.appender.a.Target=System.out
#log4j.appender.a.layout=org.apache.log4j.PatternLayout
#log4j.appender.a.layout.ConversionPattern=%-d{HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
#log4j.appender.b=org.apache.log4j.FileAppender
#log4j.appender.b.File=f://travel.log
#log4j.appender.b.layout=org.apache.log4j.PatternLayout
#log4j.appender.b.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
#log4j.rootLogger=debug,a,b

#全局日志配置
log4j.rootLogger=DEBUG,Console
#控制台输出配置
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] -%m%n
#日志输出级别,只展示了一个
log4j.logger.java.sql.PreparedStatement=DEBUG

6.生成测试类

package cn.hdc.mapper;

import cn.hdc.pojo.Book;
import cn.hdc.utils.MybaitsUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class BookMapperTest {

    private SqlSession session;

    @Before
    public void setUp() throws Exception {
        this.session = MybaitsUtils.getSession();
    }

    @After
    public void tearDown() throws Exception {
        this.session.commit();
        this.session.close();
    }

    @Test
    public void findBookByIdTest1() {
        System.out.println("第一次查询");
        Book book = session.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book);
        System.out.println("第二次查询");
        Book book2 = session.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book2);
    }

    @Test
    public void findBookByIdTest2() {
        System.out.println("第一次查询");
        Book book = session.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book);

        //更新表
        Book updatebook = new Book();
        updatebook.setId(2);
        updatebook.setBookName("三国演义");
        updatebook.setPrice(30);
        int ret = session.update("cn.hdc.mapper.BookMapper.updateBook", updatebook);
        if (ret > 0) {
            System.out.println("更新成功!");
        } else {
            System.out.println("更新失败!");
        }

        System.out.println("第二次查询");
        Book book2 = session.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book2);
    }

    @Test
    public void findBookByIdTest3() {
        SqlSession session1 = MybaitsUtils.getSession();
        SqlSession session2 = MybaitsUtils.getSession();

        System.out.println("第一次查询");
        Book book = session1.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book);
        session1.close();

        System.out.println("第二次查询");
        Book book2 = session2.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book2);
        session2.close();
    }

    @Test
    public void findBookByIdTest4() {
        SqlSession session1 = MybaitsUtils.getSession();
        SqlSession session2 = MybaitsUtils.getSession();
        SqlSession session3 = MybaitsUtils.getSession();

        System.out.println("第一次查询");
        Book book = session1.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book);
        session1.close();

        Book updatebook = new Book();
        updatebook.setId(2);
        updatebook.setBookName("三国演义");
        updatebook.setPrice(30);
        session3.update("cn.hdc.mapper.BookMapper.updateBook", updatebook);
        session3.commit();
        session3.close();

        System.out.println("第二次查询");
        Book book2 = session2.selectOne("cn.hdc.mapper.BookMapper.findBookById", 1);
        System.out.println(book2);
        session2.close();
    }
}

7.findBookByIdTest3运行结果:

sql只执行了一次,第二次查询是从二级缓存中拿的数据。 

8.findBookByIdTest4运行结果:

由于中间有更新语句,二级缓存失效。 

缓存命中率

终端用户访问缓存时,如果在缓存中查找到了要被访问的数据,就叫做命中。如果缓存中没有查找到要被访问的数据,就是没有命中。当多次执行查询操作时,缓存命中次数与总的查询次数(缓存命中次数+缓存没有命中次数)的比,就叫作缓存命中率,即缓存命中率=缓存命中次数/总的查询次数。当MyBatis开启二级缓存后,第一次查询数据时,由于数据还没有进入缓存,所以需要在数据库中查询而不是在缓存中查询,此时,缓存命中率为0。第一次查询过后,MyBatis会将查询到的数据写入缓存中当第二次再查询相同的数据时,MyBatis会直接从缓存中获取这条数据,缓存将命中,此时的缓存命中率为0.5 ( 1/2)。当第三次查询相同的数据,则缓存命中率为0.66666 ( 2/3 ),以此类推。

 

案例,商品的类别

1.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>org.example</groupId>
    <artifactId>_20230419_1</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.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>

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

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

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.26</version>
        </dependency>
    </dependencies>
</project>

2.db.properties

driver.driver=com.mysql.cj.jdbc.Driver
driver.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
driver.username=root
driver.password=666666

3.mybaits-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>
    <!-- 环境配置 -->
    <!-- 加载类路径下的属性文件 -->
    <properties resource="db.properties"/>

    <settings>
        <!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。默认:true  -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性 . 默认:true-->
        <setting name="aggressiveLazyLoading" value="false"/>
        <!--        开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>


    <!-- 数据库连接相关配置 ,db.properties文件中的内容-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver.driver}"/>
                <property name="url" value="${driver.url}"/>
                <property name="username" value="${driver.username}"/>
                <property name="password" value="${driver.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="cn.hdc.mapper"/>
    </mappers>
</configuration>

4.MybaitsUtils工具类

package cn.hdc.utils;
 
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 
import java.io.IOException;
import java.io.Reader;
 
public class MybaitsUtils {
    private static SqlSessionFactory sqlSessionFactory = null;
 
    static {
        try {
            Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
 
    public static SqlSession getSession() {
        return sqlSessionFactory.openSession();
    }
}

5.建库建表

 

USE mybatis;
# 创建一个名称为category的表
CREATE TABLE category (
id int(32) PRIMARY KEY AUTO_INCREMENT,
typename varchar(40)
);
# 插入2条数据
INSERT INTO category VALUES (1, '黑色家电');
INSERT INTO category VALUES (2, '白色家电');
# 创建一个名称为product的表
CREATE TABLE product (
id int(32) PRIMARY KEY AUTO_INCREMENT,
goodsname varchar(40),
price DOUBLE,
category_id int(32) NOT NULL,
FOREIGN KEY(category_id) REFERENCES category(id)
);
# 插入4条数据
INSERT INTO product VALUES (1, '电视机', 5000,1);
INSERT INTO product VALUES (2, '冰箱', 4000,2);
INSERT INTO product VALUES (3, '空调', 5000,2);
INSERT INTO product VALUES (4, '洗衣机', 2000,2);

 

6.根据表字段创建实体Product

package cn.hdc.pojo;

public class Product {
    private int id;
    private String goodsname;
    private double price;
    private int typeid;

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", goodsname='" + goodsname + '\'' +
                ", price=" + price +
                ", typeid=" + typeid +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getGoodsname() {
        return goodsname;
    }

    public void setGoodsname(String goodsname) {
        this.goodsname = goodsname;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public int getTypeid() {
        return typeid;
    }

    public void setTypeid(int typeid) {
        this.typeid = typeid;
    }
}

7.根据表字段创建实体Category

package cn.hdc.pojo;

import java.util.List;

public class Category {
    private int id;
    private String typename;
    private List<Product> productList;

    @Override
    public String toString() {
        return "Category{" +
                "id=" + id +
                ", typename='" + typename + '\'' +
                ", productList=" + productList +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTypename() {
        return typename;
    }

    public void setTypename(String typename) {
        this.typename = typename;
    }

    public List<Product> getProductList() {
        return productList;
    }

    public void setProductList(List<Product> productList) {
        this.productList = productList;
    }
}

8.创建CategoryMapper接口,声明要实现的方法

package cn.hdc.mapper;

import cn.hdc.pojo.Category;

public interface CategoryMapper {
    public Category findCategoryWithProduct(Integer id);
}

9.创建CategoryMapper.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="cn.hdc.mapper.CategoryMapper">
    <select id="findCategoryWithProduct" resultMap="categoryWithProduct">
        select p.id pid,
               p.goodsname,
               p.price,
               c.id cid,
               c.typename
        from category c,
             product p
        where c.id = p.category_id
          and c.id = #{id}
    </select>
    <resultMap id="categoryWithProduct" type="cn.hdc.pojo.Category">
        <id property="id" column="cid"></id>
        <result property="typename" column="typename"></result>
        <collection property="productList"
                    javaType="list"
                    ofType="cn.hdc.pojo.Product">
            <id property="id" column="pid"></id>
            <result property="goodsname" column="goodsname"></result>
            <result property="price" column="price"></result>
        </collection>
    </resultMap>
</mapper>

9.创建测试类

package cn.hdc.mapper;

import cn.hdc.pojo.Category;
import cn.hdc.utils.MybaitsUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.*;

public class CategoryMapperTest {
    private SqlSession session;

    @Before
    public void setUp() throws Exception {
        this.session = MybaitsUtils.getSession();
    }

    @After
    public void tearDown() throws Exception {
        this.session.commit();
        this.session.close();
    }

    @Test
    public void findCategoryWithProduct() {
        Category category = session.selectOne("cn.hdc.mapper.CategoryMapper.findCategoryWithProduct", 2);
        System.out.println(category);
    }
}

10.运行结果:

11.项目结构:

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/436782.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CompletableFuture异步编排

CompletableFuture异步编排 1、CompletableFuture异步编排1.1 为什么需要异步编排1.2 CompletableFuture介绍1.3 创建异步对象1.4 线程串行化与并行化方法1.5 多任务组合1.6 优化商品详情页(业务代码)1.6.1 未优化之前的代码1.6.2 使用CompletableFuture异步编排1.6.3 测试功能…

Linux 下 REST 客户端的新选择:Insomnia 3.0

正在为 Linux 桌面端找一个免费的 REST 客户端&#xff1f; 别睡不着觉了&#xff01;试试 Insomnia。 这个应用是跨平台的&#xff0c;可以工作在 Linux、macOS、Windows。开发者 Gregory Schier 告诉我们他创造这个应用是为了“帮助开发者处理和 REST API 的通信”。他还说&a…

如何在Java中创建临时文件?

在Java程序中&#xff0c;有时需要创建临时文件来暂存数据或者执行某些操作。Java提供了许多方式来创建临时文件。在本教程中&#xff0c;我们将介绍如何使用Java标准库来创建临时文件。 一、使用File.createTempFile()方法 Java标准库中的File类提供了createTempFile()方法来…

设计模式--单例模式

介绍 所谓类的单例模式 就是采取一定的方法保证在整个软件系统中对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法) 比如 Hibemate的SessionFactory 它充当数据存储源的代理 并负责创建Session对象 SessionFactory并不是轻量级的 一般情况下 一个…

Java中的Map(三种双列集合万字详解)

点击可查看单列集合Set万字详解&#xff1a;其中还包含哈希解读和底层分析。 文章目录 前言一、Map1.Map集合常用的API代码演示&#xff1a;1.Map集合的基本功能2.Map集合的获取功能3.Map的getOrDefault()方法 2.Map集合的三种遍历1.键找值、值找键2.键值对3.Lambda表达式 二、…

【C++11】晦涩难懂语法系列:可变参数模板

目录 可变参数模板 1.1 概念 1.2 可变参数模板定义 1.3 参数包的展开方式 1.3.1 递归展开参数包 1.3.2 逗号表达式展开参数包 1.4 STL的emplace系列函数 可变参数模板 1.1 概念 在C语言阶段&#xff0c;我们已经接触过可变参数&#xff0c;比如scand、printf等等 这里…

9.2 回归分析

学习目标&#xff1a; 回归分析是一种广泛应用于数据分析和预测的统计方法&#xff0c;可以用来探索自变量与因变量之间的关系并进行预测。我学习回归分析&#xff0c;我会采取以下步骤&#xff1a; 学习基本概念&#xff1a;回归分析中的基本概念包括自变量和因变量、回归系数…

运放专题:运放输入端交直流混合信号隔直放大

运放输入不隔直放大 运放输入端不隔直&#xff0c;那么经过运放放大后&#xff0c;交流成分放大了&#xff0c;直流成分也被放大了。看下面的仿真&#xff1a; 交流信号为&#xff1a;振幅3V, 频率5K的正弦波&#xff0c;直流偏置为1V 可以看到&#xff0c;交流信号被放大的…

【Linux】匿名管道代码实现-mypipe

文章目录 管道介绍什么是管道&#xff1a;管道的原理管道的特点 具体代码详写创建初始文件makefile编写定义任务列表-task.hpp分阶段代码编写总代码展示: ctrlProcess.cc 编写头文件包含(如有不会,自己查谷歌)定义全局变量以解耦main,函数框架EndPoint定义creatProcess 创建管道…

Apollo配置中心使用篇

Apollo配置中心使用篇 常见配置中心对比Apollo核心概念Apollo核心特性Apollo架构设计各模块介绍服务端设计客户端设计Apollo与Spring集成的底层原理 Apollo安装安装apollo-portalconfig service和admin service部署多网卡问题解决修改Portal环境配置调整ApolloPortal配置 Apoll…

【产品设计】用户操作日志

日志记录了代码的执行过程&#xff0c;根据目的不同&#xff0c;可以分为系统日志和操作日志。 一、什么是日志 日志记录了代码的执行过程。根据目的不同&#xff0c;可分为系统日志和操作日志。 1&#xff09;系统日志 记录系统中硬件、软件和系统问题的信息&#xff0c;同…

C#基础学习--枚举器和迭代器

目录 枚举器和可枚举类型 IEnumerator 接口 IEnumerable 接口 实现 IEnumerable 和 IEnumerator的示例 泛型枚举接口 迭代器 迭代器块 使用迭代器来创建枚举器 使用迭代器来创建可枚举类型 常见迭代器模式 产生多个可枚举类型 将迭代器作为属性 迭代器实质 枚举器和可…

【分享】比ChatGPT还厉害?可以自主解决复杂任务的Auto-GPT迅速走红(内含体验地址)

哈喽&#xff0c;大家好&#xff0c;我是木易巷~ 最近木易巷在了解Auto GPT&#xff0c;今天给大家分享一下~ 自主解决复杂任务的Auto-GPT 什么是Auto-GPT&#xff1f; Auto-GPT 是一款开源 Python 应用程序&#xff0c;由开发者用户 Significant Gravitas 于 2023 年 3 月 30…

钉钉接入“通义千问”大模型,输入斜杠“/”唤起智能服务

4月18日&#xff0c;钉钉总裁叶军在2023春季钉峰会上宣布&#xff0c;钉钉正式接入阿里巴巴“通义千问”大模型&#xff0c;输入“&#xff0f;”在钉钉即可唤起 10 余项 AI 能力&#xff0c;叶军现场演示了群聊、文档、视频会议及应用开发四个场景。 现场展示中&#xff0c;只…

C++:智能指针(auto_ptr/unique_ptr/shared_ptr/weak_ptr)

为什么需要智能指针&#xff1f; C没有垃圾回收机制。 #include<iostream> using namespace std;int div() {int a, b;cin >> a >> b;if (b 0)throw invalid_argument("除0错误");return a / b; }void Func() {// 1、如果p1这里new 抛异常会如何…

网络原理数据链路层

嘿嘿,又见面了,今天为大家带来数据链路层的相关知识.这个层面的知识离咱们程序员太遥远了,我们简单介绍一下就行 1.以太网 2.认识Mac地址 3.区分Mac地址和IP地址 4.MTU 5.DNS 1.以太网 以太网是数据链路层和物理层的使用的网络,物理层用的不咋多,我们就先不讲了,直接看数…

论文阅读 - Segment Anything

文章目录 0 前言1 预备知识1.1 深度学习训练框架1.2 语义分割训练框架 2 SAM的任务3 SAM的模型3.1 模型整体结构3.2 Image encoder3.3 Prompt encoder3.4 Mask decoder3.5 训练细节 4 SAM的数据4.1 模型辅助的手动标注阶段4.2 半自动阶段4.3 全自动阶段 5 SAM的应用5.1 拿来主义…

什么是感知机——图文并茂,由浅入深

什么是感知机——图文并茂&#xff0c;由浅入深 文章目录 什么是感知机——图文并茂&#xff0c;由浅入深引言感知机的引入宝宝版青年版老夫聊发少年狂版激活函数 感知机的应用与门或门 感知机与深度学习感知机与神经网络感知机和深度学习什么关系呢&#xff1f; 引言 生活中常…

【4月比赛合集】19场可报名的「创新应用」和「程序设计」大奖赛,任君挑选!

CompHub 实时聚合多平台的数据类(Kaggle、天池…)和OJ类(Leetcode、牛客…&#xff09;比赛。本账号同时会推送最新的比赛消息&#xff0c;欢迎关注&#xff01; 更多比赛信息见 CompHub主页 或 点击文末阅读原文 以下信息仅供参考&#xff0c;以比赛官网为准 目录 创新应用赛&…

【SpringBoot】一:SpringBoot的基础(上)

文章目录 1. 脚手架创建项目1.1使用Spring Initializr1.2 IDEA中使用脚手架创建项目 2. 代码结构2.1 单一结构2.2 多模块2.3 包和主类2.4 pom文件2.4.1 父项目2.4.2 启动器2.4.3 不使用父项目 3. 运行SpringBoot项目 1. 脚手架创建项目 脚手架辅助创建程序的工具&#xff0c;S…