第9章 MyBatis的关系映射

时间:2022-08-27 23:56:45

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

9.1 关联关系概述

第9章 MyBatis的关系映射

一对一:在任意一方引入对方主键作为外键。

一对多:在“多”的一方,添加“一“的一方的主键作为外键。(连着多条线的一方是“多”)

多对多:产生中间关系表,引入两张表的主键作为外键,两个主键成为联合主键或使用新的字段作为主键。

用Java对象描述

class A{
B b;
}
class B{
A a;
}
//一对一:在本类中定义对方类型的对象,如A类中定义B类类型的属性b,B类中定义A类类型的属性a
class A{
List<B>b;
}
class B{
A a;
}
//一对多:就是一个A类类型中对应多个B类类型的情况,需要在A类中以集合的方式引入B类类型的对象,在B类中定义A类类型的属性a
class A{
List<B> b;
}
class B{
List<A> a;
}
//多对多:在A类中定义B类类型的集合,在B类中定义A类类型的集合

一对一:用<association>元素处理

a)、property:指定映射到的实体类对象中的属性,与表字段一一对应。

b)、column:指定数据库表中对应的字段。

c)、javaType:指定映射到实体对象属性的类型。

d)、select:指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询。

e)、fetchType:指定在关联查询时是否启用延迟加载。该属性有lazy和eager两个属性值,默认值为lazy(即默认关联映射延迟加载)。

MyBatis加载关联关系对象主要通过两种方式:嵌套查询和嵌套结果。

第9章 MyBatis的关系映射

问题:虽然使用嵌套查询的方式比较简单,但是嵌套查询的方式要执行多条SQL语句,这对于大型数据集合和列表展示不是很好,因为这样可能会导致成百上千条关联的SQL语句被执行,从而极大的消耗数据库性能并且会降低查询效率。类似暴力for循环吧。

解决:MyBatis延迟加载的配置。使用MyBatis的延迟加载在一定程度上可以降低运行消耗并提高查询效率。MyBatis默认没有开启延迟加载,需要在核心配置文件中的<settings>元素内进行配置,具体配置方式如下:

<settings>
<setting name="lazyLoadingEnabled" value="true" />
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

在映射文件中,<association>元素和<collection>元素中都已默认配置了延迟加载属性,即默认属性fetchType="lazy"(属性fetchType="eager"表示立即加载),所以在配置文件中开启延迟加载后,无需在映射文件中再做配置。

举例:以个人和身份证之间的一对一关联关系

创建两个表tb_idcard和tb_person

USE mybatis;
CREATE TABLE tb_idcard(
id INT PRIMARY KEY AUTO_INCREMENT,
CODE VARCHAR(18)
);
INSERT INTO tb_idcard(CODE) VALUE('');
INSERT INTO tb_idcard(CODE) VALUE(''); 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)
);
INSERT INTO tb_person(NAME,age,sex,card_id) VALUE('Rose',29,'女',1);
INSERT INTO tb_person(NAME,age,sex,card_id) VALUE('tom',27,'男',2);

此时表内数据:

第9章 MyBatis的关系映射

创建持久化类IdCard和Person

package com.itheima.po;
/**
* 证件持久化类
*/
public class IdCard {
private Integer id;
private String code;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String toString() {
return "IdCard [id=" + id + ", code=" + code + "]";
}
}

IdCard

package com.itheima.po;
/**
* 个人持久化类
*/
public class Person {
private Integer id;
private String name;
private Integer age;
private String sex;
private IdCard card; //个人关联的证件
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer 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 + "]";
}
}

Person

嵌套查询:

IdCardMapper.xml映射文件:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.IdCardMapper"> <!-- 根据id查询证件信息,最普通的配置信息 -->
<select id="findCodeById" parameterType="Integer" resultType="IdCard">
SELECT * from tb_idcard where id=#{id}
</select> </mapper>

PersonMapper.xml映射文件:

