04ssm_Mybaits关联映射和缓存机制

04ssm_Mybaits关联映射和缓存机制

文章目录

一、关联映射概述

在实际开发中,对数据库的操作常常会涉及到多张表,针对多表之间的操作,MyBatis提供了关联映射,通过关联映射可以很好地处理表与表、对象与对象之间的关联关系。

1.数据表的三种关联映射关系

三种映射关系分别为:一对一关系、一对多关系、多对多关系

2.Java描述数据表的关联映射关系

具体描述方法如下图所示。

在这里插入图片描述

3.本单元实验环境

  • 第一步,在ssm_mybatis项目下建mybatis-chap04-relation&cache模块,目录结构如下:

在这里插入图片描述

  • 第二步,导入mybatis、mysql、junit包
    在这里插入图片描述

  • 第三步,创建数据库mybatis

    • 建表tb_idcard。建表语句如下:
USE mybatis;
# 创建一个名称为tb_idcard的表
CREATE TABLE  tb_idcard(
    id INT PRIMARY KEY AUTO_INCREMENT,
    CODE VARCHAR(18)
);
# 插入2条数据
INSERT INTO tb_idcard(CODE) VALUES('152221198711020624');
INSERT INTO tb_idcard(CODE) VALUES('152201199008150317');
- 建表tb_person。建表语句如下:
USE mybatis;
# 创建一个名称为tb_person的表
CREATE TABLE  tb_person(
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(32),
    age INT,
    sex VARCHAR(8),
    card_id INT UNIQUE,
    FOREIGN KEY(card_id) REFERENCES tb_idcard(id)
);
# 插入2条数据
INSERT INTO tb_person(name,age,sex,card_id) VALUES('Rose',22,'女',1);
INSERT INTO tb_person(name,age,sex,card_id) VALUES('jack',23,'男',2);
- 建表tb_user。建表语句如下:
USE mybatis;
# 创建一个名称为tb_user的表
CREATE TABLE tb_user (
    id int(32) PRIMARY KEY AUTO_INCREMENT,
    username varchar(32),
    address varchar(256)
);
# 插入3条数据
INSERT INTO tb_user VALUES ('1', '小明', '北京');
INSERT INTO tb_user VALUES ('2', '李华', '上海');
INSERT INTO tb_user VALUES ('3', '李刚', '上海');
- 建表tb_orders。建表语句如下:
USE mybatis;
# 创建一个名称为tb_orders的表
CREATE TABLE tb_orders (
    id int(32) PRIMARY KEY AUTO_INCREMENT,
    number varchar(32) NOT NULL,
    user_id int(32) NOT NULL,
    FOREIGN KEY(user_id) REFERENCES tb_user(id)
);
# 插入3条数据
INSERT INTO tb_orders VALUES ('1', '1000011', '1');
INSERT INTO tb_orders VALUES ('2', '1000012', '1');
INSERT INTO tb_orders VALUES ('3', '1000013', '2');
- 建表tb_product。建表语句如下:
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。建表语句如下:
USE mybatis;
# 创建一个名称为tb_ordersitem 的中间表
CREATE TABLE tb_ordersitem (
    id INT(32) PRIMARY KEY AUTO_INCREMENT,
    orders_id INT(32),
    productb_id INT(32),
    FOREIGN KEY(orders_id) REFERENCES tb_orders(id),
    FOREIGN KEY(productb_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');
- 建表tb_book。建表语句如下:
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,'黑马程序员');
  • 第四步,创建POJO类**(后续根据不同需求在pojo包中创建)**
  • 第五步,创建各POJO类的mapper接口**(后续根据不同需求在mapper包中创建)**
  • 第六步,为上述接口创建映射文件**(后续根据不同需求在resources\mapper文件夹中创建)**
  • 第七步,创建Mybatis核心配置文件mybatis-config.xml(放resources文件夹)
    • 创建db.properties,配置数据库连接各属性(放resources文件夹)
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&character=utf-8
mysql.username=root
mysql.password=123456
- 创建mybatis-config.xml(**放resources文件夹**),其中的mapper及扫描别名配置根据需要后续再配置
<?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元素-->
    <properties resource="db.properties"/>
  <!--采用包扫描方式定义别名。其内部可以配置多个package元素-->
    <typeAliases>
        <package name="com.cshbxy.pojo"/>
    </typeAliases>
  <!--配置数据源。可以多套environment,由default属性仁决定使用哪套!-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${mysql.driver}"/>
                <property name="url" value="${mysql.url}"/>
                <property name="username" value="${mysql.username}"/>
                <property name="password" value="${mysql.password}"/>
            </dataSource>
        </environment>
    </environments>
  <!--引入映射文件。其内部可以配置多个mapper元素-->
    <mappers>
        <!--后续根据实际需要通过mapper元素引用-->
    </mappers>
</configuration>
  • 第八步,创建工具类MyTool,用以获取会话对象
    • 创建com.cshbxy.util包
    • 创建工具类MyTool
package com.cshbxy.util;

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.InputStream;

public class MyTool {
    private static SqlSessionFactory factory;
    static {
        try {
            //创建会话工厂对象factory
            InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
            factory = new SqlSessionFactoryBuilder().build(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlSession(){
        return factory.openSession(true);
    }
}
  • 第九步,编写测试类(放test\java文件夹下,不同的mapper建一个测试类),此处给模板,后续自己按模板添加单元测试
import com.cshbxy.mapper.PersonMapper;
import com.cshbxy.pojo.Person;
import com.cshbxy.util.MyTool;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

public class TestPersonMapper {
    @Test
    public void test01() throws IOException {
        //第一大步,创建会话对象sqlSession
        SqlSession sqlSession = MyTool.getSqlSession();

        //第二大步,通过会话对象的getMapper()方法实现接口CustomerMapper,并获取该对象
       PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);

        //第三大步,完成测试(此处自己针对实现的方法进行测试)
        

        //第四大步,关闭会话对象。因为之前创建事务时带true参数,所以此处不必考虑事务提交问题
        sqlSession.close();
    }
}

二、一对一查询(重点掌握)

1.需求描述

按id查询tb_person数据表中每个人的身份证信息。相关表tb_person、tb_idcard数据如下:

idnameagesexcard_id
1Rose221
2jack232
idcode
1152221198711020624
2152201199008150317

2.任务分析

  • tb_idcard的主键id为身份证的id号,code为其身份证编码;
  • tb_person的主键id,外键card_id为其身份证的id号;
  • tb_person的外键card_id与tb_idcard的主键id关联;
  • 每个人只有一个身份证编码,一个身份证编码也只能属于一个人。即:构成了一对一的关联关系。

3.知识准备:实现一对一映射关系的两种方式

  • 要点: 怎么设计实体类,表现一对一关系
  • 最核心要解决的问题: 是类类型属性与查询结果字段的映射,这种映射关系通过元素的子元素完成。同时,还应将查询元素的resultMap属性赋值为配置的映射关系的id值。
  • 有两种解决方式: 嵌套查询方式、嵌套结果方式
(1)元素

具有如下属性。

****属性****说明
****property用于指定****映射到的实体类对象的属性,与表字段一一对应
****column用于****指定表中对应的字段
****javaType用于指定****映射到实体对象的属性的类型
jdbcType用于指定数据表中对应字段的类型
fetchType用于指定在关联查询时是否启用延迟加载。fetchType属性有lazy和eager两个属性值,默认值为lazy
****select用于****指定引入嵌套查询的子SQL语句
autoMapping用于指定是否自动映射
typeHandler用于指定一个类型处理器
(2)嵌套查询方式

通过执行另外一条SQL映射语句来返回预期的类类型(实体类类型)-- 对应通过子查询完成关联关系的SQL语句。

    <!--resulMap各属性及子元素说明
        id属性 指结果映射id号,与上面select元素的resultMap属性值对应
        type属性 指java中定义的实体类名或该类的别名
        id元素及result元素 配置type指定实体类的各属性与查询结果各字段的映射关系,其中id元素用以主键的映射。
  -->
    <resultMap id="person" type="person">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <!--association子元素属性说明
        property属性 需要映射的复杂类型属性名
        column属性 可理解为:指定select属性子查询接收参数的来源
        javaType属性 property指定属性的类型
        select属性 要执行的SQL映射id
        -->
        <association property="card" column="card_id" javaType="IdCard"
                     select="com.cshbxy.mapper.IdCardMapper.selectById"/>
    </resultMap>
(3)嵌套结果方式(重点掌握)

使用嵌套结果映射来处理重复的联合结果的子集 – 对应多表联合查询SQL语句。

    <resultMap id="person" type="person">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <association property="card" javaType="IdCard">
            <id property="id" column="card_id"/>
            <result property="code" column="code"/>
        </association>
    </resultMap>

4.实验步骤

第一步,创建POJO类(注意:在com.cshbxy.pojo包内创建)
  • 创建IdCard类,用以描述 tb_idcard数据表
package com.cshbxy.pojo;

public class IdCard {
    private int id;
    private String 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;
    }

    public IdCard(int id, String code) {
        this.id = id;
        this.code = code;
    }

    @Override
    public String toString() {
        return "IdCard{" +
                "id=" + id +
                ", code='" + code + '\'' +
                '}';
    }
}
  • 创建Person类,用以描述数据表tb_person与tb_idcard数据表之间的一对一关系。
    请注意:设计一对一关联类时,将外键字段card_id换成了关联表的POJO类对象
package com.cshbxy.pojo;
//设计一对一关联类时,将外键字段换成了关联表的POJO类对象
public class Person {
    private int id;
    private String name;
    private int age;
    private String sex;
    private IdCard card; //通过card字段将tb_person表关联到tb_idcard表

    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 IdCard getCard() {
        return card;
    }

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

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

注:实际项目中,该类可能会放在vo包或bo包中。

第二步,创建接口PersonMapper(注意:在com.cshbxy.mapper包中创建)
package com.cshbxy.mapper;

import com.cshbxy.pojo.Person;

public interface PersonMapper {
    /*按id查询tb_person数据表中每个人的身份证信息*/
    Person findPersonById(int id);
}
第三步,编写映射文件(注意:在resources\mapper文件夹中创建)
  • 关键:如何完成实体类Person的card属性与查询结果间的映射!
  • 有两种方式
    • 嵌套查询方式
      • 首先,在IdCardMapper.xml映射文件中创建被关联表tb_idcard的按主键id查询的SQL映射语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cshbxy.mapper.IdCardMapper">
    <!--嵌套查询方式-->
    <!--第一步,创建关联表按主键查询的SQL映射语句。-->
    <!--按id查询身份证信息-->
    <select id="selectById" resultType="IdCard">
        select * from mybatis.tb_idcard
        where id=#{id}
    </select>
</mapper>
    - 之后,在**PersonMapper.xml映射文件**中完成按id进行的一对一关联查询,同时要通过<resultMap>元素及其子元素<association>对结果进行映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cshbxy.mapper.PersonMapper">
    <!--嵌套查询方式-->
    <!--第二步,按id进行一对一关联查询,同时要通过resultMa元素及其子元素association对结果进行映射-->
    <!--按id查询tb_person数据表中每个人的身份证信息。对应前述接口中的findPersonById()方法。注:要使用resultMap属性,而不是resultType属性-->
    <select id="findPersonById" resultMap="person">
        select * from mybatis.tb_person
        where id=#{id}
    </select>
    <!--resulMap各属性及子元素说明
        id属性 指结果映射id号,与上面select元素的resultMap属性值对应
        type属性 指java中定义的实体类名或该类的别名
        id元素及result元素 配置type指定实体类的各属性与查询结果各字段的映射关系,其中id元素用以主键的映射。
  -->
    <resultMap id="person" type="person">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <!--association子元素属性说明
        property属性 需要映射的复杂类型属性名
        column属性 可理解为:指定select属性子查询接收参数的来源
        javaType属性 property指定属性的类型
        select属性 要执行的SQL映射id
        -->
        <association property="card" column="card_id" javaType="IdCard"
                     select="com.cshbxy.mapper.IdCardMapper.selectById"/>
    </resultMap>
</mapper>
- 嵌套结果方式**(重点掌握)**
    - 在**PersonMapper.xml映射文件**中完成按id进行的一对一关联查询,同时要通过<resultMap>元素及其子元素<association>对结果进行映射。该查询语句要用**多表联合查询的书写方式**
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cshbxy.mapper.PersonMapper">
    <!--嵌套结果方式:嵌套结果映射处理重复的联合结果的子集-->
    <select id="findPersonById" resultMap="person">
        select a.*,b.code from mybatis.tb_person a
        join mybatis.tb_idcard b
        on a.card_id=b.id
        where a.id=#{id}
    </select>
    <resultMap id="person" type="person">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <association property="card" javaType="IdCard">
            <id property="id" column="card_id"/>
            <result property="code" column="code"/>
        </association>
    </resultMap>
</mapper>
第四步,核心配置文件中引入第三步中创建的映射文件。
  • 如果是嵌套查询方式,要在元素中引入IdCardMapper.xml、PersonMapper.xml映射文件
<mapper resource="mapper/IdCardMapper.xml"/>
<mapper resource="mapper/PersonMapper.xml"/>
  • 如果是嵌套结果方式,只要在元素中引入PersonMapper.xml映射文件
<mapper resource="mapper/PersonMapper.xml"/>
第五步,完成测试。(按前述提示自行完成!)

测试:查询id值为2的身份信息,结果如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s7SViRIP-1653758110282)(https://secure2.wostatic.cn/static/oNFZZDx5WFkFEgNJKML7di/9df5b9_xo1.png)]

三、一对多查询(重点掌握)

1.需求描述

**按id查询tb_user数据表中每个人的订单信息。**相关表tb_user、tb_orders数据如下:

idusernameaddress
1小明北京
2李华上海
3李刚上海
idnumberuser_id
110000111
210000121
310000132

2.任务分析

  • tb_user的主键id为用户的id号;
  • tb_orders的主键为id为订单的id号,外键user_id为该订单所属用户的id;
  • tb_user主键id与tb_orders的外键user_id关联
  • 每位客户均可以有多条订单信息,但一个订单只能属于一个客户。即:构成了一对多的关联关系。

3.知识准备:实现一对多映射关系的两种方式

  • 要点: 怎么设计实体类,表现一对多关系
  • 最核心要解决的问题: 是集合类型属性与查询结果字段的映射,这种映射关系通过元素的子元素完成。同时,还应将查询元素的resultMap属性赋值为配置的映射关系的id值。
  • 有两种解决方式: 嵌套查询方式、嵌套结果方式
(1)元素
  • 大部分属性与前述元素相同
  • ofType属性:指定实体类对象中集合类属性所包含的元素的类型
(2)嵌套查询方式

通过执行另外一条SQL映射语句来返回预期的类类型(实体类类型)-- 对应通过子查询完成关联关系的SQL语句。

    <resultMap id="user" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="address" column="address"/>
        <!--ofType属性:指实体类User的集合属性对象中元素的类型。column属性值:子查询参数来源于User表中的哪个字段-->
        <collection property="ordersList" column="id" ofType="Orders"
                    select="com.cshbxy.mapper.OrdersMapper.selectByUserId"/>
    </resultMap>
(3)嵌套结果方式(重点掌握)

使用嵌套结果映射来处理重复的联合结果的子集 – 对应多表联合查询完成关联关系的SQL语句。

    <resultMap id="user" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="address" column="address"/>
        <!--ofType属性:指实体类User的集合属性对象中元素的类型-->
        <collection property="ordersList" ofType="Orders">
            <id property="id" column="orderId"/>
            <result property="number" column="number"/>
            <result property="user_id" column="user_id"/>
        </collection>
    </resultMap>

4.实验步骤

第一步,创建POJO类(注意:在com.cshbxy.pojo包内创建)
  • 创建Orders类,用以描述 tb_orders数据表
package com.cshbxy.pojo;

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

    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 getUser_id() {
        return user_id;
    }

    public void setUser_id(int user_id) {
        this.user_id = user_id;
    }

    @Override
    public String toString() {
        return "\n\tOrders{" +
                "id=" + id +
                ", number='" + number + '\'' +
                ", user_id=" + user_id +
                '}';
    }
}
  • 创建User类,用以描述数据表tb_user与tb_orders数据表之间的一对多关系。
    请注意:设计一对多关联类时,在主键表对应的实例类中增加一个元素类型为Orders的集合对象以描述一对多关系
package com.cshbxy.pojo;

import java.util.List;

public class User {
    private int id;
    private String username;
    private String address;
    private List<Orders> 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;
    }

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

注:实际项目中,该类可能会放在vo包或bo包中。

第二步,创建接口UserMapper(注意:在com.cshbxy.mapper包中创建)
package com.cshbxy.mapper;

import com.cshbxy.pojo.User;

import java.util.List;

public interface UserMapper {
    /*按id查询tb_user数据表中每个人的订单信息*/
    User findUserOrdersById(int id);
}
第三步,编写映射文件(注意:在resources\mapper文件夹中创建)
  • 关键:如何完成实体类User的ordersList属性与查询结果间的映射!
  • 有两种方式
    • 嵌套查询方式
      • 首先,在OrdersMapper.xml映射文件中创建被关联表tb_Orders的按user_id查询的SQL映射语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cshbxy.mapper.OrdersMapper">
    <!--按user_id查询订单信息-->
    <select id="selectByUserId" resultType="Orders">
        select * from mybatis.tb_orders
        where user_id=#{user_id}
    </select>
</mapper>
    - 之后,在**UserMapper.xml映射文件**中完成按id进行的一对多关联查询,同时要通过<resultMap>元素及其子元素<collection>对结果进行映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cshbxy.mapper.UserMapper">
    <!--按id查询tb_user数据表中每个人的订单信息-->
    <!--嵌套查询方式-->
    <select id="findUserOrdersById" resultMap="user">
        select * from mybatis.tb_user
        where id=#{id}
    </select>
    <resultMap id="user" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="address" column="address"/>
        <!--ofType属性:指实体类User的集合属性对象中元素的类型。column属性值:子查询参数来源于User表中的哪个字段-->
        <collection property="ordersList" column="id" ofType="Orders"
                    select="com.cshbxy.mapper.OrdersMapper.selectByUserId"/>
    </resultMap>
</mapper>
- 嵌套结果方式**(重点掌握)**
    - 在**UserMapper.xml映射文件**中完成按id进行的一对多关联查询,同时要通过<resultMap>元素及其子元素<collection>对结果进行映射。该查询语句要用**多表联合查询的书写方式**
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cshbxy.mapper.UserMapper">
    <!--按id查询tb_user数据表中每个人的订单信息-->
    <!--嵌套结果方式。如果查询结果字段有同名时,要取别名以视区别!-->
    <select id="findUserOrdersById" resultMap="user">
        select a.*,b.id orderId,b.number,b.user_id from mybatis.tb_user a
        join mybatis.tb_orders b
        on a.id=b.user_id
        where a.id=#{id}
    </select>
    <resultMap id="user" type="user">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="address" column="address"/>
        <!--ofType属性:指实体类User的集合属性对象中元素的类型-->
        <collection property="ordersList" ofType="Orders">
            <id property="id" column="orderId"/>
            <result property="number" column="number"/>
            <result property="user_id" column="user_id"/>
        </collection>
    </resultMap>
</mapper>
第四步,核心配置文件中引入第三步中创建的映射文件。
  • 如果是嵌套查询方式,要在元素中引入OrdersMapper.xml、UserMapper.xml映射文件
<mapper resource="mapper/OrdersMapper.xml"/>
<mapper resource="mapper/UserMapper.xml"/>
  • 如果是嵌套结果方式,只要在元素中引入PersonMapper.xml映射文件
<mapper resource="mapper/UserMapper.xml"/>
第五步,完成测试。(按前述提示自行完成!)

测试:查询id值为1的客户订单信息,结果如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hf0v6YZy-1653758110283)(https://secure2.wostatic.cn/static/pPX3h2iApsVkrzwdow8WbR/i2ey2jocmu.png)]

四、多对多查询

1.需求描述

按订单号查询该订单有哪些商品。 关联表tb_product、tb_ordersitem数据如下:

idnameprice
1Java基础入门44.5
2Java Web程序开发入门38.5
3SSM框架整合实战50
idorders_idproduct_id
111
213
333

2.任务分析

  • tb_ordersitem的订单号为外键orders_id,该订单包含的商品id号为外键product_id;
  • tb_product的主键为id即为商品的id号;
  • tb_ordersitem的product_id与tb_product的id字段关联;
  • 一个订单有多种商品,一种商品可以属性多个订单,两表间构成了多对多关联关系。

3.知识准备:实现多对多映射关系的两种方式

  • 要点: 怎么设计实体类,表现多对多关系
  • 最核心要解决的问题: 是类类型属性与查询结果字段的映射,这种映射关系通过元素的子元素完成。同时,还应将查询元素的resultMap属性赋值为配置的映射关系的id值。
  • 有两种解决方式: 嵌套查询方式、嵌套结果方式。实现方法与一对多类似!

4.实验步骤

第一步,创建POJO类(注意:在com.cshbxy.pojo包内创建)
  • 创建Product类,用以描述 tb_product数据表
package com.cshbxy.pojo;

public class Product {
    private int id;
    private String name;
    private double price;

    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;
    }

    @Override
    public String toString() {
        return "Product{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}
  • 创建OrdersItem类,用以描述数据表一个orders_id包含多咱商品。之后查询结果为集合即可表现多对多关系!
package com.cshbxy.pojo;

import com.cshbxy.pojo.Product;

import java.util.List;

public class OrdersItem {
    private int id;
    private int orders_id;
    private List<Product> productList; //一个orders_id包含多种商品

    public int getId() {
        return id;
    }

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

    public int getOrders_id() {
        return orders_id;
    }

    public void setOrders_id(int orders_id) {
        this.orders_id = orders_id;
    }

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

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

    @Override
    public String toString() {
        return "OrdersItem{" +
                "id=" + id +
                ", orders_id=" + orders_id +
                ", productList=" + productList +
                '}';
    }
}

注:实际项目中,该类可能会放在vo包或bo包中。

第二步,创建接口OrdersItemMapper(注意:在com.cshbxy.mapper包中创建)
package com.cshbxy.mapper;

import com.cshbxy.pojo.OrdersItem;

import java.util.List;

public interface c {
    /*按订单号查询该订单有哪些商品*/
    List<OrdersItem> findProductByOrdersId();
}
第三步,编写映射文件(注意:在resources\mapper文件夹中创建)
  • 关键:如何完成实体类OrdersItem的productList属性与查询结果间的映射!
  • 有两种方式
    • 嵌套查询方式**(自行根据一对多关系实现方法完成!)**
    • 嵌套结果方式
      • OrdersItemMapper.xml映射文件中完成按id进行的一对一关联查询,同时要通过元素及其子元素对结果进行映射。该查询语句要用多表联合查询的书写方式
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cshbxy.mapper.OrdersItemMapper">
    <!--按订单号查询该订单有哪些商品-->
    <!--嵌套结果方式-->
    <select id="findProductByOrdersId" resultMap="ordersItem">
        select a.*,b.id oid,b.orders_id,b.product_id from mybatis.tb_product a
        join mybatis.tb_ordersitem b
        on a.id=b.product_id
        where orders_id=#{id}
    </select>
    <resultMap id="ordersItem" type="ordersItem">
        <id property="id" column="oid"/>
        <result property="orders_id" column="orders_id"/>
        <collection property="productList" ofType="Product">
            <id property="id" column="id"/>
            <result property="name" column="name"/>
            <result property="price" column="price"/>
        </collection>
    </resultMap>
</mapper>
第四步,核心配置文件中引入第三步创建的映射文件。
  • 如果是嵌套查询方式,自行根据具体情况完成映射文件的引入!
  • 如果是嵌套结果方式,在元素中引入OrdersItemMapper.xml映射文件
<mapper resource="mapper/OrdersItemMapper.xml"/>
第五步,完成测试。(按前述提示自行完成!)

测试:查询订单号orders_id值为1的订单包含的商品信息,结果如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jynuhKE3-1653758110285)(https://secure2.wostatic.cn/static/igxZzAJDBPDAYAbjm8cdWv/vpjaoeizrs.png)]

思考:如查要按商品id查询有哪些订单包含该商品应如何用Mybaits实现。

五、Mybatis缓存机制

在实际开发中经常需要合理地利用MyBatis缓存来加快数据库查询,进而有效地提升数据库性能。

1.补充:Mybatis日志输出

(1)使用Mybatis自带标准日志

在在MyBatis核心配置文件中加入如下配置。

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
(2)使用log4j日志输出

第一步,在MyBatis核心配置文件中加入LOG4J的日志配置。

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

第二步,resources目录下创建log4j.properties文件**(课后查阅log4j属性配置方法!)**

#全局日志配置
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

2.一级缓存

(1)特点
  • MyBatis的一级缓存是SqlSession级别的缓存
  • 如果同一个SqlSession对象多次执行完全相同的SQL语句时,在第一次执行完成后,MyBatis会将查询结果写入到一级缓存中,当第二次执行相同的查询语句时,MyBatis会直接读取一级缓存中的数据,而不用再去数据库查询,从而提高了数据库的查询效率
  • 当程序对数据库执行了插入、更新、删除操作,MyBatis会清空一级缓存中的内容以防止程序误读
(2)开启/关闭方法
  • Mybatis默认开启一级缓存
  • 关闭一级缓存有如下方法
    • 在映射文件的select标签中设置flushCache=“true”
    • 在核心配置文件的元素中配置localCacheScope属性值为STATEMENT
<settings>
    <setting name="localCacheScope" value="STATEMENT"/>
</settings>
(3)学会如何看是否启用了一级缓存 (自己写测试案例通过日志来看)

3.二级缓存

(1)特点
  • MyBatis的二级缓存是Mapper级别的缓存
  • 多个SqlSession对象使用同一个Mapper的相同查询语句去操作数据库,在第一个SqlSession对象执行完后,MyBatis会将查询结果写入二级缓存,当第二个SqlSession对象执行相同的查询语句时,MyBatis会直接读取二级缓存中的数据
  • 当程序对数据库执行了插入、更新、删除操作,MyBatis会清空二级缓存中的内容以防止程序误读
  • 要求相关的POJO类实现了序列化(implements Serializable)
(2)开启/关闭方法
  • Mybatis默认关闭二级缓存
  • 开启二级缓存步骤
    • 第一步,开启二级缓存的全局配置,在MyBatis的核心配置进行如下配置。
<settings>
    <setting name="cacheEnabled" value="true" />
</settings>
- 第二步,开启当前Mapper的namespace下的二级缓存。可以通过映射文件中的<cache>元素来完成。
<!-- 开启当前Mapper的namespace下的二级缓存-->
<cache></cache>
(3)学会如何看是否启用了二级缓存 (自己写测试案例通过日志来看)

案例:商品的类别

现有一个商品表product(商品编号、商品名称、商品单价、商品类别)和一个商品类别表category(商品类别编号、商品类别名称),其中,商品类别表category和商品表product是一对多的关系。各表具体情况如下:

商品编号 id商品名称 ****goodsname商品单价 price商品类别 typeid
1电视机50001
2冰箱40002
3空调30002
4洗衣机20002
商品类别编号 id商品类别名称 typename
1黑色家电
2白色家电

数据表生成代码如下:

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);

要求: 通过MyBatis查询商品类别为白色家电的商品的所有信息。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TechLens

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值