MyBatisCRUD註解使用、一對多及多對一詳解

MyBatis進階

1、MyBatis回顧

MyBatis就是用來幫我們簡化數據庫操作的。

創建第一個MyBatis程序步驟

  1. 創建數據庫和表
  2. 創建一個簡單Maven項目,添加Javaweb框架
  3. 導入MyBatis、mysql、junit、lombok依賴
  4. 編輯核心配置文件Mybatis.config.xml
  5. 編寫MyBatisUtil工具類
  6. 創建實體類,一個實體類和一張數據庫中的表對應
  7. 創建操作實體類的對應接口
  8. 編寫接口對應的xml配置文件
  9. 將xml配置文件註冊到Mybatis.config.xml編輯核心配置文件中
  10. 編寫測試類,使用junit測試工具進行測試

新增業務邏輯步驟

  1. 數據庫建表
  2. 編寫實體類
  3. 編寫接口
  4. 編寫接口對應xml配置文件
  5. 將xml配置文件註冊到MyBatis核心依賴文件Mybatis.config.xml中
  6. 測試

改進

  1. 問題:在工具類中,我們所有的增刪改操作都要手動去提交事務,很麻煩

    解決方案:在工具類中重載getSession()方法,添加參數flag

// 原有的獲取sqlSession連接方法
public static SqlSession getSession() {
    return sqlSessionFactory.openSession();
}

// 新增重載獲取sqlSession連接方法,當參數爲true時,默認開啓事務提交
public static SqlSession getSession(boolean flag) {
    return sqlSessionFactory.openSession(flag);
}

2、使用註解開發

注意:雖然推薦使用註解進行簡化開發,但是MyBatis是一個例外,MyBatis推薦使用對應的xml文檔進行配置

1、面向接口編程思想

本質就是爲了解耦

  1. 增加系統可擴展性
  2. 接口中方法複用
  3. 利於管理
  4. 利於分層開發
    1. dao層、接口對應Impl實現類(業務邏輯)
    2. service、servlet等(控制層)
    3. html、css等(視圖層)

2、使用註解編程的步驟

  1. 在接口方法上使用註解

    與xml文檔配置一樣,每一個xml文檔標籤都有一個對應的註解

    在註解後的括號內些對應的SQL語句,寫了註解就不用再寫接口對應的xml文檔了

    //例如
    //根據ID獲得信息
    @Select("select * from user where id = #{id}")
    User getUserById(int id);
    
  2. 綁定到MyBatis核心配置文件mybatis-config.xml中

    <!--與使用xml文檔進行配置一樣-->
    <mapper class="org.westos.mapper.UserMapper2"/>
    
  3. 進行測試

對於簡單的CRUD(增刪改查)來說,使用註解會很方便,但是對於複雜的查詢,還是使用xml文檔配置比較好

xml配置和註解可以同時使用嗎?有哪些條件

  1. 註解和接口可以同時使用,但是對於複雜的代碼來說,使用xml配置更簡潔明瞭

  2. 接口要註冊在MyBatis核心配置文件mybatis-config.xml中,使用xml文檔配置還要在文檔中配置對應的接口

  3. 如果接口和接口.xml在同一個包下,只需要註冊接口就好了,優先使用class屬性進行配置

注意

  1. 註解的SQL語句和xml配置的SQl語句是相同的
  2. 超過多個參數必須要使用@param註解,或者使用Map集合進行參數傳遞(鍵值對)
  3. 對於可能存在的SQL注入問題
    1. #{}

      PrepareStatement,沒有SQL注入,建議使用

    2. ${}

      statement,存在SQL注入,不建議使用

3、CRUD註解練習

使用註解完成以下練習

  1. 通過id查詢用戶
  2. 更新用戶信息
  3. 添加用戶
  4. 根據id刪除用戶
  5. 最後使用junit測試

規範:增刪改要自動提交事務

//實體類User,對應數據庫中的表user
package org.westos.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
 private int id;
 private String name;
 private String pwd;
}


//接口
package org.westos.mapper;

import org.apache.ibatis.annotations.*;
import org.westos.pojo.User;

public interface UserMapper2 {

 //根據ID獲得信息
 @Select("select * from user where id = #{id}")
 User getUserById(int id);

 //更新用戶
 @Update("update user set name=#{name},pwd=#{pwd} where id=#{id}")
 int updateUser(@Param("id") int id, @Param("name") String name, @Param("pwd") String pwd);

 //插入用戶
 @Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})")
 int insertUser(User user);

 //刪除用戶
 @Delete("delete from user where id=#{id}")
 int deleteUser(int id);
}


//使用junit測試工具進行測試
package org.westos.mapper;

import org.junit.Test;
import org.westos.pojo.User;
import org.westos.utils.MyBatisUtil;

public class UserMapper2Test {

 //測試根據ID獲得信息
 @Test
 public void testGetUserById() {
     UserMapper2 mapper = MyBatisUtil.getSession().getMapper(UserMapper2.class);
     User userById = mapper.getUserById(1);
     System.out.println(userById);
 }

