本文针对MyBatis的关联映射,重点在于:
(1)不同的关联关系(一对一、一对多、多对多)如何创建数据表和在对应的类中创建外键关联。
(2)如何在各个类的xml映射文件中进行配置。
在xml映射文件中确定了什么方法,这些方法如何实现。
1、一对一关联映射
1.1数据表:
tb_card
和 tb_person
。
1.2数据表关联:
在tb_person表中新增一栏card_id,和tb_card中的id进行关联:
FOREIGN KEY(card_id) REFERENCES tb_card(id);
1.3类关联:
首先分别对照tb_card和tb_person创建两个类:Card
和Person
。然后在Person类中新增一个属性Card,用于指向某个具体的card对象。
private Card card;
这里就开始会有一些困惑了。首先我们的Person类的其他属性都是和数据库中的tb_person一一对应的,照理来说,这里应该对应的是card_id
,但实际上这里是直接指向Card对象。
1.4 xml映射文件:
1.4.1xml映射文件——CardMapper.xml:
定义了一个selectCardById
方法。根据id查询返回Card对象,实现方式如下:
SELECT * from tb_card where id = #{id}
1.4.2 xml映射文件——PersonMapper.xml:
定义了一个selectPersonById
方法。根据id查询Person。按照常理应该是返回一个Person对象,但是这里希望查询出来的结果既包含Person又能包含Card。所以需要使用到resultMap来实现。
首先还是通过Id找到Person对象,这个Person对象是resultMap的一部分。然后根据Person对象的card_id
来调用CardMapper.xml
中的selectCardById
方法。这样又能得到Card对象,这个对象也是resultMap的一部分。
也就是说,我们实现了通过Card的id查询card对象,通过Person的id查询Person对象(以及Card对象)。而为什么是实现了这些查询,例如为什么不能通过Card的id来查询Person对象。而为什么后面一对多中又有类似的操作呢。类中的定义的属性跟我们后面要实现的方法有没有什么联系呢。
2、一对多关联映射
来看一下MyBatis中一对多的关联映射:
2.1 数据表:
tb_clazz
和 tb_student
。
2.2 数据表关联:
在数据库中构建数据表的时候,外键在多方:
在tb_student
表中新增一栏clazz_id
,和tb_clazz中的id进行关联:
FOREIGN KEY(clazz_id) REFERENCES tb_clazz(id);
2.3 类关联:
首先分别对照tb_clazz和tb_student创建两个类:Clazz
和Student
。这里是一对多关系,在Student类中新增一个属性Clazz,用于指向某个具体的Clazz对象。
private Clazz clazz;
到这里为止跟之前的一对一关联映射还看不出什么区别。区别在于Clazz类。Clazz类有一个List
类型的students
属性,用于记载这个班级对应的所有学生)。
private List students;
那么,我们就可以预期,在接下来的selectClazzById
和 selectStudentById
方法中,结果中都需要返回Clazz和Student(或者Student的List)。
2.4 Xml映射文件
2.4.1xml映射文件——ClazzMapper.xml:
定义了一个selectClazzById
方法。这里还是通过clazz的id查询班级信息,但是不同于一对一映射中的CardMapper.xml,这里希望查询到的是班级和学生的信息,所以这里更类似于上面的PersonMapper.xml。
同样是先通过clazz的id查询到tb_clazz,然后映射成为Clazz对象。然后直接还是通过原来的这个clazz的id,通过调用接下来要讲到的StudentMapper.xml中的selectStudentByClazzId
方法找到对应的Student的数据并封装成一个数据列表。
这里和一对一中的PersonMapper.xml
不同的地方在于它是沿用clazz的id进行查询,而不是像PersonMapper那样利用person的id查询到person的clazz_id,然后再利用clazz_id查询出clazz。并且这里由于是一对多,得到的是一个列表。
2.4.2 xml映射文件——StudentMapper.xml:
定义了selectStudentById
方法和selectStudentByClazzId
方法。这里先说后面一个方法。因为ClazzMapper.xml会调用这个方法。这个方法看起来复杂(需要查询另外一张数据表),但是其实很简单。因为我们在上面的数据表关联中已经将clazz_id,和tb_clazz中的id进行关联。换句话说,通过tb_clazz中的id来查询Student数据就相当于是根据clazz_id来查询Student数据。所以:
SELECT * FROM tb_student WHERE clazz_id = #{id}
这个方法其实在StudentMapper.xml中不是一个关键的方法,更像是ClazzMapper.xml中的selectClazzById方法所需要的一个步骤。上面也已经提到过selectClazzById中原本就能实现找到Clazz,但是为了再显示出Student才需要引用到selectStudentByClazzId
这个方法。
StudentMapper.xml中关键的方法是selectStudentById
方法。
在selectStudentById方法中,希望通过学生id
来查询到学生对象和班级对象
。首先,如果使用SELECT * from tb_user where id= #{id}
可以查询出来Student对象。
但是这里需要将查到的student的clazz_id对应的班级的信息也都展示到结果中,所以还需要同时找到这个class对象。所以变成
SELECT * FROM tb_clazz c,tb_student s
WHERE c.id = s.clazz_id
AND s.id = #{id}
然后分别映射将结果封装到Clazz和Student对象中。
3、多对多关联映射
3.1 数据表:
tb_user
和tb_order
和tb_article
。这里虽然说是多对多的关联映射,但是同时也存在一对多的关联映射。以及一个中间表tb_item
。
tb_user
---一对多---> tb_order
<---多对多---> tb_article
3.2 数据表关联:
在数据库中构建数据表的时候,外键在多方:
在tb_order表中新增一栏user_id
,和tb_user
中的id进行关联:
FOREIGN KEY(user_id) REFERENCES tb_user(id);
但是在多和多关联映射下,不需要再表中添加新的关联栏。多对多可以通过构建一个中间表来维护关系。例如订单(tb_order
)和商品(tb_article
)的多对多关系,这时候可以使用中间表的订单id(order_id
)作为外键来参照订单表的id,中间表的商品id(article_id
)作为外键来参照商品表的id。这个表tb_item
是不能够单独存在的,而要依赖于订单和商品存在。
3.3 类关联:
需要建立三个类:User
、Order
、Article
这三个。
虽然创建了一个中间表,但是这个中间表不需要对应的类。
使用新的表进行映射后,还需要在对应的类中添加相应的属性,在订单Order类中添加商品属性(List
,而商品Article类中添加订单属性(List
。此外,上面说过User和Order还存在一对多的关系,所以还需要在Order中添加一个User属性。Order类新增属性如下:
private User user;
private List articles;
3.4 XML 映射文件
3.4.1 xml映射文件——UserMapper.xml:
这里定义了selectUserById
方法,希望通过User的id查询到user对象,以及该对象所具有的所有订单。这还是上面的一对多的范畴。查询结构要求显示User和相关的Order对象。而要获取Order对象同样需要在OrderMapper.xml中的selectOrderByUserId
中进行实现。这很容易实现。
3.4.2 xml映射文件——OrderMapper.xml:
定义了selectOrderById
和selectOrderByUserId
方法。
selectOrderByUserId在3.4.1中需要利用到。
而selectOrderById
同样要一起找到tb_user表和tb_order表的数据。但是这里还需要调用ArticleMapper.xml中的selectArticleByOrderId
方法,也就是说最后还需要显示所有的商品出来。
3.4.3 xml映射文件——ArticleMapper.xml:
多对多很复杂,但是查询看起来确实比较简单,利用了子查询。在ArticleMapper.xml中,selectArticleByOrderId
中
SELECT * FROM tb_article WHERE id IN(
SELECT article_id FROM tb_item WHERE order_id = #{id}
)
可以看到,在这里才利用了tb_item这个表,先通过order_id
查询到所有的article_id
。