MyBatis代码实例系列-04:MyBatis多表映射实例(一对多、多对一和多对多)

超级通道:MyBatis代码实例系列-绪论

本章主要记录MyBatis中的多表映射:一对多、多对一和多对多,涉及到的知识点有:
1. resultMap:当数据库方法返回的是复合数据类型(如list等),通常使用resultMap而非resultType
2. collection:在resultMap中用来配置集合类型的数据结构,用来实现表间一对多映射。
3. association:在resultMap中用来配置关联关系的数据结构,用来实现表间多对一以及一对一映射。

1.表间一对多关联映射

实例场景:person(人员表)experience(工作经历表)是一对多关系,即:一个人有多条工作经验,一条工作经验只能属于一个人。
实例目的:通过一个人员的id,查询出此人的人员信息及所有的工作经历信息。

1.1.SQL语句

drop table person;
create table `person`(`person_id` int(5) unsigned not null comment '人员id',`name` varchar(10) not null comment '姓名',`age` int(3) not null comment '年龄',primary key(person_id)
)engine=InnoDB comment='人员' auto_increment=1 default charset=utf8;
insert into person values(1,'张三',25);
insert into person values(2,'李四',16);
select * from person;drop table experience;
create table `experience`(`experience_id` int(5) unsigned not null comment '工作经验id',`person_id` int(5)  unsigned not null comment '人员id',`company` varchar(32) not null comment '公司',`position` varchar(16) not null comment '职位',primary key(experience_id)
)engine=InnoDB comment='工作经验' auto_increment=1 default charset=utf8;
insert into experience values(1,1,'北京天大地大科技有限公司','开发工程师');
insert into experience values(2,1,'南通考古研究院','考古专家');
insert into experience values(3,1,'海南旅游集团','CEO');
select * from experience;

1.2.目录结构

src
\---main\---java|   \---pers|       \---hanchao|           \---himybatis|               \---one2many|                   \---Person.java|                   \---Experience.java|                   \---IPersonDAO.java|                   \---PersonApp.java\---webapp\---mybatis-mappers\---Person.xml\---log4j.properties\---mybatis-config.xml

1.3.MyBatis配置mybatis-config.xml

关于mybatis-config.xml的完整配置,可以参考:
MyBatis代码实例系列-04:MyBatis单张表简单实现增删改查 + log4j + 手动事务控制

        <typeAlias type="pers.hanchao.himybatis.one2many.Person" alias="Person"/><typeAlias type="pers.hanchao.himybatis.one2many.Experience" alias="Experience"/><mapper resource="mybatis-mappers/Person.xml"/>

1.4.实体类+IDAO+XML

1.4.1.实体类

Person.java

package pers.hanchao.himybatis.one2many;import java.util.List;/*** 

人员信息

* @author hanchao 2018/1/27 14:55**/
public class Person {/** 人员id */private Integer id;/** 姓名 */private String name;/** 年龄 */private Integer age;/** 教育背景 */private List experienceList;//constructor,toString,setter,getter }

Experience.java

package pers.hanchao.himybatis.one2many;/*** 

工作经验

* @author hanchao 2018/1/27 14:58**/
public class Experience {/** 工作经验id */private Integer id;/** 公司名称 */private String company;/** 职位 */private String position;//constructor,toString,setter,getter }

说明:

  1. 一对多实现的关键之一:通过将对象列表作为属性,如private List experienceList;

1.4.2.IDAO

在当前场景中,人员是业务处理的对象,所以Person为操作对象,建立IDAO,即IPersonDAO

package pers.hanchao.himybatis.one2many;/*** 

人员-工作经历操作DAO层接口

* @author hanchao 2018/1/27 15:50**/
public interface IPersonDAO {/** 根据id查询人员信息及工作经验信息 */Person queryPersonById(Integer id); }

1.4.3.XML:Problem.xml





<mapper namespace="pers.hanchao.himybatis.one2many.IPersonDAO"><select id="queryPersonById" parameterType="Integer" resultMap="personExperienceMap">SELECT psn.*,exp.* FROM `person` psn LEFT JOIN `experience` exp ON psn.person_id = exp.person_id WHERE psn.person_id = #{id}select><resultMap id="personExperienceMap" type="pers.hanchao.himybatis.one2many.Person"><result property="id" column="person_id"/><result property="name" column="name"/><result property="age" column="age"/><collection property="experienceList" ofType="pers.hanchao.himybatis.one2many.Experience" column="person_id"><result property="id" column="experience_id"/><result property="company" column="company"/><result property="position" column="position"/>collection>resultMap>
mapper>

注意:

