abstract:上次我们讲到了MyBatis的一对一关系的表示,简单回顾一下一对一关系就是一个学生只有一个学生证。那么什么是一对多关系呢?一个学生有多个课程这就是一对多的关系。我们结合上一章中的学生和学生证,在此基础上新增一个课程表和课程成绩表。学生对应课程表是一对多的关系,在学生确定的情况下课程表对应课程成绩是一对一的关系。我们先来看看我们所假设的场景数据结构的设计。数据库的ER图如下(因为对数据库还处于菜鸟阶
上次我们讲到了MyBatis的一对一关系的表示,简单回顾一下一对一关系就是一个学生只有一个学生证。那么什么是一对多关系呢?一个学生有多个课程这就是一对多的关系。我们结合上一章中的学生和学生证,在此基础上新增一个课程表和课程成绩表。学生对应课程表是一对多的关系,在学生确定的情况下课程表对应课程成绩是一对一的关系。我们先来看看我们所假设的场景数据结构的设计。
数据库的ER图如下(因为对数据库还处于菜鸟阶段……所以可能ER图绘制有误,但不影响我们讲解MyBatis一对多关系的级联):
再看看数据库的物理模型包含哪些字段:
数据库的设计就差不多了,接下来是设计我们的POJO类:
首先是Student类:
package day_8_mybatis.pojo; import java.util.List; /** * @author turbo * * 2016年11月4日 */ public class Student { private int id; private String name; private String sex; private SelfCard selfCard; //学生和学生证是一对一的关系,所以存放一个对学生证类的引用 private ListcourseScoreList; //我们在一开始就提到过学生和课程是一对多的关系,所以学生POJO类中对课程类字段就是一个List用来存放学生的课程成绩。 //省略set/get方法 }
注意,我们有一个List字段是对课程成绩的引用而不是课程的引用。为什么呢?因为在我们数据库设计中,学生和课程是通过课程成绩联系起来的。
接着是我们的CourseScore类:
package day_8_mybatis.pojo; /** * @author turbo * * 2016年11月4日 */ public class CourseScore { private int id; private int studentId; private Course course; //在学生id确认的情况下,课程和成绩是一对一的关系。 private String score; //省略set/get方法 }
最后是Course类:
package day_8_mybatis.pojo; /** * @author turbo * * 2016年11月4日 */ public class Course { private int id; private String courseName; private String note; //省略set/get方法 }
现在我们是要通过一个学生ID,就能查询出这个学生的课程、以及对应课程的成绩。这个怎么来实现呢?在使用MyBatis为我们提供的级联前,我们先来梳理一下从逻辑上是怎么一步一步查询出来的。
我们要通过学生id查询出学生的基本信息(包括课程以及对应的成绩),但在学生POJO类中有一个对课程成绩的List引用(暂时忽略学生证),也就是说我们无法一条简单的sql语句(无join的sql语句)查询出结果。但是!我们可以通过student_id在课程成绩表中查询出该学生的相应课程id(注意此时还是id),但我们此时还是没办法知道具体的课程名,再利用我们上一步中student_id查询出的course_id通过课程表再来查询出对应的课程名。重新梳理一下:
通过student_id在t_student表中查询学生基本信息(name,sex)
通过student_id在t_course_score表中查询学生对应的course_id
通过course_id在t_course表中查询课程
那我们现在就从最底层做起,也就是通过course_id查询出具体课程,因为这不会涉及到其他表。
package day_8_mybatis.mapper; import day_8_mybatis.pojo.Course; /** * @author turbo * * 2016年11月4日 */ public interface CourseMapper { Course getCourse(int id); //此id为course_id }
再来看看mapper映射,也是非常简单。
现在已经写好了通过course_id来查询出具体课程。那么就是倒着走到第2步,通过student_id在t_course_score表中查询学生对应的course_id,在最开始说过,在学生确定的情况下,课程和课程成绩是一对一的关系,关于一对一的关系我们在上一篇已经讲过,不妨再重温一下。
package day_8_mybatis.mapper; import day_8_mybatis.pojo.CourseScore; /** * @author turbo * * 2016年11月4日 */ public interface CourseScoreMapper { CourseScore findCourseScoreByStudentId(int id); }
再来看看mapper映射,也是非常简单。
最后一步,也就是第1步,才进入正题MyBatis的一对多collection级联关系。
package day_8_mybatis.mapper; import day_8_mybatis.pojo.Student; /** * @author turbo * * 2016年11月4日 */ public interface StudentMapper { Student getStudent(int id); }
关键在mapper映射中。
请好好仔细品味品味,仔细回顾整个查询的逻辑过程。collection就是MyBatis为我们提供的第二个级联关系——一对多。
最后上我们的测试代码:
package day_8_mybatis; import java.io.IOException; import java.io.InputStream; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import day_8_mybatis.mapper.StudentMapper; import day_8_mybatis.pojo.Student; import day_8_mybatis.util.SessionFactory2; /** * 客户端 * @author turbo * * 2016年11月4日 */ public class Main { /** * @param args * @throws IOException */ public static void main(String[] args) throws Exception { String resource = "day_8_mybatis/mybatis-config.xml"; //获取mybatis配置文件路径 InputStream inputStream = Resources.getResourceAsStream(resource); SqlSession sqlSession = SessionFactory2.getInstance(inputStream).openSession(); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); Student student = studentMapper.getStudent(1); System.out.println("学生:" + student.getName() + " 课程:" + student.getCourseScoreList().get(0).getCourse().getCourseName() + " 分数:" + student.getCourseScoreList().get(0).getScore()); } }
//还是把day_8_mybatis.util.SessionFactory2代码贴出来吧,SqlSessionFactory用到了单例模式,这也是MyBatis官方文档所提倡的,具体可以移步之前写的几个关键类的作用域问题,《SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession作用域(Scope)和生命周期》,也可移步至《单例模式》、《再说单例模式的线程安全问题》了解单例模式。
package day_8_mybatis.util; import java.io.InputStream; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; /** * @author turbo * * 2016年10月26日 */ public class SessionFactory2 { private static SqlSessionFactory sqlSessionFactory; public static synchronized SqlSessionFactory getInstance(InputStream inputStream){ if (null == sqlSessionFactory){ sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } return sqlSessionFactory; } }