一、一对一关系映射
使用resultType+包装类实现
1、假设问题背景是要求在某一个购物平台的后台程序中添加一个这样的功能:查询某个订单的信息和下该订单的用户信息。首先我们可以知道,一般这样的平台上面,某一笔订单只属于某一个用户,从这个角度来看,可以作为一对一的参考模型
①首先创建数据表user(用户表)
CREATE TABLE `user` (
`uid` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(255) DEFAULT NULL,
`password` VARCHAR(255) DEFAULT NULL,
`sex` VARCHAR(255) DEFAULT NULL,
PRIMARY KEY (`uid`)
) ENGINE=INNODB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8创建orders表(所用的订单表)
CREATE TABLE `orders` (
`oid` INT(11) NOT NULL AUTO_INCREMENT,
`total` DOUBLE DEFAULT NULL,
`ordertime` DATETIME DEFAULT NULL,
`name` VARCHAR(20) DEFAULT NULL,
`uid` INT(11) DEFAULT NULL,
PRIMARY KEY (`oid`),
KEY `FKC3DF62E5AA3D9C7` (`uid`),
CONSTRAINT `FKC3DF62E5AA3D9C7` FOREIGN KEY (`uid`) REFERENCES `user` (`uid`)
) ENGINE=INNODB AUTO_INCREMENT=9004 DEFAULT CHARSET=utf8如下图所示:
②创建User和Order的实体类
1 package cn.mybatis.po;
2
3 public class User {
4 private int uid;
5 private String username;
6 private String password;
7 private String address;
8 private String sex;
9
10 public int getUid() {
11 return uid;
12 }
13
14 public void setUid(int uid) {
15 this.uid = uid;
16 }
17
18 public String getUsername() {
19 return username;
20 }
21
22 public String getPassword() {
23 return password;
24 }
25
26 public String getAddress() {
27 return address;
28 }
29
30 public String getSex() {
31 return sex;
32 }
33
34 public void setUsername(String username) {
35 this.username = username;
36 }
37
38 public void setPassword(String password) {
39 this.password = password;
40 }
41
42 public void setAddress(String address) {
43 this.address = address;
44 }
45
46 public void setSex(String sex) {
47 this.sex = sex;
48 }
49
50 public User(String username, String password, String address, String sex) {
51 this.username = username;
52 this.password = password;
53 this.address = address;
54 this.sex = sex;
55 }
56
57 public User() {
58 }
59
60 @Override
61 public String toString() {
62 return "User{" +
63 "uid=" + uid +
64 ", username='" + username + '\'' +
65 ", password='" + password + '\'' +
66 ", address='" + address + '\'' +
67 ", sex='" + sex + '\'' +
68 '}';
69 }
70 } 1 package cn.mybatis.po;
2
3 import java.util.Date;
4
5 public class Order {
6
7 private int oid;
8 private double total;
9 private Date ordettime;
10 private String name;
11
12 public int getOid() {
13 return oid;
14 }
15
16 public void setOid(int oid) {
17 this.oid = oid;
18 }
19
20 public double getTotal() {
21 return total;
22 }
23
24 public void setTotal(double total) {
25 this.total = total;
26 }
27
28 public Date getOrdettime() {
29 return ordettime;
30 }
31
32 public void setOrdettime(Date ordettime) {
33 this.ordettime = ordettime;
34 }
35
36 public String getName() {
37 return name;
38 }
39
40 public void setName(String name) {
41 this.name = name;
42 }
43
44 @Override
45 public String toString() {
46 return "Order{" +
47 "oid=" + oid +
48 ", total=" + total +
49 ", ordettime=" + ordettime +
50 ", name='" + name + '\'' +
51 '}';
52 }
53 }
③用于需要同时查询User和Order的信息,所以需要用到上一篇中讲到的POJO输出映射类型。具体来说就是,需要自定义一个OrderPoJo,其中包含我们要查询的Order和User信息,定义的OrderPoJo类型如下(这里我们可以使用继承的方式,如果我们查询结果中哪一个类的要查询结果多就继承该类,可以简便包装类的编写)
1 package cn.mybatis.po;
2
3 public class OrderPoJo extends Order{
4
5 private String username;
6 private String address;
7 private String sex;
8
9 public String getUsername() {
10 return username;
11 }
12
13 public void setUsername(String username) {
14 this.username = username;
15 }
16
17 public String getAddress() {
18 return address;
19 }
20
21 public void setAddress(String address) {
22 this.address = address;
23 }
24
25 public String getSex() {
26 return sex;
27 }
28
29 public void setSex(String sex) {
30 this.sex = sex;
31 }
32
33 @Override
34 public String toString() {
35 return "OrderPoJo{" +
36 super.toString() +
37 "username='" + username + '\'' +
38 ", address='" + address + '\'' +
39 ", sex='" + sex + '\'' +
40 '}';
41 }
42 }④编写好对应的实体类和包装类之后就开始写,mapper配置文件和mapper.java。mapper配置文件中我们只需要根据问题背景写好相应的Sql语句就好
这是编写好Sql的Mapper配置文件
1 <?xml version="1.0" encoding="UTF-8" ?>
2 <!DOCTYPE mapper
3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
5 <!--mapper为根元素,namespace指定了命名空间-->
6 <mapper namespace="cn.mybatis.mapper.OrderMapper">
7
8 <select id="findOrderAndUser" parameterType="int" resultType="orderPoJo">
9 SELECT orders.*,
10 user.username,user.sex,user.address
11 FROM orders,USER
12 WHERE oid = #{id} AND user.uid = orders.uid
13 </select>
14
15 </mapper>下面是mapper接口中的一个方法,由于我们只需要完成这一个问题,所以OrderMapper接口也比较简单
⑤编写好所有的文件后,使用Junit来测试文件
1 package cn.mybatis.mapper;
2
3
4 import cn.mybatis.po.OrderPoJo;
5 import org.apache.ibatis.io.Resources;
6 import org.apache.ibatis.session.SqlSession;
7 import org.apache.ibatis.session.SqlSessionFactory;
8 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
9 import org.junit.Before;
10 import org.junit.Test;
11
12 import java.io.InputStream;
13
14 public class OrderMapperTest {
15
16 private SqlSessionFactory sqlSessionFactory;
17
18 @Before
19 public void setUp() throws Exception {
20 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
21 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
22 }
23
24 @Test
25 public void testFindOrderAndUser() throws Exception {
26 SqlSession sqlSession = sqlSessionFactory.openSession();
27 OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
28
29 OrderPoJo orderPoJo = orderMapper.findOrderAndUser(3);
30
31 System.out.println(orderPoJo);
32
33 sqlSession.close();
34
35 }
36
37 }⑥我们来通过日志分析一下结果:最终能够按照既定的Sql查询出响应的结果
使用resultMap在配置文件中实现
1、我们使用resultMap来进行测试的时候,首先需要注意的是,由于没有自定义包装类型,所以需要在原始的Order中添加User类型的属性,保证可以在Mapper配置文件中将查询到的User属性配置到user中,从而得到关联查询结果
1 package cn.mybatis.po;
2
3 import java.util.Date;
4
5 public class Order {
6
7 private int oid;
8 private double total;
9 private Date ordertime;
10 private String name;
11 private User user;
12
13 public int getOid() {
14 return oid;
15 }
16
17 public void setOid(int oid) {
18 this.oid = oid;
19 }
20
21 public double getTotal() {
22 return total;
23 }
24
25 public void setTotal(double total) {
26 this.total = total;
27 }
28
29 public Date getOrdertime() {
30 return ordertime;
31 }
32
33 public void setOrdertime(Date ordertime) {
34 this.ordertime = ordertime;
35 }
36
37 public String getName() {
38 return name;
39 }
40
41 public User getUser() {
42 return user;
43 }
44
45 public void setUser(User user) {
46 this.user = user;
47 }
48
49 public void setName(String name) {
50 this.name = name;
51 }
52
53 @Override
54 public String toString() {
55 return "Order{" +
56 "oid=" + oid +
57 ", total=" + total +
58 ", ordertime=" + ordertime +
59 ", name='" + name + '\'' +
60 ", user=" + user +
61 '}';
62 }
63 }2、相关类和上面的内容一样,我们使用resultMap来实现,显然需要在Mapper中配置resultMap
1 <!--
2 订单关联User的查询resultMap
3 将查询的结果全部映射到Order类中
4 -->
5 <resultMap id="OrderResultMap" type="cn.mybatis.po.Order">
6 <!--配置映射订单-->
7 <id column="oid" property="oid"></id>
8 <result column="total" property="total"></result>
9 <result column="ordertime" property="ordertime"></result>
10 <result column="name" property="name"></result>
11
12 <!--配置关联用户信息-->
13 <!--
14 association:用于映射关联查询单个对象的信息
15 property:用于设置将关联信息映射到Order的哪个属性中
16 -->
17 <association property="user" javaType="cn.mybatis.po.User">
18 <id column="uid" property="uid"></id>
19 <result column="username" property="username"></result>
20 <result column="address" property="address"></result>
21 <result column="sex" property="sex"></result>
22 </association>
23 </resultMap>3、然后在Mapper配置文件中使用resultMap类型的statment
<select id="findOrderAndUserByResultMap" parameterType="int" resultMap="OrderResultMap">
SELECT orders.*,
user.username,user.sex,user.address
FROM orders,USER
WHERE oid = #{id} AND user.uid = orders.uid
</select>4、结果同使用resultType的结果一样
二、一对多关系映射
1、我们先分析一下orderitem和orders两张数据表的关系,我们能够想到一条订单中包含许多详细的订单条目信息,所以简单的得到下面的关系
2、在上面的基础上,我们再创建一张orderitem数据表
CREATE TABLE `orderitem` (
`itemid` INT(11) NOT NULL AUTO_INCREMENT,
`count` INT(11) DEFAULT NULL,
`subtotal` DOUBLE DEFAULT NULL,
`pid` INT(11) DEFAULT NULL,
`oid` INT(11) DEFAULT NULL,
PRIMARY KEY (`itemid`),
KEY (`oid`),
KEY (`pid`),
KEY (`oid`),
FOREIGN KEY (`oid`) REFERENCES `orders` (`oid`),
FOREIGN KEY (`pid`) REFERENCES `product` (`pid`)
) ENGINE=INNODB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8
3、我们再创建相应的实体类orderitem
1 package cn.mybatis.po;
2
3 public class OrderItem {
4
5 private int itemid;
6 private int count;
7 private double subtotal;
8 private int pid;
9
10 public int getItemid() {
11 return itemid;
12 }
13
14 public void setItemid(int itemid) {
15 this.itemid = itemid;
16 }
17
18 public int getCount() {
19 return count;
20 }
21
22 public void setCount(int count) {
23 this.count = count;
24 }
25
26 public double getSubtotal() {
27 return subtotal;
28 }
29
30 public void setSubtotal(double subtotal) {
31 this.subtotal = subtotal;
32 }
33
34 public int getPid() {
35 return pid;
36 }
37
38 public void setPid(int pid) {
39 this.pid = pid;
40 }
41
42 @Override
43 public String toString() {
44 return "OrderItem{" +
45 "itemid=" + itemid +
46 ", count=" + count +
47 ", subtotal=" + subtotal +
48 ", pid=" + pid +
49 '}';
50 }
51 }
4、类似于上面讲到的使用resultMap的方式,我们在Order中添加上orderitem这一属性,作用就是维护Order和Orderitem的关联关系
1 package cn.mybatis.po;
2
3 import java.util.Date;
4 import java.util.List;
5
6 public class Order {
7
8 private int oid;
9 private double total;
10 private Date ordertime;
11 private String name;
12 private User user;
13
14 private List<OrderItem> orderItems;
15
16 public int getOid() {
17 return oid;
18 }
19
20 public void setOid(int oid) {
21 this.oid = oid;
22 }
23
24 public double getTotal() {
25 return total;
26 }
27
28 public void setTotal(double total) {
29 this.total = total;
30 }
31
32 public Date getOrdertime() {
33 return ordertime;
34 }
35
36 public void setOrdertime(Date ordertime) {
37 this.ordertime = ordertime;
38 }
39
40 public String getName() {
41 return name;
42 }
43
44 public User getUser() {
45 return user;
46 }
47
48 public void setUser(User user) {
49 this.user = user;
50 }
51
52 public void setName(String name) {
53 this.name = name;
54 }
55
56 public List<OrderItem> getOrderItems() {
57 return orderItems;
58 }
59
60 public void setOrderItems(List<OrderItem> orderItems) {
61 this.orderItems = orderItems;
62 }
63
64 @Override
65 public String toString() {
66 return "Order{" +
67 "oid=" + oid +
68 ", total=" + total +
69 ", ordertime=" + ordertime +
70 ", name='" + name + '\'' +
71 ", user=" + user +
72 ", orderItems=" + orderItems +
73 '}';
74 }
75 }
5、我们先配置Mapper文件。使用collection配置实体类中的List属性(List<OrderItem>)
<!--查询Order和OrderItem的配置(一对多关系查询配置)-->
<resultMap id="OrderAndOrderItemByResultMap" type="cn.mybatis.po.Order" extends="OrderResultMap">
<!--使用extends,可以将某一段resultMap继承过来-->
<!--OrderItem信息-->
<!--collections:将查询到的多条信息映射到集合中
property:将查询到的多条记录映射到Order中的相应属性中(orderItems)
ofType:指的是要映射的集合中的JavaType-->
<collection property="orderItems" ofType="cn.mybatis.po.OrderItem">
<id column="itemid" property="itemid"></id>
<result column="count" property="count"></result>
<result column="subtotal" property="subtotal"></result>
</collection>
</resultMap>
6、然后将上面配置的resultMap加入到statment中
<select id="findOrderAndOrderItemByResultMap" parameterType="int" resultMap="OrderAndOrderItemByResultMap">
SELECT orders.*,
user.username,
user.sex,
user.address,
orderitem.itemid,
orderitem.count,
orderitem.subtotal
FROM
orders,
USER,
orderitem
WHERE orders.oid = #{id} AND user.uid = orders.uid AND orderitem.oid = orders.oid
</select>7、然后在Mapper接口中添加测试方法
//一对多关系测试
public Order findOrderAndOrderItemByResultMap(int id) throws Exception;8、将查询的结果进行输出可以发现能够正常查询出想要的结果
Order{oid=2, total=32.0, ordertime=Thu Dec 26 21:47:04 CST 2019, name='Lucy', user=User{uid=2, username='Rose', password='null', address='武汉市', sex='women'}, orderItems=[OrderItem{itemid=2, count=21, subtotal=32.0, pid=0}, OrderItem{itemid=4, count=32, subtotal=54.0, pid=0}]}
三、多对多关系映射
1、问题背景就是查询User所购买的商品详细信息,即查询结果包括User信息和Product信息。我们先分析一下整个数据表之间的关系如下图所示
2、在上面分析的基础上,我们创建Product数据表的对应的Product实体类
CREATE TABLE `product` (
`pid` INT(11) NOT NULL AUTO_INCREMENT,
`pname` VARCHAR(255) DEFAULT NULL,
`shop_price` DOUBLE DEFAULT NULL,
PRIMARY KEY (`pid`)
) ENGINE=INNODB AUTO_INCREMENT=80 DEFAULT CHARSET=utf8 1 package cn.mybatis.po;
2
3 public class Product {
4 private int pid;
5 private String pname;
6 private double shop_price;
7
8 public int getPid() {
9 return pid;
10 }
11
12 public void setPid(int pid) {
13 this.pid = pid;
14 }
15
16 public String getPname() {
17 return pname;
18 }
19
20 public void setPname(String pname) {
21 this.pname = pname;
22 }
23
24 public double getShop_price() {
25 return shop_price;
26 }
27
28 public void setShop_price(double shop_price) {
29 this.shop_price = shop_price;
30 }
31
32 @Override
33 public String toString() {
34 return "Product{" +
35 "pid=" + pid +
36 ", pname='" + pname + '\'' +
37 ", shop_price=" + shop_price +
38 '}';
39 }
40 }
3、一般编写Mapper配置文件可以使用resultMap或者resultType(+自定义扩展类型)来实现,这里,我们使用resultMap在Mapper配置文件中进行,所以需要在OrderItem类中添加Product类型的属性,修改后的OrderItem类如下
1 package cn.mybatis.po;
2
3 public class OrderItem {
4
5 private int itemid;
6 private int count;
7 private double subtotal;
8
9 private Product product;
10
11 public int getItemid() {
12 return itemid;
13 }
14
15 public void setItemid(int itemid) {
16 this.itemid = itemid;
17 }
18
19 public int getCount() {
20 return count;
21 }
22
23 public void setCount(int count) {
24 this.count = count;
25 }
26
27 public double getSubtotal() {
28 return subtotal;
29 }
30
31 public void setSubtotal(double subtotal) {
32 this.subtotal = subtotal;
33 }
34
35 public Product getProduct() {
36 return product;
37 }
38
39 public void setProduct(Product product) {
40 this.product = product;
41 }
42
43 @Override
44 public String toString() {
45 return "OrderItem{" +
46 "itemid=" + itemid +
47 ", count=" + count +
48 ", subtotal=" + subtotal +
49 ", product=" + product +
50 '}';
51 }
52 }4、修改相应的实体类后,可以再Mapper配置文件中配置查询结果User信息以及关联的Order、OrderItem和所要的Product信息,配置如下
<!--查询User和Product关系结果(多对多关系)-->
<resultMap id="UserAndProductResultMap" type="cn.mybatis.po.User">
<!--User信息-->
<id column="uid" property="uid"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<!--Order信息-->
<collection property="orders" ofType="cn.mybatis.po.Order">
<id column="oid" property="oid"></id>
<result column="total" property="total"></result>
<result column="ordertime" property="ordertime"></result>
<result column="name" property="name"></result>
<!--OrderItem信息-->
<collection property="orderItems" ofType="cn.mybatis.po.OrderItem">
<id column="itemid" property="itemid"></id>
<result column="count" property="count"></result>
<result column="subtotal" property="subtotal"></result>
<association property="product" javaType="cn.mybatis.po.Product">
<id column="pid" property="pid"></id>
<result column="pname" property="pname"></result>
<result column="shop_price" property="shop_price"></result>
</association>
</collection>
</collection>
</resultMap>5、然后编写响应的Sql,并添加到Mapper中的statment中
<select id="findUsersAndProduct" parameterType="int" resultMap="UserAndProductResultMap">
SELECT orders.*,
user.username,
user.sex,
user.address,
orderitem.itemid,
orderitem.count,
orderitem.subtotal,
product.pid,
product.pname,
product.shop_price
FROM
orders,
USER,
orderitem,
product
WHERE user.uid = orders.uid AND orderitem.oid = orders.oid
</select>6、在Mapper.java中添加响应的方法
public List<User> findUsersAndProduct() throws Exception;7、最后使用Junit测试结果如下