简单介绍:
在实际的开发中,多对多的查询也是非常常见的。比如一个订单可以有很多的商品,而一个商品也可以被很多的订单所包含,而这种时候,如果我们有需求查询某一个订单内包含的所有商品;或者查询购买这个商品的订单,那么就需要使用到我们的多对对查询关联映射。
在多对多查询的时候,我们需要引入到一个新的概念,叫做中间表。中间表的概念就是帮助我们维护订单表和商品的对应关系,也就是每一个订单对应的商品的编号,或者是每一个商品对应的订单的编号。我们在查询的时候,首先要根据订单的编号从中间表查询出对应的商品编号,然后通过商品编号在商品表中查询出具体的商品的信息,最后把查询结果与Java实体类映射在一起输出最终的查询结果。
而所谓的多对对,其实就是经过中间表被分成了中间表对订单表,中间表对商品表两个一对多的关系,而对于多对多的关系中,我们并不需要学习新的标签和属性,唯一需要注意的就是中间表的使用和表数据的填写即可。
使用方法:
多对多的使用方法和一对多的方法相同,其关键点在于SQL语句的编写,以及对于表关系和表字段有一个清晰的认知和明确的认识:
订单表:
其中,order_id表示订单的编号,user_id表示用户的编号,是之前用来查询一个用户对应多个订单的时候用来确定订单属于哪一个用户的字段,order_informaton是用来描述订单的字段。
商品表:
good_id表示商品的编号,good_information是用来描述商品的字段
中间表:
这个表就是用来描述订单和商品之间关系的中间表,order_id表示订单号,同一个订单号就表示来自于同一个订单;good_id表示商品编号,同一个商品编号表示同一个商品。这个表最重要的就是理解订单号和商品编号是如何对应的。
创建表的SQL语句:
create table orders(
order_id int,
user_id int,
order_information varchar(20)
);
# order_id表示的是订单的编号,user_id表示的是用户的编号,同一个用户编号表示属于同一个用户
insert into orders values (1,1,'我是张三的第一个订单'),
(2,1,'我是张三的第二个订单'),
(3,2,'我是李四的第一个订单'),
(4,2,'我是李四的第二个订单'),
(5,2,'我是李四的第三个订单'),
(6,2,'我是李四的第四个订单'),
(7,3,'我是王五的第一个订单');
select * from orders;
# 创建商品详情表,存储的是商品的详情,good_id表示商品的编号,good_information表示商品的详情介绍
create table goods(
good_id int,
good_information varchar(20)
);
insert into goods values (1,'JavaEE基础'),
(2,'Java基础'),
(3,'数据库基础'),
(4,'MySQL基础');
select * from goods;
# 创建中间表,用来对应订单与商品之间的关系
create table order_list(
id int primary key auto_increment,
order_id int,
good_id int
);
insert into order_list values (null,1,1),
(null,1,2),
(null,2,1),
(null,2,3),
(null,3,1);
select * from order_list;
通过订单号查询订单内包含的商品信息的SQL语句:
# 通过订单编号查询订单内的商品详情
select * from orders where order_id = 1;
# 通过订单编号查询中间表中维护的订单与商品的信息,查询订单对应的商品的编号
select * from order_list where order_id = 1;
# 通过上一条语句的订单编号,从商品表查询商品的详细信息
select * from goods where good_id in (select order_list.good_id from order_list where order_id = 1);
查询结果:
最终就可以查询出1号订单包含的两个商品的详细信息
通过商品编号查询包含该商品的订单:
# 通过商品编号查询包含此商品的订单
select * from goods where good_id = 2;
# 通过商品编号从中间表查询包含该商品的订单的订单号
select * from order_list where good_id = 2;
# 通过上一条语句查询出的订单的编号,从订单表查询出包含此商品的订单的详细信息
select * from orders where order_id in (select order_id from order_list where good_id = 2);
查询结果:
最终就可以查询出包含1号商品的订单有三条
代码实现:
其实在这个章节,最难的部分就是SQL语句的编写,在我们完成了SQL语句的编写之后,剩下的就是根据我们之前学过的一对多的方式,确定我们的查询的顺序,以及我们类和表,属性与字段之间的关系这些细节之后,我们就可以开始代码和SQL映射文件的编写了。
我们首先使用嵌套查询的方式编写通过订单号查询订单包含的商品的详细信息:
SQL映射文件:
<?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="Mappers.more_to_more_select">
<!-- 配置多对多查询中的使用订单号查询订单所包含的商品信息-->
<!-- 首先是子查询-->
<select id="selectGoodsById" resultType="Goods" parameterType="int">
select * from goods where good_id in (select good_id from order_list where order_id = #{id})
</select>
<!-- 然后是配置父查询-->
<select id="selectOrdersById" parameterType="int" resultMap="OrdersMapper">
select * from orders where order_id = #{id}
</select>
<resultMap id="OrdersMapper" type="Orders">
<collection property="goodsList"
column="order_id"
ofType="Goods"
select="Mappers.more_to_more_select.selectGoodsById"
javaType="java.util.List"/>
</resultMap>
</mapper>
接口文件:
package Mappers;
import com.mybatis.POJO.Goods;
import com.mybatis.POJO.Orders;
import java.util.List;
public interface more_to_more_select {
public List<Goods> selectGoodsById(int i);
public List<Orders> selectOrdersById(int i);
}
测试类:
package Mappers;
import com.mybatis.POJO.Goods;
import com.mybatis.POJO.Orders;
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 org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
public class more_to_more_selectTest {
SqlSession session = null;
more_to_more_select mapper = null;
@Before
public void setUp() throws Exception {
InputStream stream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(stream);
session = build.openSession(true);
mapper = session.getMapper(more_to_more_select.class);
}
@Test
public void testSelectGoodsById() {
for (Goods goods : mapper.selectGoodsById(1)) {
System.out.println(goods.toString());
}
}
@Test
public void testSelectOrdersById() {
for (Orders orders : mapper.selectOrdersById(1)) {
System.out.println(orders.toString());
}
}
}
运行结果:
可以看到跟我们之前查询的结果是一样的,1号订单包含两个商品
注意点:
在这个案例中我们需要注意的就是中间表的创建,使用以及字段的作用,最后就是我们之前学习过的一对多查询的相关知识。
我们只演示了多对多查询中的使用嵌套查询的方式根据订单号查询订单包含的商品的详细信息,后面还有通过商品信息查询包含该商品的订单信息。以及使用嵌套结果集的方式进行查询等,有兴趣的可以自己尝试。