天天看點

Mybatis_02

typora-copy-images-to: img

一.接口代理方式實作Dao

1.1 代理開發方式介紹

​ 采用 Mybatis 的代理開發方式實作 DAO 層的開發,這種方式是我們後面進入企業的主流。

Mapper 接口開發方法隻需要程式員編寫Mapper 接口(相當于Dao 接口),由Mybatis 架構根據接口定義建立接口的動态代理對象,代理對象的方法體同上邊Dao接口實作類方法。

Mapper 接口開發需要遵循以下規範:

1) Mapper.xml檔案中的namespace與mapper接口的全限定名相同

2) Mapper接口方法名和Mapper.xml中定義的每個statement的id相同

3) Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql的parameterType的類型相同

4) Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同

總結:

接口開發的方式: 程式員隻需定義接口,就可以對資料庫進行操作,那麼具體的對象怎麼建立?

1.程式員負責定義接口

2.在操作資料庫,mybatis架構根據接口,通過動态代理的方式生成代理對象,負責資料庫的crud操作

1.2.編寫StudentMapper接口

Mybatis_02

1.3 測試代理方式

public Student selectById(Integer id) {
        Student stu = null;
        SqlSession sqlSession = null;
        InputStream is = null;
        try{
            //1.加載核心配置檔案
            is = Resources.getResourceAsStream("MyBatisConfig.xml");

            //2.擷取SqlSession工廠對象
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

            //3.通過工廠對象擷取SqlSession對象
            sqlSession = sqlSessionFactory.openSession(true);

            //4.擷取StudentMapper接口的實作類對象
            StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); // StudentMapper mapper = new StudentMapperImpl();

            //5.通過實作類對象調用方法,接收結果
            stu = mapper.selectById(id);

        } catch (Exception e) {

        } finally {
            //6.釋放資源
            if(sqlSession != null) {
                sqlSession.close();
            }
            if(is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        //7.傳回結果
        return stu;
    }
           

1.4 源碼分析

  • 分析動态代理對象如何生成的?

    通過動态代理開發模式,我們隻編寫一個接口,不寫實作類,我們通過 getMapper() 方法最終擷取到 org.apache.ibatis.binding.MapperProxy 代理對象,然後執行功能,而這個代理對象正是 MyBatis 使用了 JDK 的動态代理技術,幫助我們生成了代理實作類對象。進而可以進行相關持久化操作。

  • 分析方法是如何執行的?

    動态代理實作類對象在執行方法的時候最終調用了 mapperMethod.execute() 方法,這個方法中通過 switch 語句根據操作類型來判斷是新增、修改、删除、查詢操作,最後一步回到了 MyBatis 最原生的 SqlSession 方式來執行增删改查。

1.5 知識小結

接口代理方式可以讓我們隻編寫接口即可,而實作類對象由 MyBatis 生成。

實作規則 :

  1. 映射配置檔案中的名稱空間必須和 Dao 層接口的全類名相同。
  2. 映射配置檔案中的增删改查标簽的 id 屬性必須和 Dao 層接口的方法名相同。
  3. 映射配置檔案中的增删改查标簽的 parameterType 屬性必須和 Dao 層接口方法的參數相同。
  4. 映射配置檔案中的增删改查标簽的 resultType 屬性必須和 Dao 層接口方法的傳回值相同。 
  5. 擷取動态代理對象 SqlSession 功能類中的 getMapper() 方法。

二. 動态sql語句

2.1 動态sql語句概述

​ Mybatis 的映射檔案中,前面我們的 SQL 都是比較簡單的,有些時候業務邏輯複雜時,我們的 SQL是動态變化的,此時在前面的學習中我們的 SQL 就不能滿足要求了。

參考的官方文檔,描述如下:

Mybatis_02

2.2 動态 SQL 之<if>

我們根據實體類的不同取值,使用不同的 SQL語句來進行查詢。比如在 id如果不為空時可以根據id查詢,如果username 不同空時還要加入使用者名作為條件。這種情況在我們的多條件組合查詢中經常會碰到。

如下圖:

<select id="findByCondition" parameterType="student" resultType="student">
    select * from student
    <where>
        <if test="id!=0">
            and id=#{id}
        </if>
        <if test="username!=null">
            and username=#{username}
        </if>
    </where>
</select>

           

當查詢條件id和username都存在時,控制台列印的sql語句如下:

… … …
     //獲得MyBatis架構生成的StudentMapper接口的實作類
    StudentMapper mapper = sqlSession.getMapper( StudentMapper.class);
    Student condition = new Student();
    condition.setId(1);
    condition.setUsername("lucy");
    Student student = mapper.findByCondition(condition);
    … … …
           
Mybatis_02

當查詢條件隻有id存在時,控制台列印的sql語句如下:

… … …
 //獲得MyBatis架構生成的UserMapper接口的實作類
 StudentMapper mapper = sqlSession.getMapper( StudentMapper.class);
    Student condition = new Student();
    condition.setId(1);
    Student student = mapper.findByCondition(condition);
… … …

           
Mybatis_02

總結文法:

<where>:條件标簽。如果有動态條件,則使用該标簽代替 where 關鍵字。
<if>:條件判斷标簽。
<if test=“條件判斷”>
	查詢條件拼接
</if>
           

2.3 動态 SQL 之<foreach>

循環執行sql的拼接操作,例如:SELECT * FROM student WHERE id IN (1,2,5)。

<select id="findByIds" parameterType="list" resultType="student">
   select * from student
   <where>
       <foreach collection="array" open="id in(" close=")" item="id" separator=",">
           #{id}
       </foreach>
   </where>
</select>
           

測試代碼片段如下:

… … …
 //獲得MyBatis架構生成的UserMapper接口的實作類
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int[] ids = new int[]{2,5};
List<Student> sList = mapper.findByIds(ids);
System.out.println(sList);
… … …

           
<foreach>:循環周遊标簽。适用于多個參數或者的關系。
    <foreach collection=“”open=“”close=“”item=“”separator=“”>
		擷取參數
	</foreach>
           

屬性

collection:參數容器類型, (list-集合, array-數組)。

open:開始的 SQL 語句。

close:結束的 SQL 語句。

item:參數變量名。

separator:分隔符。

2.4 SQL片段抽取

Sql 中可将重複的 sql 提取出來,使用時用 include 引用即可,最終達到 sql 重用的目的

<!--抽取sql片段簡化編寫-->
<sql id="selectStudent" select * from student</sql>
<select id="findById" parameterType="int" resultType="student">
    <include refid="selectStudent"></include> where id=#{id}
</select>
<select id="findByIds" parameterType="list" resultType="student">
    <include refid="selectStudent"></include>
    <where>
        <foreach collection="array" open="id in(" close=")" item="id" separator=",">
            #{id}
        </foreach>
    </where>
</select>
           

我們可以将一些重複性的 SQL 語句進行抽取,以達到複用的效果。

-  <sql>:抽取 SQL 語句标簽。 
-  <include>:引入 SQL 片段标簽。 
   <sql id=“片段唯一辨別”>抽取的 SQL 語句</sql> <include refid=“片段唯一辨別”/>
 
           

2.5 知識小結

MyBatis映射檔案配置:

<select>:查詢

<insert>:插入

<update>:修改

<delete>:删除

<where>:where條件

<if>:if判斷

<foreach>:循環

<sql>:sql片段抽取

           

三. 分頁插件

3.1 分頁插件介紹

Mybatis_02
  • 分頁可以将很多條結果進行分頁顯示。
  • 如果目前在第一頁,則沒有上一頁。如果目前在最後一頁,則沒有下一頁。
  • 需要明确目前是第幾頁,這一頁中顯示多少條結果。
  • MyBatis分頁插件總結
    1. 在企業級開發中,分頁也是一種常見的技術。而目前使用的 MyBatis 是不帶分頁功能的,如果想實作分頁的 功能,需要我們手動編寫 LIMIT 語句。但是不同的資料庫實作分頁的 SQL 語句也是不同的,是以手寫分頁 成本較高。這個時候就可以借助分頁插件來幫助我們實作分頁功能。
    2. PageHelper:第三方分頁助手。将複雜的分頁操作進行封裝,進而讓分頁功能變得非常簡單。

3.2 分頁插件的使用

MyBatis可以使用第三方的插件來對功能進行擴充,分頁助手PageHelper是将分頁的複雜操作進行封裝,使用簡單的方式即可獲得分頁的相關資料

開發步驟:

①導入與PageHelper的jar包

②在mybatis核心配置檔案中配置PageHelper插件

<!-- 注意:分頁助手的插件  配置在通用mapper之前 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
    <!-- 指定方言 -->
    <property name="dialect" value="mysql"/>
</plugin>
           

③測試分頁資料擷取

@Test
public void testPageHelper(){
    //設定分頁參數
    PageHelper.startPage(1,2);

    List<User> select = userMapper2.select(null);
    for(User user : select){
        System.out.println(user);
    }
}
           

3.3 分頁插件的參數擷取

獲得分頁相關的其他參數:

//其他分頁的資料
PageInfo<User> pageInfo = new PageInfo<User>(select);
System.out.println("總條數:"+pageInfo.getTotal());
System.out.println("總頁數:"+pageInfo.getPages());
System.out.println("目前頁:"+pageInfo.getPageNum());
System.out.println("每頁顯示長度:"+pageInfo.getPageSize());
System.out.println("是否第一頁:"+pageInfo.isIsFirstPage());
System.out.println("是否最後一頁:"+pageInfo.isIsLastPage());

           

3.4 分頁插件知識小結

​ 分頁:可以将很多條結果進行分頁顯示。

  • 分頁插件 jar 包: pagehelper-5.1.10.jar jsqlparser-3.1.jar
  • :內建插件标簽。
  • 分頁助手相關 API

    ​ 1.PageHelper:分頁助手功能類。

    1. startPage():設定分頁參數
    2. PageInfo:分頁相關參數功能類。
    3. getTotal():擷取總條數
    4. getPages():擷取總頁數
    5. getPageNum():擷取目前頁
    6. getPageSize():擷取每頁顯示條數
    7. getPrePage():擷取上一頁
    8. getNextPage():擷取下一頁
    9. isIsFirstPage():擷取是否是第一頁
    10. isIsLastPage():擷取是否是最後一頁

四.MyBatis的多表操作

4.1 多表模型介紹

我們之前學習的都是基于單表操作的,而實際開發中,随着業務難度的加深,肯定需要多表操作的。

  • 多表模型分類 一對一:在任意一方建立外鍵,關聯對方的主鍵。
  • 一對多:在多的一方建立外鍵,關聯一的一方的主鍵。
  • 多對多:借助中間表,中間表至少兩個字段,分别關聯兩張表的主鍵。

4.2 多表模型一對一操作

  1. 一對一模型: 人和身份證,一個人隻有一個身份證
  2. 代碼實作
    • 步驟一: sql語句準備
      CREATE TABLE person(
      	id INT PRIMARY KEY AUTO_INCREMENT,
      	NAME VARCHAR(20),
      	age INT
      );
      INSERT INTO person VALUES (NULL,'張三',23);
      INSERT INTO person VALUES (NULL,'李四',24);
      INSERT INTO person VALUES (NULL,'王五',25);
      
      CREATE TABLE card(
      	id INT PRIMARY KEY AUTO_INCREMENT,
      	number VARCHAR(30),
      	pid INT,
      	CONSTRAINT cp_fk FOREIGN KEY (pid) REFERENCES person(id)
      );
      INSERT INTO card VALUES (NULL,'12345',1);
      INSERT INTO card VALUES (NULL,'23456',2);
      INSERT INTO card VALUES (NULL,'34567',3);
                 
    • 步驟二:配置檔案
      <?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="com.itheima.table01.OneToOneMapper">
          <!--配置字段和實體對象屬性的映射關系-->
          <resultMap id="oneToOne" type="card">
              <id column="cid" property="id" />
              <result column="number" property="number" />
              <!--
                  association:配置被包含對象的映射關系
                  property:被包含對象的變量名
                  javaType:被包含對象的資料類型
              -->
              <association property="p" javaType="person">
                  <id column="pid" property="id" />
                  <result column="name" property="name" />
                  <result column="age" property="age" />
              </association>
          </resultMap>
      
          <select id="selectAll" resultMap="oneToOne">
              SELECT c.id cid,number,pid,NAME,age FROM card c,person p WHERE c.pid=p.id
          </select>
      </mapper>
                 
    • 步驟三:測試類
      @Test
          public void selectAll() throws Exception{
              //1.加載核心配置檔案
              InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
      
              //2.擷取SqlSession工廠對象
              SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
      
              //3.通過工廠對象擷取SqlSession對象
              SqlSession sqlSession = sqlSessionFactory.openSession(true);
      
              //4.擷取OneToOneMapper接口的實作類對象
              OneToOneMapper mapper = sqlSession.getMapper(OneToOneMapper.class);
      
              //5.調用實作類的方法,接收結果
              List<Card> list = mapper.selectAll();
      
              //6.處理結果
              for (Card c : list) {
                  System.out.println(c);
              }
      
              //7.釋放資源
              sqlSession.close();
              is.close();
          }
                 
    3.一對一配置總結:
    <resultMap>:配置字段和對象屬性的映射關系标簽。
        id 屬性:唯一辨別
        type 屬性:實體對象類型
    <id>:配置主鍵映射關系标簽。
    <result>:配置非主鍵映射關系标簽。
        column 屬性:表中字段名稱
        property 屬性: 實體對象變量名稱
    <association>:配置被包含對象的映射關系标簽。
        property 屬性:被包含對象的變量名
        javaType 屬性:被包含對象的資料類型
               

4.3 多表模型一對多操作

  1. 一對多模型: 一對多模型:班級和學生,一個班級可以有多個學生。
    • CREATE TABLE classes(
      	id INT PRIMARY KEY AUTO_INCREMENT,
      	NAME VARCHAR(20)
      );
      INSERT INTO classes VALUES (NULL,'黑馬一班');
      INSERT INTO classes VALUES (NULL,'黑馬二班');
      
      
      CREATE TABLE student(
      	id INT PRIMARY KEY AUTO_INCREMENT,
      	NAME VARCHAR(30),
      	age INT,
      	cid INT,
      	CONSTRAINT cs_fk FOREIGN KEY (cid) REFERENCES classes(id)
      );
      INSERT INTO student VALUES (NULL,'張三',23,1);
      INSERT INTO student VALUES (NULL,'李四',24,1);
      INSERT INTO student VALUES (NULL,'王五',25,2);
      INSERT INTO student VALUES (NULL,'趙六',26,2);
                 
    • <mapper namespace="com.itheima.table02.OneToManyMapper">
          <resultMap id="oneToMany" type="classes">
              <id column="cid" property="id"/>
              <result column="cname" property="name"/>
      
              <!--
                  collection:配置被包含的集合對象映射關系
                  property:被包含對象的變量名
                  ofType:被包含對象的實際資料類型
              -->
              <collection property="students" ofType="student">
                  <id column="sid" property="id"/>
                  <result column="sname" property="name"/>
                  <result column="sage" property="age"/>
              </collection>
          </resultMap>
          <select id="selectAll" resultMap="oneToMany">
              SELECT c.id cid,c.name cname,s.id sid,s.name sname,s.age sage FROM classes c,student s WHERE c.id=s.cid
          </select>
      </mapper>
                 
    • @Test
          public void selectAll() throws Exception{
              //1.加載核心配置檔案
              InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
      
              //2.擷取SqlSession工廠對象
              SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
      
              //3.通過工廠對象擷取SqlSession對象
              SqlSession sqlSession = sqlSessionFactory.openSession(true);
      
              //4.擷取OneToManyMapper接口的實作類對象
              OneToManyMapper mapper = sqlSession.getMapper(OneToManyMapper.class);
      
              //5.調用實作類的方法,接收結果
              List<Classes> classes = mapper.selectAll();
      
              //6.處理結果
              for (Classes cls : classes) {
                  System.out.println(cls.getId() + "," + cls.getName());
                  List<Student> students = cls.getStudents();
                  for (Student student : students) {
                      System.out.println("\t" + student);
                  }
              }
      
              //7.釋放資源
              sqlSession.close();
              is.close();
          }
                 
    3.一對多配置檔案總結:
    <resultMap>:配置字段和對象屬性的映射關系标簽。
        id 屬性:唯一辨別
        type 屬性:實體對象類型
    <id>:配置主鍵映射關系标簽。
    <result>:配置非主鍵映射關系标簽。
        column 屬性:表中字段名稱
        property 屬性: 實體對象變量名稱
    <collection>:配置被包含集合對象的映射關系标簽。
        property 屬性:被包含集合對象的變量名
        ofType 屬性:集合中儲存的對象資料類型
               

4.4 多表模型多對多操作

  1. 多對多模型:學生和課程,一個學生可以選擇多門課程、一個課程也可以被多個學生所選擇。
    • CREATE TABLE course(
      	id INT PRIMARY KEY AUTO_INCREMENT,
      	NAME VARCHAR(20)
      );
      INSERT INTO course VALUES (NULL,'國文');
      INSERT INTO course VALUES (NULL,'數學');
      
      
      CREATE TABLE stu_cr(
      	id INT PRIMARY KEY AUTO_INCREMENT,
      	sid INT,
      	cid INT,
      	CONSTRAINT sc_fk1 FOREIGN KEY (sid) REFERENCES student(id),
      	CONSTRAINT sc_fk2 FOREIGN KEY (cid) REFERENCES course(id)
      );
      INSERT INTO stu_cr VALUES (NULL,1,1);
      INSERT INTO stu_cr VALUES (NULL,1,2);
      INSERT INTO stu_cr VALUES (NULL,2,1);
      INSERT INTO stu_cr VALUES (NULL,2,2);
                 
    • <?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="com.itheima.table03.ManyToManyMapper">
          <resultMap id="manyToMany" type="student">
              <id column="sid" property="id"/>
              <result column="sname" property="name"/>
              <result column="sage" property="age"/>
      
              <collection property="courses" ofType="course">
                  <id column="cid" property="id"/>
                  <result column="cname" property="name"/>
              </collection>
          </resultMap>
          <select id="selectAll" resultMap="manyToMany">
              SELECT sc.sid,s.name sname,s.age sage,sc.cid,c.name cname FROM student s,course c,stu_cr sc WHERE sc.sid=s.id AND sc.cid=c.id
          </select>
      </mapper>
                 
    • @Test
          public void selectAll() throws Exception{
              //1.加載核心配置檔案
              InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
      
              //2.擷取SqlSession工廠對象
              SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
      
              //3.通過工廠對象擷取SqlSession對象
              SqlSession sqlSession = sqlSessionFactory.openSession(true);
      
              //4.擷取ManyToManyMapper接口的實作類對象
              ManyToManyMapper mapper = sqlSession.getMapper(ManyToManyMapper.class);
      
              //5.調用實作類的方法,接收結果
              List<Student> students = mapper.selectAll();
      
              //6.處理結果
              for (Student student : students) {
                  System.out.println(student.getId() + "," + student.getName() + "," + student.getAge());
                  List<Course> courses = student.getCourses();
                  for (Course cours : courses) {
                      System.out.println("\t" + cours);
                  }
              }
      
              //7.釋放資源
              sqlSession.close();
              is.close();
          }
          
                 
    <resultMap>:配置字段和對象屬性的映射關系标簽。
    	id 屬性:唯一辨別
    	type 屬性:實體對象類型
     <id>:配置主鍵映射關系标簽。
     <result>:配置非主鍵映射關系标簽。
    	column 屬性:表中字段名稱
    	property 屬性: 實體對象變量名稱
    <collection>:配置被包含集合對象的映射關系标簽。
    	property 屬性:被包含集合對象的變量名
    	ofType 屬性:集合中儲存的對象資料類型
               

4.5 多表模型操作總結

<resultMap>:配置字段和對象屬性的映射關系标簽。
    id 屬性:唯一辨別
    type 屬性:實體對象類型
<id>:配置主鍵映射關系标簽。
<result>:配置非主鍵映射關系标簽。
	column 屬性:表中字段名稱
	property 屬性: 實體對象變量名稱
<association>:配置被包含對象的映射關系标簽。
	property 屬性:被包含對象的變量名
	javaType 屬性:被包含對象的資料類型
<collection>:配置被包含集合對象的映射關系标簽。
	property 屬性:被包含集合對象的變量名
	ofType 屬性:集合中儲存的對象資料類型