在实际开发中我们不可能只是对单表进行操作,必然要操作多表。本文就来讲解多表操作中的一对一关联映射和一对多(或者多对一)关联映射,至于多对多关联映射实质上也是两个一对多(或者多对一)关联映射,所以在这里我并不打算讲解。那就先从一对一关联映射讲起吧!温馨提示:本文案例代码的编写是建立在前文《MyBatis快速入门第五讲——Mapper.xml文件中的输入和输出映射以及动态SQL》案例基础之上的!
一对一关联映射
这里,我会以商品订单数据模型为例来讲解一对一关联映射,商品订单数据模型如下图所示。
一对一查询
有这样一个需求:查询所有订单信息时,关联查询下单的用户信息。注意:因为一个订单信息只会是一个人下的订单,所以从查询订单信息角度出发关联查询用户信息,这是一对一查询(值得注意的一点是这儿我将订单与用户的关系理解为一对多的关系,但实质上从订单角度出发,多个订单应属于同一个用户,故订单与用户的关系应该是多对一的关系);如果从用户信息角度出发查询该用户下的订单信息则是一对多查询,因为一个用户可以下多个订单。解决该需求,一共有两种方法,先讲第一种方法。
方法一:使用resultType
使用resultType,定义一个订单信息po类,此po类中包括了订单信息和用户信息。于是,我们首先要在com.meimeixia.mybatis.pojo包下新建一个OrderUser类。
package com.meimeixia.mybatis.pojo;
/**
* 订单关联用户信息的POJO
* @author liayun
*
*/
public class OrderUser extends Order {
private String username;// 用户姓名
private String address;// 地址
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;
}
@Override
public String toString() {
return "OrderUser [username=" + username + ", address=" + address + ", getId()=" + getId() + ", getUserId()="
+ getUserId() + ", getNumber()=" + getNumber() + ", getCreatetime()=" + getCreatetime() + ", getNote()="
+ getNote() + "]";
}
}
OrderUser类继承Order类之后,自然就包括了Order类中的所有字段,所以在该类中只需要定义用户的信息字段即可。
然后,按照需求编写SQL语句,在OrderMapper.xml映射文件中添加一个如下<select>
元素。
<!-- 一对一关联查询,resultType的使用,要想使用resultType,一定要与跟数据库关系一模一样的pojo -->
<select id="getOrderUser" resultType="orderuser">
SELECT
o.`id`,
o.`user_id` userId,
o.`number`,
o.`createtime`,
o.`note`,
u.username,
u.address
FROM
`order` o
LEFT JOIN `user` u ON u.id = o.user_id
</select>
接着,在OrderMapper接口中声明一个如下的方法。
/**
* 一对一关联查询,resultType的使用
* @return
*/
List<OrderUser> getOrderUser();
紧接着,在OrderMapperTest单元测试类中编写一个如下的测试方法。
@Test
public void testGetOrderUser() {
SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
//获取OrderMapper接口的代理实现类
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
List<OrderUser> list = orderMapper.getOrderUser();
for (OrderUser orderUser : list) {
System.out.println(orderUser);
}
sqlSession.close();
}
在以上方法中的for (OrderUser orderUser : list) {
这句代码处打一个断点,继而以Debug模式运行该方法,大概就能看到你想要的结果了。
小结
定义专门的po类作为输出类型,其中定义了SQL查询结果集所有的字段。此方法较为简单,企业中使用普遍。
方法二:使用resultMap
使用resultMap,定义专门的resultMap用于映射一对一查询结果。首先要在Order类中加入一个User类型的user属性,该属性用于存储关联查询的用户信息,因为订单关联查询用户是一对一关系,所以这里使用单个User对象存储关联查询的用户信息。如此一来,Order类的代码就要改成下面这个样子了。
package com.meimeixia.mybatis.pojo;
import java.util.Date;
public class Order {
private Integer id;
private Integer userId;
private String number;
private Date createtime;
private String note;
private User user;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number == null ? null : number.trim();
}
public Date getCreatetime() {
return createtime;
}
public void setCreatetime(Date createtime) {
this.createtime = createtime;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note == null ? null : note.trim();
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
@Override
public String toString() {
return "Order [id=" + id + ", userId=" + userId + ", number=" + number + ", createtime=" + createtime
+ ", note=" + note + "]";
}
}
然后,在OrderMapper.xml映射文件中添加一个如下的<select>
元素。
<select id="getOrderUserMap" resultMap="order_user_map">
SELECT
o.`id`,
o.`user_id`,
o.`number`,
o.`createtime`,
o.`note`,
u.`username`,
u.`address`,
u.`birthday`,
u.`sex`
FROM
`order` o
LEFT JOIN `user` u ON u.id = o.user_id
</select>
上面id为order_user_map的resultMap也须定义,如下所示:
<!-- 定义resultMap -->
<resultMap type="order" id="order_user_map">
<!-- <id>用于映射主键 -->
<id property="id" column="id" />
<!-- 普通字段用<result>来映射 -->
<result property="userId" column="user_id" />
<result property="number" column="number" />
<result property="createtime" column="createtime" />
<result property="note" column="note" />
<!--
<association>用于配置一对一关系
property:映射的是Order实体类里面的user属性
javaType:Order实体类里面的user属性的数据类型,可以写成别名
-->
<association property="user" javaType="com.meimeixia.mybatis.pojo.User">
<id property="id" column="user_id" />
<result property="username" column="username" />
<result property="address" column="address" />
<result property="birthday" column="birthday" />
<result property="sex" column="sex" />
</association>
</resultMap>
针对以上<association>
标签的配置,我觉得有必要说明如下几点:
- association:该标签表示进行关联查询单条记录,即可以使用它来配置一对一关联关系。
- property:该属性表示的是关联查询的结果要存储在Order实体类里面的user属性中。即它对应Order实体类里面一对一关联映射的那个属性(也就是user属性);
- javaType:该属性表示的是关联查询的结果类型,即user属性的数据类型,可使用别名;
<id property="id" column="user_id"/>
:意思是查询结果的user_id列对应关联对象的id属性,这儿的<id>
标签表示user_id是关联查询对象的唯一标识;<result property="username" column="username"/>
:意思是查询结果的username列对应关联对象的username属性。
OrderMapper.xml映射文件配置好之后,我们还要在OrderMapper接口中声明一个如下方法。
/**
* 一对一关联查询,resultMap的使用
* @return
*/
List<Order> getOrderUserMap();
最后,在OrderMapperTest单元测试类中编写一个如下测试方法。
@Test
public void testGetOrderUserMap() {
SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
//获取OrderMapper接口的代理实现类
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
List<Order> list = orderMapper.getOrderUserMap();
for (Order order : list) {
System.out.println(order);
System.out.println(" 此订单的用户为:" + order.getUser());
}
sqlSession.close();
}
运行以上单元测试方法,你便可以看到Eclipse控制台打印出了如下内容。
小结
我们可以使用association标签完成关联查询,并将关联查询信息映射到pojo对象中。
一对多关联映射
现有这样一个需求:查询所有用户信息及用户所关联的订单信息。这儿,用户信息和订单信息为一对多关系。此处,我会使用resultMap来解决这个需求。首先在User实体类中加入一个List<Orders> orders
属性,如此一来,User类的代码就要改成下面这个样子了。
package com.meimeixia.mybatis.pojo;
import java.util.Date;
import java.util.List;
public class User {
private Integer id; // int类型的id是不可能为null的!
private String username;// 用户姓名
private String sex;// 性别
private Date birthday;// 生日
private String address;// 地址
private String uuid2;
private List<Order> orders;
public List<Order> getOrders() {
return orders;
}
public void setOrders(List<Order> orders) {
this.orders = orders;
}
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 getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getUuid2() {
return uuid2;
}
public void setUuid2(String uuid2) {
this.uuid2 = uuid2;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", sex=" + sex + ", birthday=" + birthday + ", address="
+ address + ", uuid2=" + uuid2 + "]";
}
}
然后,在UserMapper.xml映射文件中添加一个如下的<select>
元素。
<select id="getUserOrderMap" resultMap="user_order_map">
SELECT
u.`id`,
u.`username`,
u.`birthday`,
u.`sex`,
u.`address`,
u.`uuid2`,
o.`id` oid,
o.`number`,
o.`createtime`,
o.`note`
FROM
`user` u
LEFT JOIN `order` o ON o.`user_id` = u.`id`
</select>
上面id为user_order_map的resultMap也须定义,如下所示:
<!-- 定义resultMap -->
<resultMap type="user" id="user_order_map">
<id property="id" column="id" />
<result property="username" column="username" />
<result property="address" column="address" />
<result property="birthday" column="birthday" />
<result property="sex" column="sex" />
<!--
<collection>用于配置一对多关联关系
property:映射的是User实体类里面的orders属性
ofType:User实体类里面的orders属性的数据类型,可以写成别名
-->
<collection property="orders" ofType="order">
<!-- <id>用于映射主键 -->
<id property="id" column="oid" />
<result property="userId" column="id" />
<result property="number" column="number" />
<result property="createtime" column="createtime" />
<result property="note" column="note" />
</collection>
</resultMap>
针对以上<collection>
标签的配置,我觉得有必要说明如下几点:
<collection>
标签部分定义了用户关联的订单信息,即它表示关联查询结果集。- property:该属性表示关联查询的结果集存储在User对象的哪个属性上,也就是说它对应的是User对象中的集合属性;
- ofType:该属性指定关联查询的结果集中的对象类型(即List集合中的元素类型)。此处可以使用别名,也可以使用全限定名;
<id>
标签及<result>
标签的意义同一对一查询。
UserMapper.xml映射文件配置好之后,我们还要在UserMapper接口中声明一个如下方法。
/**
* 演示一对多的关联查询,使用resultMap
* @return
*/
List<User> getUserOrderMap();
最后,在UserMapperTest单元测试类中编写一个如下测试方法。
@Test
public void testGetUserOrderMap() {
SqlSession sqlSession = SqlSessionFactoryUtils.getSqlSessionFactory().openSession();
//获取接口的代理实现类,只不过不需要我们去写了
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> list = userMapper.getUserOrderMap();
for (User user : list) {
System.out.println(user);
for (Order order : user.getOrders()) {
if (order.getId() != null) {
System.out.println(" 此用户下的订单有:" + order);
}
}
}
sqlSession.close();
}
运行以上单元测试方法,你便可以看到Eclipse控制台打印出了如下内容。
至此,一对一关联映射和一对多关联映射我就已经总结完了,觉得总结得还蛮走心的。