04ssm_Mybaits关联映射和缓存机制

文章目录

  • 04ssm_Mybaits关联映射和缓存机制
    • 一、关联映射概述
      • 1.数据表的三种关联映射关系
      • 2.Java描述数据表的关联映射关系
      • 3.本单元实验环境
    • 二、一对一查询(重点掌握)
      • 1.需求描述
      • 2.任务分析
      • 3.知识准备:实现一对一映射关系的两种方式
        • (1)元素
        • (2)嵌套查询方式
        • (3)嵌套结果方式(重点掌握)
      • 4.实验步骤
        • 第一步,创建POJO类(注意:在com.cshbxy.pojo包内创建)
        • 第二步,创建接口PersonMapper(注意:在com.cshbxy.mapper包中创建)
        • 第三步,编写映射文件(注意:在resources\mapper文件夹中创建)
        • 第四步,核心配置文件中引入第三步中创建的映射文件。
        • 第五步,完成测试。(按前述提示自行完成!)
    • 三、一对多查询(重点掌握)
      • 1.需求描述
      • 2.任务分析
      • 3.知识准备:实现一对多映射关系的两种方式
        • (1)元素
        • (2)嵌套查询方式
        • (3)嵌套结果方式(重点掌握)
      • 4.实验步骤
        • 第一步,创建POJO类(注意:在com.cshbxy.pojo包内创建)
        • 第二步,创建接口UserMapper(注意:在com.cshbxy.mapper包中创建)
        • 第三步,编写映射文件(注意:在resources\mapper文件夹中创建)
        • 第四步,核心配置文件中引入第三步中创建的映射文件。
        • 第五步,完成测试。(按前述提示自行完成!)
    • 四、多对多查询
      • 1.需求描述
      • 2.任务分析
      • 3.知识准备:实现多对多映射关系的两种方式
      • 4.实验步骤
        • 第一步,创建POJO类(注意:在com.cshbxy.pojo包内创建)
        • 第二步,创建接口OrdersItemMapper(注意:在com.cshbxy.mapper包中创建)
        • 第三步,编写映射文件(注意:在resources\mapper文件夹中创建)
        • 第四步,核心配置文件中引入第三步创建的映射文件。
        • 第五步,完成测试。(按前述提示自行完成!)
        • 思考:如查要按商品id查询有哪些订单包含该商品应如何用Mybaits实现。
    • 五、Mybatis缓存机制
      • 1.补充:Mybatis日志输出
        • (1)使用Mybatis自带标准日志
        • (2)使用log4j日志输出
      • 2.一级缓存
        • (1)特点
        • (2)开启/关闭方法
        • (3)学会如何看是否启用了一级缓存 (自己写测试案例通过日志来看)
      • 3.二级缓存
        • (1)特点
        • (2)开启/关闭方法
        • (3)学会如何看是否启用了二级缓存 (自己写测试案例通过日志来看)
    • 案例:商品的类别

一、关联映射概述

在实际开发中,对数据库的操作常常会涉及到多张表,针对多表之间的操作,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 configurationPUBLIC "-//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 {//创建会话工厂对象factoryInputStream 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 {@Testpublic void test01() throws IOException {//第一大步,创建会话对象sqlSessionSqlSession sqlSession = MyTool.getSqlSession();//第二大步,通过会话对象的getMapper()方法实现接口CustomerMapper,并获取该对象PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);//第三大步,完成测试(此处自己针对实现的方法进行测试)//第四大步,关闭会话对象。因为之前创建事务时带true参数,所以此处不必考虑事务提交问题sqlSession.close();}
}

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

1.需求描述

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

id name age sex card_id
1 Rose 22 1
2 jack 23 2
id code
1 152221198711020624
2 152201199008150317

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;}@Overridepublic 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;}@Overridepublic 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 mapperPUBLIC "-//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_idcardwhere id=#{id}</select>
</mapper>
    - 之后,在**PersonMapper.xml映射文件**中完成按id进行的一对一关联查询,同时要通过<resultMap>元素及其子元素<association>对结果进行映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//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_personwhere 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 mapperPUBLIC "-//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 ajoin mybatis.tb_idcard bon a.card_id=b.idwhere 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数据如下:

id username address
1 小明 北京
2 李华 上海
3 李刚 上海
id number user_id
1 1000011 1
2 1000012 1
3 1000013 2

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;}@Overridepublic 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;}@Overridepublic 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 mapperPUBLIC "-//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_orderswhere user_id=#{user_id}</select>
</mapper>
    - 之后,在**UserMapper.xml映射文件**中完成按id进行的一对多关联查询,同时要通过<resultMap>元素及其子元素<collection>对结果进行映射
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//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_userwhere 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 mapperPUBLIC "-//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 ajoin mybatis.tb_orders bon a.id=b.user_idwhere 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数据如下:

