mybatis關聯關係對映

前路漫漫,兩手空空發表於2018-12-21

1、一對多關係(OneToMany)

在實際專案中,一對多是非常常見的關係。比如:一個班級可以有多個學生,一個學生只能屬於一個班級,班級和學生是一對多關係,而學生和班級是多對一關係。

資料庫中一對多關係通常使用主外來鍵關聯,外來鍵列應該在多方,即由多方維護關係(一對多中多的一方維護)

(1)建立資料庫

班級表t_clazz:

CREATE TABLE t_clazz(
    cid INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID',
    code VARCHAR(18) COMMENT '班級號',
    name VARCHAR(18) COMMENT '班級名'
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

說明:

ENGINE=InnoDB:MySQL 的資料庫引擎的設定

AUTO_INCREMENT=1:自動遞增列的 初始數值 = 1

DEFAULT CHARSET=utf8:預設字符集 = utf8

學生表t_student:

CREATE TABLE t_student(
    sid INT PRIMARY KEY AUTO_INCREMENT COMMENT '學生ID',
    name VARCHAR(18) COMMENT '姓名',
    sex VARCHAR(18) COMMENT '性別',
    age INT COMMENT '年齡',
    clazz_cid INT, #外來鍵:班級ID
    CONSTRAINT fk_stu FOREIGN KEY(clazz_cid) REFERENCES t_clazz(cid)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

(2)持久化類

建立一個Clazz類和一個Student類,分別對映t_clazz表和t_student表。

// 班級類
public class Clazz implements Serializable {
    private Integer cid;
    private String code;
    private String name;
    //班級和學生是一對多的關係,即一個班級可以有多個學生
    private List<Student> studentList;
    //省略構造方法和set/get方法……
}
// 學生類
public class Student implements Serializable {
    private Integer sid;
    private String name;
    private String sex;
    private Integer age;
    //學生和班級是多對一的關係,即一個學生只屬於一個班級
    private Clazz clazz;
    //省略構造方法和set/get方法……
}

(3)介面

在com.zking.dao中建立實體類的介面

// 班級類介面
public interface ClazzService{
    // SQL1:新增Clazz物件
    public int addClazz(Clazz clazz);

    // SQL2:刪除Clazz物件
    public int deleteClazzById(Clazz clazz);

    // SQL3:修改Clazz物件
    public int updateClazz(Clazz clazz);
    
    // SQL4:根據ID查詢Clazz物件
    public Clazz selectClazzById(Integer cid);

    // SQL5:查詢所有Clazz物件
    public List<Clazz> selectAllClazz();

    // SQL6:模糊查詢Clazz物件
    public List<Clazz> selectClazzWhere(Clazz clazz);

    // SQL7:多表聯查:查詢班級資訊及學員資訊
    public Clazz selectClazzAndStudent(Integer cid);

    // SQL8:包含查詢
    public List<Clazz> selectClazzIn(Clazz clazz);

}
// 學生類介面
public interface StudentService{
    // SQL1:新增Student物件
    public int addStudent(Student student);
    
    // SQL2:根據SID刪除Student物件
    public int deleteStudentById(Integer sid);

    // SQL3:修改Student物件
    public int updateStudent(Clazz clazz);

    // SQL4:根據SID查詢Student物件
    public Student selectStudentById(Integer cid);

    // SQL5:查詢所有Student物件
    public List<Student> selectAllStudent();

    // SQL6:模糊查詢Student物件
    public List<Student> selectStudentWhere(Student student);

    // SQL7:多表聯查:查詢班級資訊及學員資訊
    public Student selectClazzAndStudent(Integer sid);

    // SQL8:包含查詢
    public List<Student> selectStudentIn(Student student);
}

(4)物件/關係對映檔案

ClazzMapper.xml程式碼:

<mapper namespace=”com.zking.dao.ClazzService”>

    <!--SQL1 :根據cid查詢Clazz,返回Clazz物件-->
    <select id=”selectClazzById” parameterType=”Integer” resultMap=”clazzMapper”>
        SELECT * FROM t_clazz WHERE cid=#{cid}
    </select>

    <!--對映Clazz物件的resultMap(結果對映集)-->
    <resultMap id=”clazzMapper” type=”com.zking.pojo.Clazz”>
        <id property=”cid” column=”CID” />
        <result property=”code” column=”CODE” />
        <result property=”name” column=”NAME” />
        <!--一對多關聯對映:
        Collection表示關聯的是集合
        Property=”studentList”對應的是Clazz類中List<Student> studentList屬性名
        ofType="com.zking.pojo.Student"表示對映的類為List<Student>中的Student
        fetchType=”lazy”表示懶載入,即使用時才例項化
        -->
    <collection property="studentList" ofType="com.zking.pojo.Student"  fetchType=”lazy”>
        <id property=”sid” column=”SID” />
        <!--<result>中出現的屬性、列名不一定全是類中的屬性,如果只查出要sid和name,那麼後面的sex,age都可以不用寫這裡的name,sex,age最好不要和上面<resultMap>中的name,sex,age名字重複property對應com.zking.pojo.Student實體類中的屬性,column對應表中的欄位名-->
        <result property="name" column="name" />
        <result property="sex" column="sex" />
        <result property="age" column="age" />
    </collection>
    <!--寫法二:
    將cid做為引數傳遞給com.zking.dao.StudentService.selectClazzStudent
    -->
    <collection property=”studentList” ofType=”com.zking.pojo.Student”
        column=”cid” Select=”com.zking.dao.StudentService.selectClazzStudent”>
    </collection>
    </resultMap>
</mapper>

StudentMapper.xml程式碼:

<mapper namespace=”com.zking.dao.StudentService”>

    <!--SQL1:根據id查詢Student,返回Student物件-->
    <select id=”selectStudentById” parameterType=”Integer” resultMap=”studentMapper”>
        SELECT * FROM t_student WHERE sid=#{sid}
    </select>

    <!--對映Student物件的resultMap(結果對映集)-->
    <resultMap id=”studentMapper” type=”com.zking.pojo.Student”>
        <id property=”sid” column=”sid” />
        <result property=”name” column=”name” />
        <result property=”sex” column=”sex” />
        <result property=”age” column=”age” />
    <!--多對一關聯對映:association
       assoclation表示對映一個物件
    -->
    <association property="clazz" javaType="com.zking.pojo.Clazz">
	    <id property=”id” column=”id” />
		<result property="code" column="code" />
        <result property="name" column="name" />
</association>

    <!--方式二:
        將CLAZZ_CID做為引數傳遞給com.zking.dao.ClassService.selectClazzById
    -->
    <association property="clazz" javaType="com.zking.pojo.Clazz"
    column=”CLAZZ_CID” select=”com.zking.dao.ClazzService.selectClazzById”>
	</association>

</resultMap>

    <!--SQL2:查詢所有Student物件-->
    <select id=”selectAllStudent” resultType=”com.zking.pojo.Student”>
        SELECT * FROM t_student
    </select>

    <!--SQL3:根據班級號查詢所有Student物件-->
    <select id=”selectClazzStudent” paramerType=”Integer”  resultType=”com.zking.pojo.Student”>
        SELECT * FROM t_student WHERE Clazz_cid = #{Clazz.cid}
    </select>
</mapper>

(5)測試

public class TestDemo{
        private SqlSession sqlSession;

	@Before
	public void before(){
	    try {
			InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
			SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
			sqlSession = sqlSessionFactory.openSession();
	    }catch(Exception e){
			e.printStackTrace();
	    }
	}

	@After
	public void after(){
            sqlSession.commit();
            sqlSession.close();
	}

    @Test
    public void testClazz(){
	        ClazzDao clazzDao = sqlSession.getMapper(ClazzDao.class);
	        Clazz clazz = clazzDao.getClazzById(2);
	        System.out.println(clazz);
	        System.out.println(clazz.getId());
	        System.out.println(clazz.getCode());
                System.out.println(clazz.getStudentList());
    }

    @Test
    public void testStudent(){
	        StudentDao studentDao = sqlSession.getMapper(StudentDao.class);
	        Student student = studentDao.getStudentById(2);
	        System.out.println(student);
	        System.out.println(student.getName());
	        System.out.println(student.getSex());
                System.out.println(student.getClazz().getCode());
    }
}

2、多對多(ManyToMany)

在實際專案中,多對多是非常常見的關係。比如:課程管理中,一個學生對應多個課程,一個課程對應多個學生,他們之間多個一對多組成,就形成了多對多。

對於資料庫中多對多關係建議使用一個中間表來維護關係

(1)建立資料庫

學生表 t_student:

create table t_student(
    student_id		INT PRIMARY KEY AUTO_INCREMENT,
    student_name		VARCHAR(10),
    student_sex		VARCHAR(10),	#性別
    student_major	VARCHAR(10),	#專業
    student_grade	VARCHAR(10)		#年級
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

課程表 t_course:

create table t_course(
    course_id		INT	PRIMARY KEY AUTO_INCREMENT,		#課程ID
    course_code		VARCHAR(30),		#課程編號
    course_name		VARCHAR(30)			#課程名
)ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

學生vs課程關係表 t_student_course:

create table t_student_course (
    student_id	INT,		#學生ID,FK
    course_id	INT,		#課程ID,FK
    CONSTRAINT fk_student_id FOREIGN KEY(student_id) REFERENCES t_student(student_id),
    CONSTRAINT fk_course_id FOREIGN KEY(course_id) REFERENCES t_course(course_id)
)ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; 

(2)持久化類

學生實體Student:

public class Student {  
    private Integer studentID;  
    private String studentName;		// 姓名  
    private String studentSex;		// 性別  
    private String studentMajor;	// 專業  
    private String studentGrade;	// 年級
    //一對多:即一個學生物件對應多個課程物件
    private List<Course> courses;  // 所選的課程
    //省略getter和setter方法
    //構造方法及toString()
}

課程實體Course:

public class Course {  
    private Integer courseID;  
    private String courseCode;		//課程編號  
    private String courseName;		//課程名稱
    //一對多:即一個課程物件對應多個學生物件
    private List<Student> students;		//選課學生
    //省略getter和setter方法
    //構造方法及toString()
} 

注意:關係表不需要建立實體類。

(3)介面

注意:在學習過程中,我們會為Student類建立StudentService介面,在該介面中指定Student類的CURD操作,會為Course類建立CourseService介面,在該介面中指定Course類的CURD操作。

為操作便捷,這裡我統一寫個介面:Many2ManyService,該介面負責Student類和Course類的CURD操作。

在com.zking.dao中建立實體類的介面:

public interface Many2ManyService {  
    //SQL1:插入student物件  
    public int addStudent(Student student);  

    //SQL2:插入course物件  
    public int addCourse(Course course);  

    //SQL3:根據student_id查詢學生  
    public Student selectStudentById(Integer student_id);  

    //SQL4:通過course_id查詢課程  
    public Course selectCourseById(Integer course_id);  

    //SQL5:學生x選課y  
    public int studentSelectCourse(Student student, Course course);  

    //SQL6:查詢比指定student_id值小的學生資訊  
    public List<Student> getStudentByIdOnCondition(Integer student_id);  

    //SQL7:查詢student級聯查詢出所選的course並且組裝成完整的物件  
    public Student getStudentByIdWithCourses(Integer course_id);  
}

(4)物件/關係對映檔案

為Many2ManyService介面建立Mapper對映檔案,實現介面中的方法:

<mapper namespace="com.mybatis.mappers.Many2ManyService">

    <!-- SQL1:插入student物件  -->
    <insert id="addStudent" parameterType="Student">  
        INSERT INTO
        t_student(student_name, student_sex, student_major, student_grade)
        VALUES(
        #{studentName},#{studentSex},#{studentMajor},#{studentGrade}
        )  
    </insert>

    <!-- SQL2:插入course物件 -->
    <insert id="addCourse" parameterType="Course">  
        INSERT INTO 
        t_course(course_code, course_name)
        VALUES(
        #{courseCode}, #{courseName}
        )  
    </insert>

    <!--SQL: 新增關係表-->
    <insert id="studentSelectCourse">
        <!-- param1代表方法中第一個引數 以此類推 -->  
        INSERT INTO
        t_student_course(student_id, course_id)  
        VALUES(
        //第一個引數的student_id,第二個引數的course_id
        #{param1.studentID}, #{param2.courseID}
        )  
    </insert> 

    <!--SQL3:根據student_id查詢學生-->
    <select id="selectStudentById" parameterType="Integer" resultType="Student">  
        SELECT
        student_id, student_name, student_sex, student_major, student_grade 
        from
        t_student 
        Where
        student_id = #{studentID}
    </select>  

    <!--SQL4:通過course_id查詢課程-->
        <select id="selectCourseById" parameterType="Integer" resultType="Course">  
        SELECT
        Course_id AS courseID, course_code AS courseCode, course_name AS courseName
        FROM
        t_course
        Where
        Course_Id = #{courseID}  
    </select>

     <!--SQL5:學生x選課y-->
    <insert id="studentSelectCourse">
        <!-- param1代表方法中第一個引數 以此類推 -->  
        INSERT INTO
        t_student_course(student_id, course_id)  
        VALUES(
        //第一個引數的student_id,第二個引數的course_id
        #{param1.studentID}, #{param2.courseID}
        )  
    </insert> 

    <!-- 如果有特殊符號的話 需要用 <![CDATA[ 特殊符號 ]]>  例如 < & 等等 -->  
    <select id="getStudentByIdOnCondition" parameterType="Integer" resultType="Student">  
        <![CDATA[  
        SELECT
        student_id, student_name, student_sex, student_major, student_grade
        FROM
        t_student
        WHERE
        student_id < #{studentID}
        ]]>  
    </select>

    <!--  這裡使用了巢狀結果ResultMap的方式進行級聯查詢,當然也可以使用巢狀查詢select -->  
    <!-- 對映一個基本的Student查詢結果 -->  
    <resultMap id="StudentResult" type="Student">  
        <id property="id" column="id"/>  
        <result property="name" column="name"/>  
        <result property="gender" column="gender"/>  
        <result property="major" column="major"/>  
        <result property="grade" column="grade"/>  
    </resultMap>  

    <!-- 繼承上面那個基本的對映,再擴充套件出級聯查詢 -->  
    <resultMap id="StudentResultWithCourses" type="Student" extends="StudentResult">  
        <collection property="courses" resultMap="CourseResult"></collection>  
    </resultMap>

    <!-- 這裡特別是column="cid"是和select語句中的 c.id as cid對應的 一定一定一定要對應起來 -->  
    <resultMap id="CourseResult" type="Course">
        <id property="id" column="cid"/>
        <result property="courseCode" column="course_code"/>
        <result property="courseName" column="course_name"/>
    </resultMap> 

    <!--    
    注意:查詢語句的中的c.id as cid這個地方,避免名字相同出現查詢結果不正確的情況  
    同時在id="CourseResult"的resultMap中也有與這裡對應的設定要特別特別注意  
    -->  
    <select id="getStudentByIdWithCourses" parameterType="Integer"  resultMap="StudentResultWithCourses">  
        SELECT 
        s.id, s.name, s.gender, s.major, s.grade, c.id AS cid, c.course_code, c.course_name, sc.id, sc.student_id, sc.course_id  
        FROM
        student s, course c, student_course sc  
        WHERE    
        s.id=#{id}  AND  s.id=sc.student_id  AND  sc.course_id=c.id  
    </select>
</mapper>  

(5)測試

public class Many2ManyServiceTest {   
    private SqlSession sqlSession;
    Private Many2ManyService mapper;
	@Before
	public void before(){
	    try {
		InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		sqlSession = sqlSessionFactory.openSession();
                // 使用sqlsession獲得對映介面的實現類物件,介面的引用指向實現類的物件  
                mapper = sqlSession.getMapper(Many2ManyService.class);
	    }catch(Exception e){
			e.printStackTrace();
	    }
	}

	@After
	public void after(){
        sqlSession.commit();
        sqlSession.close();
   	}

    @Test  
    public void test_insertStudent() {
        Student student = new Student(1,"Suwu150","男","計算機","21");  
        mapper.addStudent(student);  
        session.commit();  
        System.out.println("執行完畢");  
    } 
    @Test  
    public void test_insertCourse(){  
        SqlSession session=null;  
        session = MyBatisSqlSessionFactory.openSession();//使用封裝之後的類  
        // 使用sqlsession獲得對映介面的實現類物件,介面的引用指向實現類的物件  
        Many2ManyMapper mapper = session.getMapper(Many2ManyMapper.class);  
        mapper.insertCourse(new Course(1,"001","corejava"));  
        mapper.insertCourse(new Course(2,"002","oracle"));  
        session.commit();  
        System.out.println("執行完畢");  
    }  
    @Test  
    public void test_selectStudentById(){  
        SqlSession session=null;  
        session = MyBatisSqlSessionFactory.openSession();//使用封裝之後的類  
        // 使用sqlsession獲得對映介面的實現類物件,介面的引用指向實現類的物件  
        Many2ManyMapper mapper = session.getMapper(Many2ManyMapper.class);  
        Student student = mapper.selectStudentById(2);  
        System.out.println(student);  
        System.out.println("執行完畢");  
    }  
    @Test  
    public void test_selectCourseById(){  
        SqlSession session=null;  
        session = MyBatisSqlSessionFactory.openSession();//使用封裝之後的類  
        // 使用sqlsession獲得對映介面的實現類物件,介面的引用指向實現類的物件  
        Many2ManyMapper mapper = session.getMapper(Many2ManyMapper.class);  
        Course course = mapper.selectCourseById(2);  
        System.out.println(course);  
        System.out.println("執行完畢");  
    }  
    @Test  
    public void test_studentSelectCourse(){  
        SqlSession session=null;  
        session = MyBatisSqlSessionFactory.openSession();//使用封裝之後的類  
        // 使用sqlsession獲得對映介面的實現類物件,介面的引用指向實現類的物件  
        Many2ManyMapper mapper = session.getMapper(Many2ManyMapper.class);  
        Student student=new Student();  
        Course course=new Course();  
        student.setId(2);  
        course.setId(1);  
        mapper.studentSelectCourse(5, student, course);  
        session.commit();  
    }
    @Test  
    public void test_getStudentByIdOnCondition(){  
        SqlSession session=null;  
        session = MyBatisSqlSessionFactory.openSession();//使用封裝之後的類  
        // 使用sqlsession獲得對映介面的實現類物件,介面的引用指向實現類的物件  
        Many2ManyMapper mapper = session.getMapper(Many2ManyMapper.class);  
        List<Student> list = mapper.getStudentByIdOnCondition(3);  
        System.out.println(list);  
    }
    @Test  
    public void test_getStudentByIdWithCourses(){  
        SqlSession session=null;  
        session = MyBatisSqlSessionFactory.openSession();//使用封裝之後的類  
        // 使用sqlsession獲得對映介面的實現類物件,介面的引用指向實現類的物件  
        Many2ManyMapper mapper = session.getMapper(Many2ManyMapper.class);  
        Student student = mapper.getStudentByIdWithCourses(2);  
        System.out.println(student);  
    }  
}  

 

相關文章