<!-- 嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型 -->
<select id="findPersonById" parameterType="Integer"
resultMap="IdCardWithPersonResult123">
SELECT * from tb_person where id=#{id}
</select> <!-- resultMap最终还是要将结果映射到pojo上,type就是指定映射到哪一个pojo -->
<resultMap type="Person" id="IdCardWithPersonResult123">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="age" column="age" />
<result property="sex" column="sex" /> <!-- 一对一:association使用select属性引入另外一条SQL语句,是另一个映射文件的select元素id -->
<association property="card" column="card_id" javaType="IdCard"
select="com.itheima.mapper.IdCardMapper.findCodeById" />
</resultMap>

测试方法:

    /**
* 嵌套查询
*/
@Test
public void findPersonByIdTest() {
// 1、通过工具类生成SqlSession对象
SqlSession session = MybatisUtils.getSession();
// 2.使用MyBatis嵌套查询的方式查询id为1的人的信息
Person person = session.selectOne("com.itheima.mapper."
+ "PersonMapper.findPersonById", 1);
// 3、输出查询结果信息
System.out.println(person);
// 4、关闭SqlSession
session.close();
}

findPersonByIdTest()

运行结果:执行了多条简单的SQL语句

第9章 MyBatis的关系映射

嵌套结果:

<!-- 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集 -->
<select id="findPersonById2" parameterType="Integer"
resultMap="IdCardWithPersonResult2">
SELECT p.*,idcard.code
from tb_person p,tb_idcard idcard
where p.card_id=idcard.id
and p.id= #{id}
</select> <resultMap type="Person" id="IdCardWithPersonResult2">
<id property="id" column="id" /><!-- 声明主键,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>

PersonMapper.xml映射文件

    /**
* 嵌套结果
*/
@Test
public void findPersonByIdTest2() {
// 1、通过工具类生成SqlSession对象
SqlSession session = MybatisUtils.getSession();
// 2.使用MyBatis嵌套结果的方法查询id为1的人的信息
Person person = session.selectOne("com.itheima.mapper."
+ "PersonMapper.findPersonById2", 1);
// 3、输出查询结果信息
System.out.println(person);
// 4、关闭SqlSession
session.close();
}

测试方法

测试结果:只执行了一条复杂的SQL语句。

DEBUG [main] - ==> Preparing: SELECT p.*,idcard.code from tb_person p,tb_idcard idcard where p.card_id=idcard.id and p.id= ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
Person [id=1, name=Rose, age=29, sex=女, card=IdCard [id=1, code=152221198711020624]]

select p.*,idcard.code from tb_person p,tb_idcard idcard where p.card_id=idcard.id and p.id = #{id}

这里的p是tb_person的别名,因为别名容易写,第二个tb_person后接空格再加p是格式,如果不用别名就是:

select tb_person.*,tb_idcard.code from tb_person,tb_idcard where tb_person.card_id=tb_idcard.id and tb_person.id = #{id}

一对一关系配置模板:

第9章 MyBatis的关系映射

9.3 一对多

<resultMap>元素中,包含一个子元素<collection>元素,属性大部分和<association>元素相同,但有一个特殊属性ofType,这个属性和javaType属性对应,用于指定实体对象中集合类属性所包含的元素类型。

<collection>元素的使用模板:

第9章 MyBatis的关系映射

1.在MySQL中建表tb_user和tb_orders,并插入几条数据

USE mybatis;

CREATE TABLE tb_user(
id INT(32) PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(32),
address VARCHAR(256)
);
INSERT INTO tb_user VALUES('','詹姆斯','克利夫兰');
INSERT INTO tb_user VALUES('','科比','洛杉矶');
INSERT INTO tb_user VALUES('','保罗','洛杉矶'); USER mybatis;
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)
);
INSERT INTO tb_orders VALUES('','','');
INSERT INTO tb_orders VALUES('','','');
INSERT INTO tb_orders VALUES('','','');

结果:

第9章 MyBatis的关系映射

2.创建持久化类Orders和User

