前言
数据库中的关系:一对一;一对多;多对多
理解为什么要有这些关系?数据库是对现实世界的抽象。个人是数据库中一条横向的信息,如果社会所有人所有事,都无交集,那信息查询很简单,每次查一条就好了。那么应对复杂的社会关系,数据库就有了以上的这些关系。
下面我通过 三个例子:实现Mybatis的多表查询 ,希望这篇文章对你在探索Mybatis多表查询时有帮助。
Tips : 如果你以前使用过多表查询,现在模糊了,看一下 “Mybatis针对多表查询提供哪些技术即可”;如果你第一次接触mybatis的多表查询,我建议你完整看完,并且自己去练习一下。
Mybatis针对多表查询提供哪些技术?
主要就是这个 resultMap
// totalStudent ID 标识对应下面
// type表示,这个信息最后是封装到student类中,而student类中可能包含其他类,(单类)使用association;(列表)使用collection
<resultMap id="totalStudent" type="student">
// 查询到的student表的主键,id 表示一条数据的主键,对应数据库中的主键(规范)
<id column="id" property="id"></id>
// 查询到student表的student_name 封装到 student类中的studentName,result表示结果,本质上id和result作用一样,只是一个规范罢了
// column对应着数据库的列名,property对应着实体类中的名称
<result column="student_name" property="studentName"></result>
// association 用于 一对一,多对一的情况,封装的是一个对象
// property 表示student类中(包含的其他类)的名称,例如 Info infoo,填入infoo
// javaType 表示该(其他类)的全类名或者别名,用于定位封装,例如 Info infoo,填入Info的全类名或者别名
<association property="info" javaType="info">
<id column="id" property="id"></id>
<result column="info" property="info"></result>
</association>
// collection 用于 一对多的情况,封装的是一个集合
// property 表示student类中(List<info> cyl)的名称,例如这里就该填cyl
// ofType 表示该(List<info> cyl)的全类名或者别名,用于定位封装,例如这里就该填info的全类名或者别名
<collection property="***" ofType="***">
<id column="***" property="***"></id>
<result column="***" property="***"></result>
</collection>
</resultMap>
// totalStudent ID 标识对应上面
<select id="***" resultMap="totalStudent">
</select>
Question resultType 和 resultMap 区别
回答:
resultType :没有涉及到多表查询,查询的结果直接可以封装到对应的bean类型,在resultType中注明即可。resultMap :最大的特征就是,要封装的类属性中含有其他的类对象,需要将结果封装到对应的类后,再注入到相应的属性中。
注意点 association 和 collection 使用注意事项
- association使用 javaType 描述的是pojo的类型
- collection使用 oftype 描述的是集合的类型
一对一 学籍信息
学生只能有一个学籍信息,通过学籍信息也只能查到对应的一个学生
示例目标 :
- 查询某个人具体的学籍信息
- 查询学籍信息对应的具体人员
数据库实现
erDiagram
Person }|--|{ info : search
- Student表
CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`id_card` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `id_card` (`id_card`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
- Info表
CREATE TABLE `info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`student_id_card` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`info` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `student_id_card` (`student_id_card`),
CONSTRAINT `info_ibfk_1` FOREIGN KEY (`student_id_card`) REFERENCES `student` (`id_card`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
Mybatis实现一对一查询
association:一个学生只有一个学籍信息,一个学籍信息只对应一个学生
Pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
private Integer id;
private String studentName;
private String idCard;
// 查询每个学生时,顺带知道他的学籍信息
private Info info;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Info {
private Integer id;
private String studentIdCard;
private String info;
// 查询每个学籍信息时,知道该学籍信息是属于哪个学生的
private Student student;
@Override
public String toString() {
return "Info{" +
"id=" + id +
", info='" + info + ''' +
'}';
}
}
Mapper Interface
public interface StudentMapper {
// 查询所有学生信息
public List<Student> getAllStudent();
// 查询某个学生信息
public Student selectById(@Param("id") Integer id);
}
public interface InfoMapper {
// 通过身份ID在学籍信息中查询对应人员信息
public Info selectByIdCard(@Param("id") String idCard);
}
StudentMapper.xml
<resultMap id="totalStudent" type="student">
<id column="id" property="id"></id>
<result column="student_name" property="studentName"></result>
<result column="id_card" property="idCard"></result>
<association property="info" javaType="info">
<id column="id" property="id"></id>
<result column="info" property="info"></result>
</association>
</resultMap>
<!--select * from student s,info i WHERE s.id_card = i.student_id_card;-->
<select id="getAllStudent" resultMap="totalStudent">
select * from student s,info i WHERE s.id_card = i.student_id_card;
</select>
<!--select * from student s,info i WHERE s.id_card = i.student_id_card AND s.id_card='330127199808110000';-->
<select id="selectById" resultMap="totalStudent">
select * from student s,info i WHERE s.id_card = i.student_id_card AND s.id_card=#{id};
</select>
- getAllStudent 测试结果:
==> Preparing: select * from student s,info i WHERE s.id_card = i.student_id_card;
==> Parameters:
<== Columns: id, student_name, id_card, id, student_id_card, info
<== Row: 1, chengyunlai, 330127199808110000, 1, 330127199808110000, 研究生
<== Row: 2, xuhuajie, 330110199702230000, 2, 330110199702230000, 研究生
<== Total: 2
{"id":1,"idCard":"330127199808110000","info":{"id":1,"info":"研究生"},"studentName":"chengyunlai"}
{"id":2,"idCard":"330110199702230000","info":{"id":2,"info":"研究生"},"studentName":"xuhuajie"}
- selectById 测试结果:
==> Preparing: select * from student s,info i WHERE s.id_card = i.student_id_card AND s.id_card=?;
==> Parameters: 330127199808110000(String)
<== Columns: id, student_name, id_card, id, student_id_card, info
<== Row: 1, chengyunlai, 330127199808110000, 1, 330127199808110000, 研究生
<== Total: 1
{"id":1,"idCard":"330127199808110000","info":{"id":1,"info":"研究生"},"studentName":"chengyunlai"}
实验结果 :将学籍信息封装在了学生类中,实现了通过学生查询到相关学籍信息
InfoMapper.xml
<resultMap id="information" type="info">
<id column="id" property="id"></id>
<result column="student_id_card" property="studentIdCard"></result>
<result column="info" property="info"></result>
<association property="student" javaType="student">
<id column="id" property="id"></id>
<result column="student_name" property="studentName"></result>
<result column="id_card" property="idCard"></result>
</association>
</resultMap>
<!--select * from info i,student s where i.student_id_card = s.id_card AND i.student_id_card='330127199808110000';-->
<select id="selectByIdCard" resultMap="information">
select * from info i,student s where i.student_id_card = s.id_card AND i.student_id_card=#{id};
</select>
- selectByIdCard 测试结果:
==> Preparing: select * from info i,student s where i.student_id_card = s.id_card AND i.student_id_card=?;
==> Parameters: 330127199808110000(String)
<== Columns: id, student_id_card, info, id, student_name, id_card
<== Row: 1, 330127199808110000, 研究生, 1, chengyunlai, 330127199808110000
<== Total: 1
{"id":1,"info":"研究生","student":{"id":1,"idCard":"330127199808110000","studentName":"chengyunlai"},"studentIdCard":"330127199808110000"}
实验结果 :通过身份Id实现了查询学籍信息及对应的人员信息
一对多 家有宠物
我喜欢小狗,我可以养很多小狗,但小狗认主人只能有一个,要负责任!
示例目标 :
- 查询某个人有哪些宠物
- 查询某个宠物对应的主人是谁
数据库实现
erDiagram
Person }|--|{ pet1 : hava
Person }|--|{ pet2 : hava
- person表
CREATE TABLE `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`person_name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
- pet表
CREATE TABLE `pet` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pet_name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
`person_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `person_id` (`person_id`),
CONSTRAINT `pet_ibfk_1` FOREIGN KEY (`person_id`) REFERENCES `person` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
Mybatis实现一对多查询
collection 一个人有很多宠物,查询人的时候多个宠物封装用collection
association 一个宠物只认一个主人,反过来又是一个:一对一的问题
Pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Person {
private Integer id;
private String personName;
// 注意看 一对多,这里是一个集合,这很关键,封装需要用collection
private List<Pet> pet;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Pet {
private Integer id;
private String petName;
private long personId;
}
Mapper Interface
public interface PersonMapper {
// 遍历所有人员及其养的所有宠物信息
public List<Person> getPersonAndPet();
}
public interface PetMapper {
// 通过宠物姓名查询信息,级联查询该宠物是属于哪个主人
public Pet getPetInfoByName(@Param("name") String petName);
}
PersonMapper.xml
<mapper namespace="top.itifrd.mapper.PersonMapper">
<resultMap id="allPerson" type="person">
<id column="id" property="id"></id>
<result column="person_name" property="personName"></result>
<collection property="pet" ofType="pet">
<id column="pt.id" property="id"></id>
<result column="pet_name" property="petName"></result>
<result column="person_id" property="personId"></result>
</collection>
</resultMap>
<select id="getPersonAndPet" resultMap="allPerson">
select * from person ps,pet pt where ps.id = pt.person_id;
</select>
- getPersonAndPet测试结果:
==> Preparing: select * from person ps,pet pt where ps.id = pt.person_id;
==> Parameters:
<== Columns: id, person_name, id, pet_name, person_id
<== Row: 1, chengyunlai, 1, 小黑, 1
<== Row: 1, chengyunlai, 2, 小哈, 1
<== Total: 2
{"id":1,"personName":"chengyunlai","pet":[{"personId":1,"petName":"小黑"},{"personId":1,"petName":"小哈"}]}
PerMapper.xml
<mapper namespace="top.itifrd.mapper.PetMapper">
<resultMap id="allPet" type="pet">
<id column="id" property="id"></id>
<result column="pet_name" property="petName"></result>
<result column="pt.person_id" property="personId"></result>
<association property="person" javaType="person">
<id column="id" property="id"></id>
<result column="person_name" property="personName"></result>
</association>
</resultMap>
<select id="getPetInfoByName" resultMap="allPet">
select * from pet pt, person ps where pt.person_id = ps.id and pt.pet_name = #{name};
</select>
- getPetInfoByName测试结果:
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4c15e7fd]
==> Preparing: select * from pet pt, person ps where pt.person_id = ps.id and pt.pet_name = ?;
==> Parameters: 小黑(String)
<== Columns: id, pet_name, person_id, id, person_name
<== Row: 1, 小黑, 1, 1, chengyunlai
<== Total: 1
Pet(id=1, petName=小黑, personId=0, person=Person(id=1, personName=chengyunlai, pet=null))
分析 一对一 和 一对多
在XML操作上这两者并没有多少本质的区别,可以说是一模一样。其本质其实是在数据库的构建上,在构建中体现数据表之间的关系,这个很重要
多对多 角色表
我可能是个 程序员, 摄影师, 美食达人,而我也不是唯一的程序员,摄影师,美食达人,我的朋友可能也是待更新