id name price
1 Java基础入门 44.5
2 Java Web程序开发入门 38.5
3 SSM框架整合实战 50
id orders_id product_id
1 1 1
2 1 3
3 3 3

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;}@Overridepublic 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;}@Overridepublic 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 mapperPUBLIC "-//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 ajoin mybatis.tb_ordersitem bon a.id=b.product_idwhere 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 电视机 5000 1
2 冰箱 4000 2
3 空调 3000 2
4 洗衣机 2000 2
商品类别编号 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查询商品类别为白色家电的商品的所有信息。

04ssm_Mybaits关联映射和缓存机制相关推荐

  1. 第4章 MyBatis的关联映射和缓存机制

    目录/Contents 第4章 MyBatis的关联映射和缓存机制 学习目标 了解数据表之间的三种关联关系 了解对象之间的三种关系 熟悉关联关系中的嵌套查询和嵌套结果 掌握一对一关联映射 掌握一对多关 ...

  2. mysql映射表_[玩转MySQL之四]MySQL缓存机制

    一.前言 在当今的各种系统中,缓存是对系统性能优化的重要手段.MySQL Query Cache(MySQL查询缓存)在MySQL Server中是默认打开的,但是网上各种资料以及有经验的DBA都建议 ...

  3. Hibernate5笔记7--Hibernate缓存机制

    Hibernate缓存机制: 缓存范围: 应用程序中根据缓存的范围,可以将缓存分为三类:   (1)事务范围缓存(单Session,即一级缓存) 事务范围的缓存只能被当前事务访问,每个事务都有各自的缓 ...

  4. Hibernate和MyBatis的缓存机制和比较

    原文地址:https://my.oschina.NET/u/1445731/blog/416200?p=%7B%7BtotalPage%7D%7D Mybatis缓存 分为1级缓存和2级缓存,2级缓存 ...

  5. 大规模网站架构的缓存机制和几何分形学

    缓存机制在我们的实际研发工作中,被极其广泛地应用,通过这些缓存机制来提升系统交互的效率.简单的总结来说,就是在两个环节或者系统之间,会引入一个cache/buffer做为提升整体效率的角色. 而 有趣 ...

  6. 【问底】徐汉彬:大规模网站架构的缓存机制和几何分形学

    摘要:缓存机制简单总结可以说是空间换时间,被用于提升系统交互的效率.而有趣的是,这种缓存机制令人惊奇并且优美的遵循着"几何分形"的规律,也就是几何分形学中的"自相似性&q ...

  7. 13.MyBatis缓存机制

    13.MyBatis缓存机制 1. 为什么使用缓存? 当用户频繁查询某些固定的数据时,第一次将这些数据从数据库中查询出来,保存在缓存中.当用户再次查询这些数据时,不用再通过数据库查询,而是去缓存里面查 ...

  8. MyBatis复习笔记6:MyBatis缓存机制

    MyBatis缓存机制 MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制.缓存可以极大的提升查询效率. MyBatis系统中默认定义了两级缓存. 一级缓存和二级缓存. 默认情 ...

  9. Mybait缓存机制

    MyBatis同大多数ORM框架一样,提供了一级缓存和二级缓存的支持. 一级缓存:其作用域为session范围内,当session执行flush或close方法后,一级缓存会被清空. 二级缓存:二级缓 ...

最新文章

  1. 一文梳理2019年腾讯广告算法大赛冠军方案
  2. [转]C++ 使用Makefile文件
  3. js margin作用到父元素_CSS中margin-top对父级元素产生作用的问题
  4. 可以在某些场合替代onload事件的domReady事件
  5. JavaScipt30(第三个案例)(主要知识点:css变量)
  6. bootstrap 学习网址
  7. 12产品经理要懂的-人性满足思维
  8. linux内存操作--ioremap和mmap
  9. codeforces C. Valera and Tubes
  10. SpaceVim 1.1.0 发布,模块化 Vim IDE
  11. C语言程序——标准输出格式
  12. php后缀视频,PHP获取文件后缀名的方法有哪些?(图文+视频)
  13. 六大危害不容忽视 笔记本外接显示器杂谈
  14. 在Word中嵌入Mathtype公式编辑器
  15. 未来的外科手术可能由气泡代劳
  16. 数仓知识03:“自上而下“建设数仓和“自下而上“建设数仓的差异
  17. 洛谷P4207 [NOI2005]月下柠檬树(计算几何+自适应Simpson法)
  18. 360度全景标定方法_基于IMU辅助的360度全景视图多相机标定方法与流程
  19. 用AI变革新药研发,终极目标是延缓衰老,这家创业公司迎来“里程碑”
  20. 2.1.4 选择器处理网络请求

热门文章

  1. 微信小程序的酒店客房预订系统
  2. R语言:北京PM2.5浓度回归分析
  3. 商城系统中常用的订单流水号生成工具OrderIdUtil工具类(亲测好用)
  4. python商城系统_基于python的海鲜商城系统
  5. 【天光学术】4s店销售经理述职报告
  6. 能源互联网电容补偿柜
  7. 三 计算机知识的重要性分析,学习计算机基础知识对中专学生的重要性分析
  8. pytest测试框架学习-1
  9. HttpClient 实现 快递100 快递查询
  10. 机器学习之KNN原理与代码实现