MyBatis学习(四):一对多、多对一、多对多

在JAVA实体类中,类与类的一对多、多对一、多对多关系通过组合的形式实现;
在数据库中,表之间的一对多、多对一、多对多的关系通过使用外键来实现;

一对多、多对一、多对多关系的查询都通过多表查询完成。

其中多对多的关系都需要通过一个中间类/表来实现,比如产品和订单之间,一个订单中可以有多个产品,一个产品也可以存在与多个订单之中,这种情况就称为多对多的关系,要建立两者之间的连接,需要建立一个中间类/表(订单项),一个订单项实例包含自身属性和产品属性、订单属性。

MyBatis一对多、多对一关系的实现

一对多关系和多对一关系实质上是两个实体类(数据库表之间的关系),比如一个分类中包含多个产品,那么分类和产品之间就是一对多的关系;反过来想,就会有多个产品对应一个分类(假如产品只属于一个分类),这就是一对多的关系和多对一的关系。
一对多
那么在JAVA类层面和数据库层面怎么实现这种关系呢?对于JAVA类层面,分类中含有多种产品,那么在代表分类的JAVA类中添加一个产品类的List对象;对于数据库层面,在一对多中对应的“多”的表中(产品表)增加对应的“一”的主键列作为外键,如此便建立了一对多的关系。

如果只是查询一对多的关系,只通过一查多,配置已经完成,我们可以去简历XML映射文件添加查询语句,但是此处要注意,因为是数据库表之间是通过外键建立联系,只是通过一张表无法查询到对应关系的全部信息,因此只能通过多表查询来完成。

除此之外,我们还要注意到,MyBatis的工作方式是将查询到的字段和返回值类型中的属性进行匹配,要求实体类属性和表列名必须一致(可以定义这种一致性,在总配置文件中可以选择不同的属性和列的匹配方式),现在,多表查询,如果两个表中出现了重复的列呢?两个表中的所有列,如何才能和一个实体类进行匹配呢(包含另一个实体类,但是存储的是类对象,SQL查询结果都为属性列)?这里就需要我们自己定义resultMap对象,用于匹配查询结果和类中的属性。以上述分类和产品为例,定义JAVA类如下:

public class Category {
	private int id;
	private String name;
	List<Product> products;
	//省略get set方法
}

定义产品类如下:

public class Product {
	private int id;
	private String name;
	private float price;
}

数据库表结构如下:(分别为分类表和产品表)
在这里插入图片描述在这里插入图片描述
与分类的实体类相映射的XML文件中查询分类所定义的产品:
注意:所有的属性都需要配置!!!即使不重名的,否则查询结果不会导入属性中
只有在SQL语句中不查询的列可以不配置(不需要的列信息)

 <!-- 一对多查询  -->
        <!--  配置resultMap
        type指示此返回结果的类型
        id为本个resultMap的标识 用于在查询标签的resultMap属性中使用
        id为主键元素  result为非主键元素
        column行指代的为查询结果表中生成的列名称(在SQL语句中使用的列别名) 
        property为java实体类中的字段值 -->
        <resultMap type="Category" id="categoryBean">
        	<id column="cid" property="id" />
        	<result column='cname' property="name"/>
        	<!-- 一对多的关系  collection配置分类 的实体类中的产品属性-->
            <!-- property: 指的是集合属性的值, ofType:指的是集合中元素的类型 -->
            <collection property="products" ofType="Product">
            	<id column="pid" property="id"/>
            	<result column="pname" property="name"/>
            	<result column="price" property="price"/>
            </collection>
        </resultMap>
  <!--查询语句如下-->
          <select id="listCategory" resultMap="categoryBean">
        	select c.id as cid,c.name as cname,p.id as pid,p.name as pname,p.price from category as c left join product as p on c.id = p.id
        </select>

多对一
在JAVA类中如何配置多对一的关系呢?多对一就是多个产品拥有共同的分类,但这并不影响他们自己拥有分类,因此只需要在产品类中添加分类对象即可。至于在数据库的层面,我们知道一对多的关系中已经通过外键将两张表联系起来,因此并不需要添加其他表列。

现在,产品类中也多出了类属性,在查询时MybBatis不能将连接查询的表列自动匹配给实体类属性的属性,同样需要配置resultMap

修改后Product类如下:

public class Product {
	private int id;
	private String name;
	private float price;
	private Category category;
}

配置与产品类相对应的XML映射文件如下:

<!--   多对一关系展示     -->
    	<resultMap type="Product" id="productBean">
    		<id column="pid" property="id"/>
    		<result column="pname" property="name"/>
    		<!-- 即使列名和属性名相同 也要配置 否则resultMap里回缺少此项的值 而是默认值 -->
    		<result column="price" property="price"/>
    		<!-- 多对一的关系  使用association-->
    		 <!-- property: 指的是属性名称, javaType:指的是属性的类型 -->
			<association property="category" javaType="Category">
				<id column="cid" property="id"/>
				<result column="cname" property="name"/>
			</association>    	
    	</resultMap>
    	<!--  select 查询语句   -->
    	<select id="listProduct" resultMap="productBean">
    		select p.id as pid,p.name as pname,p.price,c.id as cid,c.name as cname from product as p,category as c where p.cid=c.id
    	</select>

关于多对多关系为什么要创建中间项?

从类的角度理解为,一个订单类包括多个产品类,而一个产品类又包括多个订单类,enmmm好像没有什么不好,有没有大佬解答下。
从数据库表的角度理解,可以减少信息的冗余,产品里面只存储所有的产品,订单类只存储所有的订单,在订单中没有产品ID,在产品表中没有订单ID, 产品和订单之间的联系通过订单项建立(在一对多和多对一的关系中,我们通过在对应的“多”表中添加了对应的“一”表列)。
这也是数据库设计上要求的!!!
多对多的关系分解为两个一对多、多对一的关系,一个订单对应多个订单项,一个订单项对应一个订单、一个产品,一个产品对应多个订单项。

建立如下java类:

public class Order {
	private int id;
	private String code;
	private List<OrderItem> orderItems;
}
public class OrderItem {
	private int id;
	private int number;
	private Order order;
	private Product product;
}

数据库表如下(product、orderItem、order):
在这里插入图片描述在这里插入图片描述在这里插入图片描述
通过订单查询订单中的产品(实质为查询订单类属性信息,因此需要配置与订单类重名的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.cn.bean">
    <!--  多对多关系演示 -->
    <!-- type返回的对象类型 id命名标识 -->
    	<resultMap type="Order" id="orderBean">
    		<!-- Order类的非类属性 -->
    		<!-- Order表主键 column:查询结果中取的列别名 property:对应Order类中的信息-->
    		<id column="oid" property="id"/>
    		<!-- 没有取别名的属性也需要进行配置 -->
    		<result column="code" property="code"/>
    		<!-- 对应多个订单项 使用collection-->
    		<!-- Order类中属性  属性所对应的类型 -->
    		<collection property="orderItems" ofType="OrderItem">
    			<!-- 可以不配置不需要的列 -->
    			<!-- OrderItem中没有什么有用的信息 比如id信息对于我们来说意义不大  不配置此列 -->
    			<id column="oiid" property="id"/>
    			<!--!!!!不配置此列会出现查询结果不全的情况 原因未知!!!!!! -->
    			<result column="number" property="number"/><!-- 产品数量 -->
    			<!-- OrderItem和Product的一对多关系 -->
    			<association property="product" javaType="Product">
    				<id column="pid" property="id"/>
    				<result column="name" property="name"/>
    				<result column="price" property="price"/>
    			</association>
    		</collection>
    	</resultMap>
    	<!-- 查询所有的订单 -->
    	<select id="listOrder" resultMap="orderBean">
    		select o.id as oid,o.code,oi.number,p.id as pid,p.name,p.price from `order` as o left join orderItem as oi on o.id=oi.oid inner join product as p on oi.pid=p.id
    	</select>
    </mapper>

前面说不查询的列可以不配置,但是多对多的关系中,中间表的主键项如果不配置的话,就会出现查询结果部分丢失的情况,原因未知。
并不是主键必须配置,如果删除掉resultMap中产品表中关于id的配置,也能够成功查询,可能是因为中间表是个特例?等一个大佬。。。
在这里插入图片描述

最后,一个可有可无的提醒,千万记得将映射文件加入mybatis的总配置文件中0.0
思考一个问题,映射文件必须与实体类重名吗?不一定
映射文件中各种标签的id可以重名吗?不可以
如果可以的话根据传入参数的不同会不会是重载呢?不会

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值