package com.itheima.po;
import java.util.List;
/**
* 用户持久化类
*/
public class User {
private Integer id; // 用户编号
private String username; // 用户姓名
private String address; // 用户地址
private List<Orders> ordersList; //用户关联的订单...................................
public Integer getId() {
return id;
}
public void setId(Integer 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 + ", ordersList=" + ordersList + "]";
}
}

User.java

package com.itheima.po;

import java.util.List;

/**
* 订单持久化类
*/
public class Orders {
private Integer id; //订单id
private String number;//订单编号
//关联商品集合信息
private List<Product> productList; public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
// @Override
// public String toString() {
// return "Orders [id=" + id + ", number=" + number + "]";
// }
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 + ", productList=" + productList + "]";
} }

Orders.java

3.创建映射文件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"> <!-- namespace表示命名空间 -->
<mapper namespace="com.itheima.mapper.UserMapper"> <!-- 一对多:查看某一用户及其关联的订单信息
注意:当关联查询出的列名相同,则需要使用别名区分 -->
<select id="findUserWithOrders123" parameterType="Integer"
resultMap="UserWithOrdersResult123">
SELECT u.*,o.id as orders_id,o.number
from tb_user u,tb_orders o
WHERE u.id=o.user_id
and u.id=#{id}
</select> <resultMap type="User" id="UserWithOrdersResult123">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="address" column="address"/> <!-- 一对多关联映射:collection
ofType表示属性集合中元素的类型,List<Orders>属性即Orders类 -->
<collection property="ordersList" ofType="Orders">
<id property="id" column="orders_id"/>
<result property="number" column="number"/>
</collection> </resultMap>
</mapper>

4.测试方法:

/**
* 一对多
*/
@Test
public void findUserTest() {
// 1、通过工具类生成SqlSession对象
SqlSession session = MybatisUtils.getSession();
// 2、查询id为1的用户信息
User user = session.selectOne("com.itheima.mapper."
+ "UserMapper.findUserWithOrders123", 1);
// 3、输出查询结果信息
System.out.println(user);
// 4、关闭SqlSession
session.close();
}

findUserTest

5.查询结果:

DEBUG [main] - ==> Preparing: SELECT u.*,o.id as orders_id,o.number from tb_user u,tb_orders o WHERE u.id=o.user_id and u.id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 2
User [id=1, username=詹姆斯, address=克利夫兰, ordersList=[Orders [id=1, number=1000011, productList=null], Orders [id=2, number=1000012, productList=null]]]

9.4 多对多

以商品和订单为例,一个订单可以包含多个商品,一个商品可以属于多个订单,数据库中的多对多联系通常使用一个中间表来维护,中间表中的订单id作为外键参照订单表的id,商品id作为外键参照商品表的id。

(外键:这个表的某一属性是别的表的主键,可以有重复,可以有多个,可以为空)

1.建表:tb_product和tb_ordersitem

USE mybatis;

CREATE TABLE tb_product(
id INT(32) PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(32),
price DOUBLE
);
INSERT INTO tb_product VALUES('','Java基础入门','44.5');
INSERT INTO tb_product VALUES('','Java Web程序开发入门','38.5');
INSERT INTO tb_product VALUES('','SSM框架整合实战',''); 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)
);
INSERT INTO tb_ordersitem VALUES('','','');
INSERT INTO tb_ordersitem VALUES('','','');
INSERT INTO tb_ordersitem VALUES('','','');

建表后:

第9章 MyBatis的关系映射

2.创建持久化类Product表示商品,订单用以前的Order

package com.itheima.po;
import java.util.List;
/**
* 商品持久化类
*/
public class Product {
private Integer id; //商品id
private String name; //商品名称
private Double price;//商品单价
private List<Orders> orders; //与订单的关联属性...........................
public Integer getId() {
return id;
}
public void setId(Integer 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> getOrders() {
return orders;
}
public void setOrders(List<Orders> orders) {
this.orders = orders;
}
@Override
public String toString() {
return "Product [id=" + id + ", name=" + name
+ ", price=" + price + "]";
}
}

Product.java

3.创建订单实体映射文件OrdersMapper.xml和ProductMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.OrdersMapper"> <!-- 多对多嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型
定义了一个id为findOrdersWithPorduct的select语句来查询订单及其关联的商品信息-->
<select id="findOrdersWithPorduct" parameterType="Integer"
resultMap="OrdersWithProductResult">
select * from tb_orders WHERE id=#{id}
</select> <resultMap type="Orders" id="OrdersWithProductResult">
<id property="id" column="id" />
<result property="number" column="number" /> <!-- property属性表示订单持久化类中的商品属性,ofType属性表示集合中的数据为Product类型,而column的
属性值会作为参数执行ProductMapper中定义的id为findProductById的执行语句来查询订单中的商品信息-->
<collection property="productList" column="id" ofType="Product"
select="com.itheima.mapper.ProductMapper.findProductById">
</collection> </resultMap> <!-- 多对多嵌套结果查询:查询某订单及其关联的商品详情 -->
<select id="findOrdersWithPorduct2" parameterType="Integer"
resultMap="OrdersWithPorductResult2">
select o.*,p.id as pid,p.name,p.price
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 type="Orders" id="OrdersWithPorductResult2">
<id property="id" column="id" />
<result property="number" column="number" />
<!-- 多对多关联映射:collection -->
<collection property="productList" ofType="Product">
<id property="id" column="pid" />
<result property="name" column="name" />
<result property="price" column="price" />
</collection>
</resultMap> </mapper>

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="com.itheima.mapper.ProductMapper">
<select id="findProductById" parameterType="Integer"
resultType="Product">
SELECT * from tb_product where id IN(
SELECT product_id FROM tb_ordersitem WHERE orders_id = #{id}
)
</select>
</mapper>

ProductMapper.xml

4.测试方法:

    /**
* 多对多
*/
@Test
public void findOrdersTest(){
// 1、通过工具类生成SqlSession对象
SqlSession session = MybatisUtils.getSession();
// 2、查询id为1的订单中的商品信息
Orders orders = session.selectOne("com.itheima.mapper."
+ "OrdersMapper.findOrdersWithPorduct", 1);
// 3、输出查询结果信息
System.out.println(orders);
// 4、关闭SqlSession
session.close();
}

findOrdersTest()

5.测试结果:

DEBUG [main] - ==> Preparing: select * from tb_orders WHERE id=?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
DEBUG [main] - ==> Preparing: SELECT * from tb_product where id IN( SELECT product_id FROM tb_ordersitem WHERE orders_id = ? )
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 2
Orders [id=1, number=1000011, productList=[Product [id=1, name=Java基础入门, price=44.5], Product [id=3, name=SSM框架整合实战, price=50.0]]]

第9章 MyBatis的关系映射的更多相关文章

  1. MyBatis加强(1)~myBatis对象关系映射(多对一关系、一对多关系)、延迟&sol;懒加载

    一.myBatis对象关系映射(多对一关系.一对多关系) 1.多对一关系: ---例子:多个员工同属于一个部门. (1)myBatis发送 额外SQL: ■ 案例:员工表通过 dept_id 关联 部 ...

  2. MyBatis框架——关系映射(一对多、多对多、多对一查询)

