目录
一、多表映射概念
1.1 多表查询结果映射思路
数据库的表结构具有复杂性,不是所有数据库都达到第三范式或BCNF范式,故数据库查询结果与java对象的属性映射也变得复杂。MyBatis使用ResultMap实现复杂的数据库映射模式。示例如下:
这是一个需要映射的select语句:
<!-- 非常复杂的语句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
select
B.id as blog_id,
B.title as blog_title,
B.author_id as blog_author_id,
A.id as author_id,
A.username as author_username,
A.password as author_password,
A.email as author_email,
A.bio as author_bio,
A.favourite_section as author_favourite_section,
P.id as post_id,
P.blog_id as post_blog_id,
P.author_id as post_author_id,
P.created_on as post_created_on,
P.section as post_section,
P.subject as post_subject,
P.draft as draft,
P.body as post_body,
C.id as comment_id,
C.post_id as comment_post_id,
C.name as comment_name,
C.comment as comment_text,
T.id as tag_id,
T.name as tag_name
from Blog B
left outer join Author A on B.author_id = A.id
left outer join Post P on B.id = P.blog_id
left outer join Comment C on P.id = C.post_id
left outer join Post_Tag PT on PT.post_id = P.id
left outer join Tag T on PT.tag_id = T.id
where B.id = #{id}
</select>
该语句具体思想如下:B对象标识一篇博客,由某位作者A编写P,每篇博文由很多评论C和标签PT、T。
我们需要设置ResultMap作为select标签的返回值类型,实现返回值的复杂映射。resultMap编写如下:
<!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
</collection>
</resultMap>
1.2 实体类设计方案
不同表格时间有一对一、一对多、多对多的多表关系。不同表格之间的实体类有不同的设计方案。
1.2.1 对一关系设计
外卖订单与用户是多对一关系。对一关系下,实体类中只要包含单个对方对象类型属性即可。属性中包含对方对象。
public class Customer {
private Integer customerId;
private String customerName;
}
public class Order {
private Integer orderId;
private String orderName;
private Customer customer;// 体现的是对一的关系
}
1.2.2 对多关系设计
用户与订单时一对多关系。对多关系下,实体类中只要包含对方类型集合属性即可。属性中包含对方对象集合。
public class Customer {
private Integer customerId;
private String customerName;
private List<Order> orderList;// 体现的是对多的关系
}
public class Order {
private Integer orderId;
private String orderName;
private Customer customer;// 体现的是对一的关系
}
//查询客户和客户对应的订单集合 不要管!
只有真实发生多表查询时,才需要设计和修改实体类,否则不提前设计和修改实体类!
无论多少张表联查,实体类设计都是两两考虑!
在查询映射的时候,只需要关注本次查询相关的属性!例如:查询订单和对应的客户,就不要关注客户中的订单集合!
多表映射案例准备
为后续继续学习多表映射关系,请先完成如下准备:
数据库:
CREATE TABLE `t_customer` (`customer_id` INT NOT NULL AUTO_INCREMENT, `customer_name` CHAR(100), PRIMARY KEY (`customer_id`) );
CREATE TABLE `t_order` ( `order_id` INT NOT NULL AUTO_INCREMENT, `order_name` CHAR(100), `customer_id` INT, PRIMARY KEY (`order_id`) );
INSERT INTO `t_customer` (`customer_name`) VALUES ('c01');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o1', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o2', '1');
INSERT INTO `t_order` (`order_name`, `customer_id`) VALUES ('o3', '1');
实体类设计:
创建新的Module:mybatis-high-multitable-05
pojo包下创建:
@Data
public class Customer {
private Integer customerId;
private String customerName;
private List<Order> orderList;// 体现的是对多的关系
}
@Data
public class Order {
private Integer orderId;
private String orderName;
private Customer customer;// 体现的是对一的关系
}
二、对一映射
查询需求:根据orderId查询订单以及订单关联的用户的信息!
1. 配置Mapper接口并声明查询方法:
public interface OrderMapper {
Order selectOrderWithCustomer(Integer orderId);
}
2. 实现Mapper映射文件:
<!-- 创建resultMap实现“对一”关联关系映射 -->
<!-- id属性:通常设置为这个resultMap所服务的那条SQL语句的id加上“ResultMap” -->
<!-- type属性:要设置为这个resultMap所服务的那条SQL语句最终要返回的类型 -->
<resultMap id="selectOrderWithCustomerResultMap" type="order">
<!-- 先设置Order自身属性和字段的对应关系 -->
<id column="order_id" property="orderId"/>
<result column="order_name" property="orderName"/>
<!-- 使用association标签配置“对一”关联关系 -->
<!-- property属性:在Order类中对一的一端进行引用时使用的属性名 -->
<!-- javaType属性:一的一端类的全类名 -->
<association property="customer" javaType="customer">
<!-- 配置Customer类的属性和字段名之间的对应关系 -->
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
</association>
</resultMap>
<!-- Order selectOrderWithCustomer(Integer orderId); -->
<select id="selectOrderWithCustomer" resultMap="selectOrderWithCustomerResultMap">
SELECT order_id,order_name,c.customer_id,customer_name
FROM t_order o
LEFT JOIN t_customer c
ON o.customer_id=c.customer_id
WHERE o.order_id=#{orderId}
</select>
核心标签:
<assotiation>:配置对一关系的对象,将对一关系对象的属性赋值。
property属性:对一对象在原对象中的属性名
javaType属性:对一对象对应的java类名
标签中声明对一对象的字段->属性对应关系
</association>
3. 注册Mapper映射文件
<!-- 注册Mapper配置文件:告诉Mybatis我们的Mapper配置文件的位置 -->
<mappers>
<!-- 在mapper标签的resource属性中指定Mapper配置文件以“类路径根目录”为基准的相对路径 -->
<mapper resource="mappers/OrderMapper.xml"/>
</mappers>
三、对多映射
查询需求:根据customer_id查询客户和客户关联的订单信息!
1. 定义相关Mapper接口并声明方法
public interface CustomerMapper {
Customer selectCustomerWithOrderList(Integer customerId);
}
2. 配置相关的Mapper映射文件
<!-- 配置resultMap实现从Customer到OrderList的“对多”关联关系 -->
<resultMap id="selectCustomerWithOrderListResultMap"
type="customer">
<!-- 映射Customer本身的属性 -->
<id column="customer_id" property="customerId"/>
<result column="customer_name" property="customerName"/>
<!-- collection标签:映射“对多”的关联关系 -->
<!-- property属性:在Customer类中,关联“多”的一端的属性名 -->
<!-- ofType属性:集合属性中元素的类型 -->
<collection property="orderList" ofType="order">
<!-- 映射Order的属性 -->
<id column="order_id" property="orderId"/>
<result column="order_name" property="orderName"/>
</collection>
</resultMap>
<!-- Customer selectCustomerWithOrderList(Integer customerId); -->
<select id="selectCustomerWithOrderList" resultMap="selectCustomerWithOrderListResultMap">
SELECT c.customer_id,c.customer_name,o.order_id,o.order_name
FROM t_customer c
LEFT JOIN t_order o
ON c.customer_id=o.customer_id
WHERE c.customer_id=#{customerId}
</select>
核心标签:
<collection>:配置对多对象,将对多对象的属性赋值
property属性:对多对象在原对象中的属性名
ofType:集合属性中元素的java类名
</collection>
3. 注册Mapper映射文件
<!-- 注册Mapper配置文件:告诉Mybatis我们的Mapper配置文件的位置 -->
<mappers>
<!-- 在mapper标签的resource属性中指定Mapper配置文件以“类路径根目录”为基准的相对路径 -->
<mapper resource="mappers/OrderMapper.xml"/>
<mapper resource="mappers/CustomerMapper.xml"/>
</mappers>
四、多表映射总结
4.1 多表映射优化
<setting name="autoMappingBehavior">标签:
指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。
进行多表resultMap映射的时候,可以省略符合列和属性命名映射规则(列名=属性名,或者开启驼峰映射也可以自定映射)的result标签!
举例:
<!--开启resultMap自动映射 -->
<setting name="autoMappingBehavior" value="FULL"/>
<resultMap id="teacherMap" type="teacher">
<id property="tId" column="t_id" />
<!-- 开启自动映射,并且开启驼峰式支持!可以省略 result!-->
<!-- <result property="tName" column="t_name" />-->
<collection property="students" ofType="student" >
<id property="sId" column="s_id" />
<!-- <result property="sName" column="s_name" />-->
</collection>
</resultMap>
4.2 Mapper批量映射优化
多个Mapper需要映射时,在config文件中一个一个配置较为繁琐,MyBatis允许通过指定Mapper所在的包在指定Mapper映射文件。
创建要求:
Mapper接口和Mapper配置文件名必须保持一致,如:
Mapper Interface:EmployeeMapper.java
Mapper.xml:EmployeeMapper.xml
Mapper配置文件必须放在Mapper接口所在的包内:
1. 将Mapper配置文件和Mapper接口放在同一个package内,但是需要额外配置Maven打包方式,较为复杂
2. 在resources文件夹下创建于mapper接口package路径一致的文件目录结构来存放Mapper配置文件,如:
- Mapper接口放在com.landy.mapper包中
- 在resources文件夹中创建com/landy/mapper文件路径,在mapper文件夹中存放相关的mapper.xml文件
随后,在config文件中配置相关包,即可编译成功:
<!-- 配置映射文件信息 -->
<mappers>
<package name="com.landy.mapper"/>
</mappers>
4.3 总结:
对一:association + property(属性名) + javaType(返回实体类型)
对多:collection + property(属性名) + ofType(返回实体类型)