MyBatis之級聯——一對多關系


上次我們講到了MyBatis的一對一關系的表示,簡單回顧一下一對一關系就是一個學生只有一個學生證。那么什么是一對多關系呢?一個學生有多個課程這就是一對多的關系。我們結合上一章中的學生和學生證,在此基礎上新增一個課程表和課程成績表。學生對應課程表是一對多的關系,在學生確定的情況下課程表對應課程成績是一對一的關系。我們先來看看我們所假設的場景數據結構的設計。

數據庫的ER圖如下(因為對數據庫還處於菜鳥階段……所以可能ER圖繪制有誤,但不影響我們講解MyBatis一對多關系的級聯):

再看看數據庫的物理模型包含哪些字段:

 

數據庫的設計就差不多了,接下來是設計我們的POJO類:

首先是Student類:

 1 package day_8_mybatis.pojo;
 2 
 3 import java.util.List;
 4 
 5 /**
 6  * @author turbo
 7  *
 8  * 2016年11月4日
 9  */
10 public class Student {
11     private int id;
12     private String name;
13     private String sex;
14     private SelfCard selfCard;  //學生和學生證是一對一的關系,所以存放一個對學生證類的引用
15     private List<CourseScore> courseScoreList;  //我們在一開始就提到過學生和課程是一對多的關系,所以學生POJO類中對課程類字段就是一個List用來存放學生的課程成績。
16     //省略set/get方法      
17 }

注意,我們有一個List字段是對課程成績的引用而不是課程的引用。為什么呢?因為在我們數據庫設計中,學生和課程是通過課程成績聯系起來的。

接着是我們的CourseScore類:

 1 package day_8_mybatis.pojo;
 2 
 3 /**
 4  * @author turbo
 5  *
 6  * 2016年11月4日
 7  */
 8 public class CourseScore {
 9     private int id;
10     private int studentId;
11     private Course course;  //在學生id確認的情況下,課程和成績是一對一的關系。
12     private String score;
13     //省略set/get方法  
14 }

最后是Course類:

 1 package day_8_mybatis.pojo;
 2 
 3 /**
 4  * @author turbo
 5  *
 6  * 2016年11月4日
 7  */
 8 public class Course {
 9     private int id;
10     private String courseName;
11     private String note;
12     //省略set/get方法
13 }

現在我們是要通過一個學生ID,就能查詢出這個學生的課程、以及對應課程的成績。這個怎么來實現呢?在使用MyBatis為我們提供的級聯前,我們先來梳理一下從邏輯上是怎么一步一步查詢出來的。

我們要通過學生id查詢出學生的基本信息(包括課程以及對應的成績),但在學生POJO類中有一個對課程成績的List引用(暫時忽略學生證),也就是說我們無法一條簡單的sql語句(無join的sql語句)查詢出結果。但是!我們可以通過student_id在課程成績表中查詢出該學生的相應課程id(注意此時還是id),但我們此時還是沒辦法知道具體的課程名,再利用我們上一步中student_id查詢出的course_id通過課程表再來查詢出對應的課程名。重新梳理一下:

  1. 通過student_id在t_student表中查詢學生基本信息(name,sex)
  2. 通過student_id在t_course_score表中查詢學生對應的course_id
  3. 通過course_id在t_course表中查詢課程

那我們現在就從最底層做起,也就是通過course_id查詢出具體課程,因為這不會涉及到其他表。

 1 package day_8_mybatis.mapper;
 2 
 3 import day_8_mybatis.pojo.Course;
 4 
 5 /**
 6  * @author turbo
 7  *
 8  * 2016年11月4日
 9  */
10 public interface CourseMapper {
11     Course getCourse(int id);  //此id為course_id
12 }

再來看看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="day_8_mybatis.mapper.CourseMapper">
6     <select id="getCourse" parameterType="int" resultType="day_8_mybatis.pojo.Course">
7         select id, course_name as courseName, note from t_course where id = #{id}
8     </select>
9 </mapper>

現在已經寫好了通過course_id來查詢出具體課程。那么就是倒着走到第2步,通過student_id在t_course_score表中查詢學生對應的course_id,在最開始說過,在學生確定的情況下,課程和課程成績是一對一的關系,關於一對一的關系我們在上一篇已經講過,不妨再重溫一下。

 1 package day_8_mybatis.mapper;
 2 
 3 import day_8_mybatis.pojo.CourseScore;
 4 
 5 /**
 6  * @author turbo
 7  *
 8  * 2016年11月4日
 9  */
