学习目标
- 了解数据表之间以及对象之间的三种关联关系
- 熟悉关联关系中的嵌套查询和嵌套结果
- 掌握一对一、一对多和多对多关联映射的使用
文章目录
1. 关联关系概述
- 一对一的关系:就是在本类中定义对方类型的对象
- 一对多的关系:就是一个A类类型对应多个B类类型的情况
- 多对多的关系:在A类中定义B类类型的集合,在B类中定义A类类型的集合
2. 一对一
Mybatis通过<resultMap>中的子元素<association>来处理一对一关联关系.
在<association>元素中,通常可以配置一下属性:
-
property:指定映射到的实体类对象属性,与表字段意义对应。
-
column:指定表中对应的字段。
-
javaType:指定映射到实体对象属性的类型。
-
select:指定引入嵌套查询的子SQL语句,该属性用于关联映射中的嵌套查询。
-
fetchType:指定在关联查询时是否启用延迟加载。fetchType属性有lazy和eager两个属性值,默认值为lazy(即默认关联延迟加载)。
<association>元素只需要参考如下两种实例配置:
<!-- 方式一:嵌套查询 -->
<association property="card" column="card_id"
javaType="IdCard"
select="com.mybatis.mapper.IdCardMapper.findCodeById" />
<!-- 方式二:嵌套结果 -->
<association property="card" javaType="com.mybatis.po.IdCard">
<id property="id" column="card_id" />
<result property="code" column="code" />
</association>
Mybatis在映射文件中加载关联关系对象主要通过两种方式:嵌套查询和嵌套结果。嵌套查询是指通过执行另外一条SQL映射语句来返回预期的复杂类型;嵌套结果是使用嵌套结果映射来处理重复的联合结果的子集。我们可以使用上述任何一种方式实现对关联关系的加载。
查询个人及其关联的身份证信息是先通过查询个人表中的主 主键来获个人信息, 然后通过表中的外键,来获取证件表中的身份证号信息。其具体实现步骤如下:
(1)在mybatis数据库中分别创建名为tb_idcard和tb_person的数据表,同时预先插入两条数据。
USE mybatis;
#创建一个名称为tb_idcard的表
CREATE TABLE tb_idcard(
id INT PRIMARY KEY AUTO_INCREMENT,
CODE VARCHAR(18)
);
#插入两条数据
INSERT INTO tb_ idcard (CODE) VALUES ('152221198711020624') ;
INSERT INTO tb_ idcard (CODE) VALUES ('152201199008150317') ;
#创建一个名称为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)
);
#插入两条数据
INSERT INTO tb_ person (name,age,sex,card id) VALUSE('Rose',27,'tom',27,'男',2);
INSERT INTO tb_person (name,age,sex, card _id) VALUES('tom',29, '女',1);
(2) 在Eclipse中创建工程,然后引入相关JAR包、log4j日志文件、MybatisUtils工具类以及mybatis-config.xml核心配置文件。
(3)在项目的com.mybatis.po包下创建持久化类idCard和Person。
package com.mybatis.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 + "]";
}
}
package com.mybatis.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 + "]";
}
}
(4)在com.mybatis.mapper包中,创建证件映射文件idCardMapper.xml和个人映射文件PersonMapper.xml,并在两个映射文件中编写一对一关联映射查询的配置信息。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 根据客户名编号查询客户信息列表 -->
<mapper namespace="com.mybatis.mapper.IdCardMapper">
<!-- 根据id查询证件信息 -->
<select id="findCodeById" parameterType="Integer"
resultType="IdCard">
select * from tb_idcard where id=#{id}
</select>
</mapper>
<?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.mybatis.mapper.PersonMapper">
<!-- 嵌套查询:通过执行另外一条SQL映射语句来返回预期的特殊类型 -->
<select id="findPersonById" parameterType="Integer"
resultMap="IdCardWithPersonResult">
select * from tb_person where id=#{id}
</select>
<resultMap type="Person" id="IdCardWithPersonResult">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="age" column="age" />
<result property="sex" column="sex" />
<!-- 一对一:association使用select属性引入另外一条SQL语句 -->
<association property="card" column="card_id"
javaType="IdCard"
select="com.mybatis.mapper.IdCardMapper.findCodeById" />
</resultMap>
<!-- 嵌套结果:使用嵌套结果映射来处理重复的联合结果的子集 -->
<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" />
<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>
(5)在核心配置文件mybatis-config.xml中,引入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 resource="db.properties" />
<!-- 使用扫描包的形式定义别名 -->
<typeAliases>
<package name="com.mybatis.po" />
</typeAliases>
<!-- 环境配置 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/mybatis/mapper/IdCardMapper.xml" />
<mapper resource="com/mybatis/mapper/PersonMapper.xml" />
</mappers>
</configuration>
(6)在com.mybatis.test包中,创建测试类MybatisAssociatedTest,并在类中编写测试方法findPersonByIdTest()。
package com.mybatis.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.mybatis.po.Person;
import com.mybatis.util.MybatisUtils;
/*
* Mybatis关联查询映射测试类
*/
public class MybatisAssociatedTest {
/*
* 嵌套查询
*/
@Test
public void CustomerTest() throws Exception {
// 1.通过工具类生成SqlSession对象
SqlSession session = MybatisUtils.getSession();
// 2.使用Mybatis嵌套查询的方式查询id为1的人的信息
Person person = session.selectOne("com.mybatis.mapper." + "PersonMapper.findPersonById", 1);
// 3.输出查询结果
System.out.println(person);
// 4.关闭SqlSession
session.close();
}
/*
* 嵌套结果
*/
@Test
public void CustomerTest2() {
// 1.通过工具类生成SqlSession对象
SqlSession session = MybatisUtils.getSession();
// 2.使用Mybatis嵌套查询的方式查询id为1的人的信息
Person person = session.selectOne("com.nynu.qdy.mapper." + "PersonMapper.findPersonById2", 1);
// 3.输出查询结果
System.out.println(person);
// 4.关闭SqlSession
session.close();
}
}
(7) 使用JUnit4分别执行findByIdTest()和CustomerTest2()后,控制台的输出结果如下图所示。
3.一对多
Mybatis通过<resultMap>中的子元素<collection>来处理一对多关联关系。<collection>子元素的属性与<association>元素相同,但还包括一个特殊属性——ofType。ofType属性与javaType属性对应,它用于指定实体对象中集合类属性所包含的元素类型。
<collection>元素的使用可以参考如下两种实例进行配置:
<!--方式一:嵌套查询-->
<collection property="ordersList" column="id"
ofType="com.mybatis.po.Orders">
select="com.mybatis.mapper.OrdersMapper.selectOrders" />
</collection>
<!-- 方式二:嵌套结果 -->
<collection property="ordersList" ofType="Orders">
<id property="id" column="orders_id" />
<result property="number" column="number" />
</collection>
在了解了MyBatis处理一对多关联关系的元素和方式后,接下来以用户和订单之间的这种一 对多关联关系为例,详细讲解如何在MyBatis中处理一对多 关联关系,具体步骤如下:
(1) 在mybatis数据库中,创建两个数据表,分别为tb_ _user和tb_ _orders, 同时在表中预先插入几条数据,执行的SQL语句如下所示。
#创建一个名称为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 的表
CREATE TABLE tb_orders(
1d 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', '1000011','1');
INSERT INTO tb orders VALUES ('3', '1000011','2');
(2) 在com.mybatis.po包中,创建持久化类Orders和User,并在两个类中定义相关属性和方法。
package com.mybatis.po;
import java.util.List;
public class Orders {
private Integer 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;
}
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 + "]";
}
}
package com.mybatis.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
+ "]";
}
}
(3) 在com.mybatis.mapper包中,创建用户实体类映射文件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.mybatis.mapper.UserMapper">
<!-- 一对多:查看某一用户及其关联的订单信息 注意:当关联查询出的列名相同,则需要使用别名区分 -->
<select id="findUserWithOrders" parameterType="Integer"
resultMap="UserWithOrdersResult">
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="UserWithOrdersResult">
<id property="id" column="id" />
<result property="username" column="username" />
<result property="address" column="address" />
<!-- 一对多关联映射:collection ofType表示属性集合中元素的类型,List<Orders>属性即Order类 -->
<collection property="ordersList" ofType="Orders">
<id property="id" column="orders_id" />
<result property="number" column="number" />
</collection>
</resultMap>
</mapper>
(4) 在核心配置文件mybatis-config.xml中,引入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 resource="db.properties" />
<!-- 使用扫描包的形式定义别名 -->
<typeAliases>
<package name="com.mybatis.po" />
</typeAliases>
<!-- 环境配置 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/mybatis/mapper/IdCardMapper.xml" />
<mapper resource="com/mybatis/mapper/PersonMapper.xml" />
<mapper resource="com/mybatis/mapper/UserMapper.xml" />
</mappers>
</configuration>
(5) 在测试类MybatisAssociatedTest中,编写测试方法findUserTest()方法。
package com.mybatis.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.mybatis.po.User;
import com.mybatis.util.MybatisUtils;
/*
* Mybatis关联查询映射测试类
*/
public class MybatisAssociatedTest {
/*
* 一对多
*/
@Test
public void findUserTest() {
// 1.通过工具类生成SqlSession对象
SqlSession session = MybatisUtils.getSession();
// 2.查询id为1的用户信息
User user = session.selectOne("com.mybatis.mapper." + "UserMapper.findUserWithOrders", 1);
// 3.输出查询结果信息
System.out.println(user);
// 4.关闭SqlSession
session.close();
}
}
(6) 使用JUnit执行findUserTest()方法后,控制台输出结果如下图所示。
4. 多对多
了解了数据库中订单表与商品表之间的多对多关联关系后,下面我们就通过具体的案例来讲解如何使用MyBatis来处理这种多对多的关系,具体实现步骤如下:
(1) 创建数据表。在mybatis数据库中新建名称为tb_product和tb_ordersitem的两个数据表,同时在表中预先插入几条数据。其执行的SQL语句如下所示。
#创建一个名称为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.51') ;
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');
(2) 在com.mybatis.po包中,创建持久化类Product,并在类中定义相关属性和方法。
package com.mybatis.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 + ", prices=" + price + ", orders=" + orders + "]";
}
}
(3)在com.mybatis.mapper包中,创建订单实体映射文件Ordermapper.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.nynu.qdy.mapper.OrdersMapper">
<!-- 多对多嵌套映射查询: 通过执行另外一条SQL映射语句来返回预期的特殊类型 -->
<select id="findOrdersWithProduct" 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" />
<collection property="productList" column="id"
ofType="Product"
select="com.mybatis.mapper.ProductMapper.findProductById">
</collection>
</resultMap>
<!-- 多对多嵌套结果查询:查询某订单及其关联的商品详情 -->
<select id="findOrdersWithProduct2" parameterType="Integer"
resultMap="OrdersWithProductResult2">
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="OrdersWithProductResult2">
<id property="id" column="id" />
<result property="number" column="number" />
<!-- 多对多关联映射:collection -->
<collection property="productList" ofType="Product">
<id property="id" column="id" />
<result property="name" column="name" />
<result property="price" column="price" />
</collection>
</resultMap>
</mapper>
(4)将创建的映射文件中OrderMapper.xml和ProductMapper.xml的文件路径配置到核心配置文件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" />
<!-- 使用扫描包的形式定义别名 -->
<typeAliases>
<package name="com.mybatis.po" />
</typeAliases>
<!-- 环境配置 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/mybatis/mapper/OrdersMapper.xml" />
<mapper resource="com/mybatis/mapper/ProductMapper.xml" />
</mappers>
</configuration>
(5)在测试类MybatisAssociatedTest中,编写多对多关联查询的测试方法findOrdersTest()。
package com.mybatis.test;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import com.mybatis.po.Orders;
import com.mybatis.po.User;
import com.mybatis.util.MybatisUtils;
/*
* Mybatis关联查询映射测试类
*/
public class MybatisAssociatedTest {
/*
* 多对多
*/
@Test
public void findOrdersTest() {
// 1.通过工具类生成SqlSession对象
SqlSession session = MybatisUtils.getSession();
// 2.查询id为1的订单中的商品信息
Orders orders = session.selectOne("com.mybatis.mapper." + "OrdersMapper.findOrdersWithProduct", 1);
// 3.输出查询结果信息
System.out.println(orders);
// 4.关闭SqlSession
session.close();
}
/*
* 多对多嵌套查询
*/
@Test
public void findOrdersTest2() {
// 1.通过工具类生成SqlSession对象
SqlSession session = MybatisUtils.getSession();
// 2.使用Mybatis嵌套查询的方式查询id为1的人的信息
Orders orders = session.selectOne("com.mybatis.mapper." + "OrdersMapper.findOrdersWithProduct2", 1);
// 3.输出查询结果
System.out.println(orders);
// 4.关闭SqlSession
session.close();
}
}
(6) 使用JUnit4执行findOrdersTest()方法后,控制台的输出结果如下图所示。