 //更新用戶
 //增刪改都要設置自動提交事務,在工具類中設置getSession()方法重載,添加布爾類型參數
 @Test
 public void testUpdateUser() {
     UserMapper2 mapper = MyBatisUtil.getSession(true).getMapper(UserMapper2.class);
     int i = mapper.updateUser(1, "張三", "666666");
     System.out.println(i);
 }

 //插入新用戶
 @Test
 public void testInsertUser() {
     UserMapper2 mapper = MyBatisUtil.getSession(true).getMapper(UserMapper2.class);
     int i = mapper.insertUser(new User(2,"李四","111111"));
     System.out.println(i);
 }

 //刪除用戶
 @Test
 public void testDeleteUser() {
     UserMapper2 mapper = MyBatisUtil.getSession(true).getMapper(UserMapper2.class);
     int i = mapper.deleteUser(4);
     System.out.println(i);
 }
}

mybatis-config.xml配置文檔中進行配置

<mappers>
    <mapper class="org.westos.mapper.UserMapper2"/>
</mappers>

3、一對多(collection)

在一個學校裏有老師也有學生,一個老師可以有多個學生,所以針對某一門課,老師和學生是一對多的關係

一對多從一的一方的角度出發,少的找多的(老師找學生),是包含的關係(一個老師下包含多個學生,所以老師類要有一個students集合,存放包含的學生)

  1. 設計數據庫表

    老師有兩個屬性,id和name,而學生除了id和name之外,還有另一個屬性tid(對應教師的id)

    使用sql語句,創建表teacher和student

    CREATE TABLE `teacher` (
      `id` INT(10) NOT NULL,
      `name` VARCHAR(30) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8
    
    INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老師'); 
    
    CREATE TABLE `student` (
      `id` INT(10) NOT NULL,
      `name` VARCHAR(30) DEFAULT NULL,
      `tid` INT(10) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `fktid` (`tid`),
      CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8
    
    
    INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); 
    INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小紅', '1'); 
    INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小張', '1'); 
    INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 
    INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1'); 
    
  2. 設計實體類

    實體類Teacher中,除了id和name屬性之外,還應該有一個學生集合List<Student>,用來存儲該老師對應的學生

    實體類Student中,除了id和name屬性之外,還應該有對應教師的id

    使用lombok創建實體類

    //Teacher類
    package org.westos.pojo;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import java.util.List;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Teacher {
        private int id;
        private String name;
        //存儲名下的學生集合
        List<Student> students;
    }
    
    //Student類
    package org.westos.pojo;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Student {
        private int id;
        private String name;
        //對應的教師id
        private int tid;
    }
    
  3. 編寫接口

    我們現在想讀取id=1的教師的id和name,並將該教師名下的所有學生集合students輸出出來

    編寫接口TeacherMapper

    package org.westos.mapper;
    import org.westos.pojo.Teacher;
    
    public interface TeacherMapper {
        //根據老師的ID得到老師信息和對應學生的信息
        public Teacher getTeacherById(int id);
    }
    
  4. 編寫接口對應的xml配置文檔

    先獲取Teacher的id和name屬性,至於students集合,我們沒辦法從teacher一張用戶表中查出,而是要使用student和teacher表聯合查詢。

    這個時候就要使用collection標籤,將student表中的信息映射出來

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="org.westos.mapper.TeacherMapper">
    
        <!--因爲teacher對象的students屬性沒辦法直接從teacher表中查詢出來,所以要使用連表查詢-->
        <!--凡是我們無法直接從數據庫表中得到的數據,都要做resultMap集合映射-->
        <select id="getTeacherById" resultMap="TeacherStudent">
            SELECT t.id tid,t.name tname,s.`id` sid,s.`name` sname ,s.`tid` tid
            FROM student s,teacher t
            WHERE t.`id`=1
        </select>
    
        <resultMap id="TeacherStudent" type="org.westos.pojo.Teacher">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
    
            <!--對於teacher對象的students屬性,我們無法直接從teacher表中得到,所以要做映射-->
            <!--類型是Student-->
            <collection property="students" ofType="org.westos.pojo.Student">
                <!--對照上面的sql語句,一一進行別名-->
                <!--左邊是Java類中的屬性名稱,右邊是數據庫的字段名稱-->
                <result property="id" column="sid"/>
                <result property="name" column="sname"/>
                <result property="tid" column="tid"/>
            </collection>
        </resultMap>
    
    </mapper>
    
  5. 配置到MyBatis核心配置文檔

    <mapper class="org.westos.mapper.TeacherMapper"/>
    
  6. 使用junit測試工具測試

    package org.westos.mapper;
    
    import org.junit.Test;
    import org.westos.pojo.Teacher;
    import org.westos.utils.MyBatisUtil;
    
    public class TeacherMapperTest {
    
        @Test
        public void testGetTeacherById(){
            //得到mapper對象,因爲是查詢,就沒有開啓事務
            TeacherMapper mapper = MyBatisUtil.getSession().getMapper(TeacherMapper.class);
            Teacher teacherById = mapper.getTeacherById(1);
            System.out.println(teacherById);
        }
    }
    

小結:

一對多是指從一的一方(教師)看待,在設計實體類的時候,教師下應該設計一個包含多的一方(學生)的集合,用來存儲學生的信息。而學生應該設置一個屬性用來存放教師的id,以便一對多的關係映射

4、多對一(association)

在指定的一門課上,全班的學生對應一個老師,學生有多個,而老師只有一個,這就是一對多

多對一從多的一方的角度出發,多的去找少的(學生找老師),是關聯關係(一個學生關聯一個老師,所以student類中要有一個teacher對象屬性,用來關聯對應的老師)

  1. 設計數據庫表

    老師有兩個屬性,id和name,而學生除了id和name之外,還有另一個屬性tid(對應教師的id)

    使用sql語句,創建表teacher和student

    CREATE TABLE `teacher` (
      `id` INT(10) NOT NULL,
      `name` VARCHAR(30) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8
    
    INSERT INTO teacher(`id`, `name`) VALUES (1, '秦老師'); 
    
    CREATE TABLE `student` (
      `id` INT(10) NOT NULL,
      `name` VARCHAR(30) DEFAULT NULL,
      `tid` INT(10) DEFAULT NULL,
      PRIMARY KEY (`id`),
      KEY `fktid` (`tid`),
      CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8
    
    
    INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); 
    INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小紅', '1'); 
    INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小張', '1'); 
    INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 
    INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1'); 
    
  2. 設計實體類

    對於Student類,每一個Student對象,不僅有id和name屬性,還應該有一個teacher屬性,用來關聯對應的老師的信息(多對一是多的找少的,也就是學生找老師)

    對於Teacher類,含有id和name兩個屬性

    //Student類
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Student {
        private int id;
        private String name;
        private Teacher teacher;
    }
    
    //Teacher類
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class Teacher {
        private int id;
        private String name;
    }
    
  3. 編寫接口

    獲得所有的學生的信息,包括學生對應的老師的信息

    package org.westos.mapper;
    
    import org.westos.pojo.Student;
    import java.util.List;
    
    public interface StudentMapper {
    
        //獲取所有的學生的及學生對應老師的信息
        List<Student> getStudents();
    }
    
  4. 編寫對應的mapper.xml

    在student表中並不能查詢到teacher的信息,所以需要使用連表查詢

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="org.westos.mapper.StudentMapper">
    
        <!--先寫好sql語句,運行無誤後粘貼過來-->
        <!--因爲無法直接拿到teacher的信息,所以需要使用resultMap映射-->
        <select id="getStudents" resultMap="StudentTeacher">
            SELECT s.id sid,s.name sname,t.id tid,t.name tname
            FROM student s,teacher t
            WHERE s.`tid`=t.`id`
        </select>
    
        <!--student中的id和name可以直接從student表中獲得-->
        <resultMap id="StudentTeacher" type="org.westos.pojo.Student">
            <id property="id" column="sid"/>
            <result property="name" column="sname"/>
    
            <!--teacher對象無法從student表中獲得,所以要連表查詢-->
            <association property="teacher" javaType="org.westos.pojo.Teacher">
                <!--對應上面的sql語句,左邊爲在Java類中的屬性名,右邊爲表中字段名-->
                <result property="id" column="tid"/>
                <result property="name" column="tname"/>
            </association>
        </resultMap>
    </mapper>
    
  5. 註冊到MyBatis核心配置文檔

    <mapper class="org.westos.mapper.StudentMapper"/>
    
  6. 使用junit測試工具進行測試

    package org.westos.mapper;
    
    import org.junit.Test;
    import org.westos.pojo.Student;
    import org.westos.utils.MyBatisUtil;
    import java.util.List;
    
    public class StudentMapperTest {
    
        @Test
        public void testGetStudents() {
            StudentMapper mapper = MyBatisUtil.getSession().getMapper(StudentMapper.class);
            //得到student集合
            List<Student> students = mapper.getStudents();
            //遍歷打印
            for (Student student : students) {
                System.out.println(student);
            }
        }
    }
    

5、resultMap小結

結果集映射

  1. 所有無法直接返回結果的(引用類型),都需要做結果集映射
  2. 當實體類的屬性名與表中的字段名不一致時,要使用結果集映射
  3. resultMap標籤中的標籤及屬性含義
    1. id,對應表的主鍵
    2. result,對應表中的普通字段
    3. property,實體類中的屬性名
    4. column,表中的字段名
    5. association,多對一情況使用,用來關聯其他對象
    6. collection,一對多情況使用,用來包含其他對象
    7. ofType,一對多情況使用,表示包含的對象的類型
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章