    关系映射 一.映射(多)对一.(一)对一的关联关系 1).使用列的别名 ①.若不关联数据表,则可以得到关联对象的id属性 ②.若还希望得到关联对象的其它属性.则必须关联其它的数据表 1.创建表: 员工 ...

  3. Mybatis对象关系映射 one2one&comma;one2many&comma;many2many

    MyBatis中的高级映射一般要借助select元素中的resultMap属性进行实现,通过此属性配置实现一对一,一对多等关系映射的实现 一对一映射:association 一对多映射:collect ...

  4. 框架应用:Mybatis &lpar;三&rpar; - 关系映射

    你有一张你自己的学生证?(一对一) 你这一年级有多少个学生?(一对多) 班上学生各选了什么课?(多对多) 两张表以上的操作都需要联立多张表,而用SQL语句可以直接联立两张表,用工程语言则需要手动完成这 ...

  5. mybatis的关系映射

    一.多对一的映射关系 举例:根据员工编号查询员工所在部门的部门信息 第一步,需要在多的一方也就是员工实体类中持有一的一方部门实体类的引用 第二步,在dao接口中声明方法 第三步,在mapper中实现该 ...

  6. mybatis 对象关系映射例子

    入门 http://legend2011.blog.51cto.com/3018495/908956 增删改 http://legend2011.blog.51cto.com/3018495/9130 ...

  7. &lbrack;刘阳Java&rsqb;&lowbar;MyBatis&lowbar;实体关系映射&lowbar;第8讲

    MyBatis既然是一个ORM框架,则它也有像Hibernate那样的一对多,多对多,多对一的实体关系映射功能.下面我们就来介绍一下如何使用MyBatis的实体关系映射 1.MyBatis实体关系映射 ...

  8. Java EE数据持久化框架 • 【第3章 MyBatis高级映射】

    全部章节   >>>> 本章目录 3.1 一对一映射 3.1.1 自动化一对一映射 3.1.2 标签配置一对一映射 3.1.3 标签配置一对一映射 3.1.4 实践练习 3.2 ...

  9. Mybatis框架中实现双向一对多关系映射

    学习过Hibernate框架的伙伴们很容易就能简单的配置各种映射关系(Hibernate框架的映射关系在我的blogs中也有详细的讲解),但是在Mybatis框架中我们又如何去实现 一对多的关系映射呢 ...

随机推荐

  1. ASP&period;NET MVC 从零开始 - 自动化部署(其二)

    这篇文章是从我的 github 博客 http://lxconan.github.io 导入的. 这是这个系列的第五篇了,前四篇请参见: ASP.NET MVC 从零开始 – Create and R ...

  2. VS2010遇到&lowbar;WIN32&lowbar;WINNT宏定义问题

    最近拿到一个别人的工程,是使用VS.net创建的,而我的机器上只有vs2010,于是用自带的转换工具将它转换成vs2010的工程,转换之前我就很担心,怕转换完后会出问题,但是没有办法,我实在是不想再安 ...

  3. Android(java)学习笔记74:Java线程池

    线程池: 1)程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互.而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池. 2)线程池里的每一 ...

  4. PHP导出MySQL数据到Excel文件

    PHP导出MySQL数据到Excel文件 转载 常会碰到需要从数据库中导出数据到Excel文件,用一些开源的类库,比如PHPExcel,确实比较容易实现,但对大量数据的支持很不好,很容易到达PHP内存 ...

  5. InitParam与ContextParm的异同

    web.xml里面可以定义两种参数:(1)application范围内的参数,存放在servletcontext中,在web.xml中配置如下: xml 代码 <context-param&gt ...

  6. DevExpress Report 其他常用设计技巧

    原文:DevExpress Report 其他常用设计技巧 1 设置默认的打印纸张及页边距 选择Report-打开属性窗口,设置默认边距(Margins)和默认纸张(PaperKind). 2 修改R ...

  7. projective dynamics的global solve中 引入拉格朗日乘子的简化方法

    想了一下使用乘子法还是可行的/做一个简化.在约束C(xn) 在C(xn-1)处线性展开 (n是时间步骤)具体推导留作备份等有时间了去代码实现 3式是一个典型的LCP问题 用PGS就行 左边的系数部分依 ...

  8. numpy中的数学

    1.dot,exp v = np.dot(arg1,arg2) #矩阵乘法 v2 = np.exp() # e的-x 次方

  9. CSS加DIV布局

    第一种: <div> <div class="right"> <p></p> <p></p> <p&g ...

  10. Jenkins&lpar;四&rpar;---Jenkins添加密钥对

    一.添加密钥 1.添加git用户和git密码对 ,用于git客户端从gitlab上拉取代码到本地 /** lihaibo 文章内容都是根据自己工作情况实践得出. *版权声明:本博客欢迎转发,但请保留原 ...