  1. queryPersonById查询的返回值不是简单的Person类型,而是组合类型personExperienceMap
  2. personExperienceMap类型中由collection属性配置集合类型的元素,以实现1对多的目的。
  3. result标签的property表示的是Java实体中的字段,column表示的是数据库表中的字段。
  4. collection标签的column字段表示外键字段。

1.5.result

2018-01-28 15:52:48 INFO  PersonApp:48 - Person{id=1, name='张三', age=25}
2018-01-28 15:52:48 INFO  PersonApp:76 - Experience{id=1, company='北京天大地大科技有限公司', position='开发工程师'}
2018-01-28 15:52:48 INFO  PersonApp:76 - Experience{id=2, company='南通考古研究院', position='考古专家'}
2018-01-28 15:52:48 INFO  PersonApp:76 - Experience{id=3, company='海南旅游集团', position='CEO'}2018-01-28 15:52:48 INFO  PersonApp:54 - Person{id=2, name='李四', age=16}
2018-01-28 15:52:48 INFO  PersonApp:73 - 此人无工作经验

2.表间多对一关联映射

实例场景:experience(工作经历表)person(人员表)与是多对一关系,即:一条工作经验只能属于一个人,一个人有多条工作经验。
实例目的:通过一个工作经验的id,查询出此工作经历信息及所属的人员信息。

2.1.SQL语句

drop table person;
create table `person`(`person_id` int(5) unsigned not null comment '人员id',`name` varchar(10) not null comment '姓名',`age` int(3) not null comment '年龄',primary key(person_id)
)engine=InnoDB comment='人员' auto_increment=1 default charset=utf8;
insert into person values(1,'张三',25);
insert into person values(2,'李四',16);
select * from person;drop table experience;
create table `experience`(`experience_id` int(5) unsigned not null comment '工作经验id',`person_id` int(5)  unsigned not null comment '人员id',`company` varchar(32) not null comment '公司',`position` varchar(16) not null comment '职位',primary key(experience_id)
)engine=InnoDB comment='工作经验' auto_increment=1 default charset=utf8;
insert into experience values(1,1,'北京天大地大科技有限公司','开发工程师');
insert into experience values(2,1,'南通考古研究院','考古专家');
insert into experience values(3,1,'海南旅游集团','CEO');
select * from experience;

2.2.目录结构

src
\---main\---java|   \---pers|       \---hanchao|           \---himybatis|               \---many2one|                   \---Experience.java|                   \---Person.java|                   \---IExperienceDAO.java|                   \---ExperienceApp.java\---webapp\---mybatis-mappers\---Experience.xml\---log4j.properties\---mybatis-config.xml

2.3.MyBatis配置mybatis-config.xml

关于mybatis-config.xml的完整配置,可以参考:
MyBatis代码实例系列-04:MyBatis单张表简单实现增删改查 + log4j + 手动事务控制



<mapper resource="mybatis-mappers/Experience.xml"/>

2.4.实体类+IDAO+XML

2.4.1.实体类

Experience.java

package pers.hanchao.himybatis.many2one;
/*** 

工作经验

* @author hanchao 2018/1/27 16:07**/
public class Experience {/** 工作经验id */private Integer id;/** 公司名称 */private String company;/** 职位 */private String position;/** 所属人员 */private Person person;//constructor,toString,setter,getter }

Person.java

package pers.hanchao.himybatis.many2one;/*** 

人员信息

* @author hanchao 2018/1/27 16:06**/
public class Person {/** 人员id */private Integer id;/** 姓名 */private String name;/** 年龄 */private Integer age;//constructor,toString,setter,getter }

说明:

1. 多对一实现的关键之一:通过将对象作为属性,如private Person person;

2.4.2.IDAO

在当前场景中,工作经历是业务处理的对象,所以Experience为操作对象,建立IDAO,即IExperienceDAO

package pers.hanchao.himybatis.many2one;/*** 

工作经历-人员DAO层接口

* @author hanchao 2018/1/27 16:02**/
public interface IExperienceDAO {/** 根据id查询工作经验及所属人员信息 */Experience queryExperienceById(Integer id); }

2.4.3.XML:Experience.xml





<mapper namespace="pers.hanchao.himybatis.many2one.IExperienceDAO"><select id="queryExperienceById" parameterType="Integer" resultMap="experiencePersonMap">SELECT exp.*,psn.* FROM experience exp LEFT JOIN person psn ON exp.person_id = psn.person_id WHERE exp.experience_id = #{id}select><resultMap id="experiencePersonMap" type="pers.hanchao.himybatis.many2one.Experience"><result property="id" column="experience_id"/><result property="company" column="company"/><result property="position" column="position"/><association property="person" column="user_id" javaType="pers.hanchao.himybatis.many2one.Person"><result property="id" column="person_id"/>association>resultMap>
mapper>

注意:

  1. queryExperienceById查询的返回值不是简单的Experience类型,而是组合类型experiencePersonMap
  2. experiencePersonMap类型中由association属性配置关联关系的实体,以实现多对一的目的。
  3. result标签的property表示的是Java实体中的字段,column表示的是数据库表中的字段。
  4. 字段的property和column一致,可以不配置。
  5. association标签的column字段表示外键字段。

2.5.result

2018-01-28 15:53:47 INFO  ExperienceApp:38 - Experience{id=2, company='南通考古研究院', position='考古专家'}
2018-01-28 15:53:47 INFO  ExperienceApp:39 - Person{id=1, name='张三', age=25}

3.表间多对多关联映射

实例场景:student(学生表)course(选修)与是多对多关系他们通过中间表student_course相关联,即:一个学生可以选修多门选修课,每门选修课可以被多个学生选修。
实例目的:

  1. 通过一个学生的id,查询出此学生的学生信息及所选选修课信息。
  2. 通过一个选修课的id,查询出此选修课的信息及所有选修本课的学生信息。
  3. 添加一个新的学生信息。
  4. 添加一门新的选修课。
  5. 新来的学生选修了新开设的选修课。

3.1.SQL语句

drop table student;
create table `student`(`student_id` int(4) unsigned not null comment '学生id',`name` varchar(10) not null comment '姓名',`number` varchar(3) not null comment '学号',primary key(student_id)
)engine=InnoDB comment='学生' auto_increment=1 default charset=utf8;
insert into student values(1,'张三','003');
insert into student values(2,'李四','004');
insert into student values(3,'王五','005');
select * from student;drop table course;
create table `course`(`course_id` int(5) unsigned not null comment '选修课id',`name` varchar(10)  not null comment '课程名',`score` int(1) unsigned not null comment '学分',primary key(course_id)
)engine=InnoDB comment='选修课' default charset=utf8;
insert into course values(1,'Java开发',2);
insert into course values(2,'国家地理',1);
insert into course values(3,'十级英语',2);
insert into course values(4,'高等数学',3);
insert into course values(5,'国语',1);
select * from course;drop table student_course;
create table `student_course`(`student_id` int(4) unsigned not null comment '学生id',`course_id` int(4) unsigned not null comment '选修课id',primary key(student_id,course_id)
)engine = InnoDB comment='学生选修课中间表' charset=utf8;
insert into student_course values(1,1);
insert into student_course values(1,3);
insert into student_course values(1,5);insert into student_course values(2,2);
insert into student_course values(2,4);insert into student_course values(3,1);
insert into student_course values(3,2);
insert into student_course values(3,3);
insert into student_course values(3,4);
insert into student_course values(3,5);
select * from student_course;

注意:多对多最好是使用这种单表+中间表+单表的数据结构

3.2.目录结构

src
\---main\---java|   \---pers|       \---hanchao|           \---himybatis|               \---many2many|                   \---Student.java|                   \---Course.java|                   \---StudentCourse.java|                   \---IStudentDAO.java|                   \---ICourseDAO.java|                   \---IStudentCourseDAO.java|                   \---Many2manyApp.java\---webapp\---mybatis-mappers\---Student.xml\---Course.xml\---StudentCourse.xml\---log4j.properties\---mybatis-config.xml

3.3.MyBatis配置mybatis-config.xml

关于mybatis-config.xml的完整配置,可以参考:
MyBatis代码实例系列-04:MyBatis单张表简单实现增删改查 + log4j + 手动事务控制


<typeAlias type="pers.hanchao.himybatis.many2many.Student" alias="Student"/>
<typeAlias type="pers.hanchao.himybatis.many2many.Course" alias="Course"/>
<typeAlias type="pers.hanchao.himybatis.many2many.StudentCourse" alias="StudentCourse"/>
<mapper resource="mybatis-mappers/Student.xml"/>
<mapper resource="mybatis-mappers/Course.xml"/>
<mapper resource="mybatis-mappers/StudentCourse.xml"/>

3.4.实体类+IDAO+XML

3.4.1.实体类

单表Student.java

package pers.hanchao.himybatis.many2many;import java.util.List;/*** 

学生信息表

* @author hanchao 2018/1/27 16:54**/
public class Student {/** 学生id */private Integer id;/** 姓名 */private String name;/** 学号 */private String number;/** 所有选修课 */private List courseList;//constructor,toString,setter,getter }

单表Course.java

package pers.hanchao.himybatis.many2many;import java.util.List;/*** 

选课表

* @author hanchao 2018/1/27 16:55**/
public class Course {/** 选课id */private Integer id;/** 课程名称 */private String name;/** 学分 */private Integer score;/** 选择本课程的学生 */private List studentList;//constructor,toString,setter,getter }

中间表StudentCourse.java

package pers.hanchao.himybatis.many2many;/*** 

学生选修课中间表

* @author hanchao 2018/1/27 16:59**/
public class StudentCourse {private Integer studentId;private Integer courseId;//constructor,toString,setter,getter }

注意:

  1. 对单表Student和Course,都需要以对方的对象列表作为属性如private List courseList;和private List studentList;

3.4.2.IDAO

在当前场景中,学生和课程可以作为业务对象。所以分别以StudentCourse为操作对象,建立IDAO,即IStudentDAOICourseDAO

IStudentDAO.java

package pers.hanchao.himybatis.many2many;/*** 

学生DAO

* @author hanchao 2018/1/27 17:06**/
public interface IStudentDAO {/** 新增一个学生 */void insertStudent(Student student);/** 根据id查询一个学生的信息 */Student queryStudentById(Integer id); }

ICourseDAO.java

package pers.hanchao.himybatis.many2many;/*** 

选修课DAO

* @author hanchao 2018/1/27 17:03**/
public interface ICourseDAO {/** 新增一门课程 */void insertCourse(Course course);/** 根据id查询一门课程 */Course queryCourseById(Integer id); }

IStudentCourseDAO.java

package pers.hanchao.himybatis.many2many;/*** 

学生选课DAO

* @author hanchao 2018/1/27 17:08**/
public interface IStudentCourseDAO {/** 插入一条学生选课信息 */void insertStudentCourse(StudentCourse studentCourse);/** */Student queryStudentByCourseId(Integer id);/** */Course queryCourseByStudentId(Integer id); }

3.4.3.XML

Student.xml





<mapper namespace="pers.hanchao.himybatis.many2many.IStudentDAO"><insert id="insertStudent" parameterType="Student">INSERT INTO `student`(student_id,name,number) VALUES (#{id},#{name},#{number})insert><select id="queryStudentById" parameterType="Integer" resultMap="studentCourseMap">SELECT * FROM `student` WHERE student_id = #{id}select><resultMap id="studentCourseMap" type="Student"><result property="id" column="student_id"/><collection property="courseList" column="student_id"select="pers.hanchao.himybatis.many2many.IStudentCourseDAO.queryCourseByStudentId"/>resultMap>
mapper>

Course.xml





<mapper namespace="pers.hanchao.himybatis.many2many.ICourseDAO"><insert id="insertCourse" parameterType="Course">INSERT INTO `course`(course_id,name,score) VALUES (#{id},#{name},#{score})insert><select id="queryCourseById" parameterType="Integer" resultMap="courseStudentMap">SELECT * FROM `course` WHERE course_id = #{id}select><resultMap id="courseStudentMap" type="Course"><result property="id" column="course_id"/><collection property="studentList" column="course_id"select="pers.hanchao.himybatis.many2many.IStudentCourseDAO.queryStudentByCourseId"/>resultMap>
mapper>

StudentCourse.xml





<mapper namespace="pers.hanchao.himybatis.many2many.IStudentCourseDAO"><insert id="insertStudentCourse" parameterType="StudentCourse">INSERT INTO `student_course`(student_id,course_id) VALUES (#{studentId},#{courseId})insert><select id="queryCourseByStudentId" resultMap="courseByStudentIdMap" parameterType="Integer">SELECT cos.*,sc.course_idFROM `course` cos,`student_course` scWHERE cos.course_id = sc.course_idAND sc.student_id = #{student_id}select><resultMap id="courseByStudentIdMap" type="Course"><result property="id" column="course_id"/>resultMap><select id="queryStudentByCourseId" resultMap="studentByCourseMap" parameterType="Integer">SELECT std.*,sc.student_idFROM `student` std,`student_course` scWHERE std.student_id = sc.student_idAND sc.course_id = #{course_id}select><resultMap id="studentByCourseMap" type="Student"><result property="id" column="student_id"/>resultMap>
mapper>

说明:

  1. StudentCourse的一对多关系,实际上是通过中间表student_course转化成了两个1对多关系,即一个student对应多个student_course以及一个Course对应多个student_course。所以,多对多映射实际上是两个一对多映射的组合。
  2. 一对多查询的返回值不是简单的实体类型,而是组合类型,如studentCourseMapcourseStudentMap等。
  3. resultMap类型中由collection属性配置集合类型的元素,以实现1对多的目的。
  4. result标签的property表示的是Java实体中的字段,column表示的是数据库表中的字段。
  5. 字段的propertycolumn一致,可以不配置。
  6. collection标签的column字段表示外键字段。
  7. collection标签的select字段表示的是映射到中间表中的方法。

3.5.result

2018-01-28 15:54:11 INFO  Many2manyApp:40 - =========查询学生id=1的所有选修课
2018-01-28 15:54:11 INFO  Many2manyApp:43 - Student{id=1, name='张三', number='003'}
2018-01-28 15:54:11 INFO  Many2manyApp:109 - Course{id=1, name='Java开发', score=2}
2018-01-28 15:54:11 INFO  Many2manyApp:109 - Course{id=3, name='十级英语', score=2}
2018-01-28 15:54:11 INFO  Many2manyApp:109 - Course{id=5, name='国语', score=1}2018-01-28 15:54:11 INFO  Many2manyApp:48 - =========查询选修课id=1的所有学生
2018-01-28 15:54:11 INFO  Many2manyApp:51 - Course{id=1, name='Java开发', score=2}
2018-01-28 15:54:11 INFO  Many2manyApp:109 - Student{id=1, name='张三', number='003'}
2018-01-28 15:54:11 INFO  Many2manyApp:109 - Student{id=3, name='王五', number='005'}2018-01-28 15:54:11 INFO  Many2manyApp:56 - =========新增一个学生:陈六
2018-01-28 15:54:11 INFO  Many2manyApp:59 - Student{id=4, name='陈六', number='006'}2018-01-28 15:54:11 INFO  Many2manyApp:63 - =========新增一门课程:玄学
2018-01-28 15:54:11 INFO  Many2manyApp:66 - Course{id=6, name='玄学', score=3}2018-01-28 15:54:11 INFO  Many2manyApp:70 - =========新学生 陈六 选修了新选修课 玄学
2018-01-28 15:54:11 INFO  Many2manyApp:74 - StudentCourse{studentId=4, courseId=6}2018-01-28 15:54:11 INFO  Many2manyApp:78 - =========查询陈六选修的课程
2018-01-28 15:54:11 INFO  Many2manyApp:80 - Student{id=4, name='陈六', number='006'}
2018-01-28 15:54:11 INFO  Many2manyApp:109 - Course{id=6, name='玄学', score=3}2018-01-28 15:54:11 INFO  Many2manyApp:85 - =========查询选修玄学的所有学生
2018-01-28 15:54:11 INFO  Many2manyApp:87 - Course{id=6, name='玄学', score=3}
2018-01-28 15:54:11 INFO  Many2manyApp:109 - Student{id=4, name='陈六', number='006'}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部