10 public interface CourseScoreMapper {
11     CourseScore findCourseScoreByStudentId(int id);
12 }

再來看看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="day_8_mybatis.mapper.CourseScoreMapper">
 6     <resultMap id="courseScoreMap" type="day_8_mybatis.pojo.CourseScore">
 7         <id property="id" column="id"/>
 8         <result property="studentId" column="student_id"/>
 9         <result property="score" column="score"/>
10         <association property="course" column="course_id" select="day_8_mybatis.mapper.CourseMapper.getCourse" />  <!--將查詢出來的course_id交給CouseMapper來查詢出具體課程信息-->
11     </resultMap>
12     
13     <select id="findCourseScoreByStudentId" parameterType="int" resultMap="courseScoreMap">
14         select id, student_id, course_id, score from t_course_score where student_id = #{id}
15     </select>
16 </mapper>

最后一步,也就是第1步,才進入正題MyBatis的一對多collection級聯關系。

 1 package day_8_mybatis.mapper;
 2 
 3 import day_8_mybatis.pojo.Student;
 4 
 5 /**
 6  * @author turbo
 7  *
 8  * 2016年11月4日
 9  */
10 public interface StudentMapper {
11     Student getStudent(int id);
12 }

關鍵在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="day_8_mybatis.mapper.StudentMapper">
 6     <resultMap type="day_8_mybatis.pojo.Student" id="studentMap">
 7         <id property="id" column="id"/>
 8         <result property="name" column="name"/>
 9         <result property="sex" column="sex"/>
10         <association property="selfCard" column="id" select="day_8_mybatis.mapper.SelfCardMapper.findSelfCardByStudentId"/>
11         <collection property="courseScoreList" column="id" select="day_8_mybatis.mapper.CourseScoreMapper.findCourseScoreByStudentId" />
12         </resultMap>
13     <select id="getStudent" parameterType="int" resultMap="studentMap">
14         select id, name, sex from t_student where id = #{id}
15     </select>
16 </mapper>

請好好仔細品味品味,仔細回顧整個查詢的邏輯過程。collection就是MyBatis為我們提供的第二個級聯關系——一對多。

最后上我們的測試代碼:

 1 package day_8_mybatis;
 2 
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5 
 6 import org.apache.ibatis.io.Resources;
 7 import org.apache.ibatis.session.SqlSession;
 8 
 9 import day_8_mybatis.mapper.StudentMapper;
10 import day_8_mybatis.pojo.Student;
11 import day_8_mybatis.util.SessionFactory2;
12 
13 /**
14  * 客戶端
15  * @author turbo
16  *
17  * 2016年11月4日
18  */
19 public class Main {
20 
21     /**
22      * @param args
23      * @throws IOException 
24      */
25     public static void main(String[] args) throws Exception {
26         String resource = "day_8_mybatis/mybatis-config.xml";        //獲取mybatis配置文件路徑
27         InputStream inputStream = Resources.getResourceAsStream(resource);
28         SqlSession sqlSession = SessionFactory2.getInstance(inputStream).openSession();
29         StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
30         Student student = studentMapper.getStudent(1);
31         System.out.println("學生:" + student.getName() + " 課程:" + student.getCourseScoreList().get(0).getCourse().getCourseName() + " 分數:" + student.getCourseScoreList().get(0).getScore());
32 
33     }
34 
35 }

//還是把day_8_mybatis.util.SessionFactory2代碼貼出來吧,SqlSessionFactory用到了單例模式,這也是MyBatis官方文檔所提倡的,具體可以移步之前寫的幾個關鍵類的作用域問題,《SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession作用域(Scope)和生命周期》,也可移步至《單例模式》《再說單例模式的線程安全問題》了解單例模式。

 1 package day_8_mybatis.util;
 2 
 3 import java.io.InputStream;
 4 
 5 import org.apache.ibatis.session.SqlSessionFactory;
 6 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 7 
 8 /**
 9  * @author turbo
10  *
11  * 2016年10月26日
12  */
13 public class SessionFactory2 {
14     private static SqlSessionFactory sqlSessionFactory;
15     
16     public static synchronized SqlSessionFactory getInstance(InputStream inputStream){
17         if (null == sqlSessionFactory){
18             sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
19         }
20         
21         return sqlSessionFactory;
22     }
23 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM