学习目标
- 了解数据表之间以及对象之间的三种关联关系
- 熟悉关联关系中的嵌套查询和嵌套结果
- 掌握一对一、一对多和多对多关联映射的使用
文章目录
1. 关联关系概述
2. 一对一
3.一对多
4. 多对多
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;}@Overridepublic 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;}@Overridepublic 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 mapperPUBLIC "-//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 mapperPUBLIC "-//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.codefrom tb_person p,tb_idcard idcardwhere p.card_id=idcard.idand 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 configurationPUBLIC "-//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 {/** 嵌套查询*/@Testpublic 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.关闭SqlSessionsession.close();}/** 嵌套结果*/@Testpublic 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.关闭SqlSessionsession.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;}@Overridepublic 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;}@Overridepublic 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 mapperPUBLIC "-//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.numberfrom tb_user u,tb_orders owhere u.id=o.user_idand 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 configurationPUBLIC "-//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 {/** 一对多*/@Testpublic 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.关闭SqlSessionsession.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; // 商品idprivate 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;}@Overridepublic 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 mapperPUBLIC "-//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.pricefrom tb_orderso,tb_product p,tb_ordersitem oiwhere oi.orders_id=o.idandoi.product_id=p.idand 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 configurationPUBLIC "-//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 {/** 多对多*/@Testpublic 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.关闭SqlSessionsession.close();}/** 多对多嵌套查询*/@Testpublic 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.关闭SqlSessionsession.close();}
}
(6) 使用JUnit4执行findOrdersTest()方法后,控制台的输出结果如下图所示。