Spring 學習小記(九)

xiaoxie_advent 發表於 2020-12-04
MyBatis對映檔案
 
    本小記學習目標
        1.MyBatis的核心配置檔案(全域性配置檔案)的介紹
        2.掌握MyBatis的SQL對映檔案(重點掌握)
        3.掌握MyBatis的級聯查詢實現方法
 
一、MyBatis核心配置檔案
    MyBatis核心配置檔案,配置了很多影響MyBatis行為的資訊,這些資訊通常來說也只會配置在一個檔案中,並且一般是不會去輕易改變的,當它與Spring整合後,這些配置資訊會配置到Spring的配置檔案中,在實際的開發過程中需要去編寫或修改這個配置檔案的情況不多。
MyBatis核心配置檔案的模板如下:
<? xml version= "1.0" encoding= "UTF-8" ?>
<! DOCTYPE configuration PUBLIC "-// mybatis.org//DTD Config 3.0//EN" " http://mybatis.org/dtd/mybatis-3-config.dtd " >
< configuration >
       <!-- 屬性 -->
       < properties />
       <!-- 設定 -->
       < settings >
             < setting name= "" value= "" />
       </ settings >
       <!-- 型別命名(別名) -->
       < typeAliases />
       <!-- 型別處理器 -->
       < typeHandlers />
       <!-- 物件工廠 -->
       < objectFactory type= "" />
       <!-- 外掛 -->
       < plugins >
             < plugin interceptor= "" ></ plugin >
       </ plugins >
       <!-- 配置環境 -->
       < environments default= "" >
             <!-- 環境變數 -->
             < environment id= "" >
                   <!-- 事務管理器 -->
                   < transactionManager type= "" />
                   <!-- 資料來源 -->
                   < dataSource type= "" />
             </ environment >
       </ environments >
       <!-- 資料庫廠商標識 -->
       < databaseIdProvider type= "" />
       <!-- 對映器,通知MyBatis去哪裡去找對映檔案 -->
       < mappers >
             < mapper resource= "com/XXX/XXX/XXXMapper.xml" />
       </ mappers >
</ configuration >
 
二、MyBatis對映檔案
MyBatis對映器介紹
    對映器,它是MyBatis中最為重要的元件,它是由介面加上Xml檔案(sql對映檔案)組成。MyBatis也可以由註解來完成,但是一般來說不常用註解做Sql對映
Sql對映檔案的常用配置元素有如下一些:
    select        查詢語句    可以自定義引數、返回結果集……
    insert        插入語句    執行後返回一個整數,表示插入的記錄行數
    update        更新語句    執行後返回一個整數,表示更新的記錄行數
    delete        刪除語句    執行後返回一個整數,表示刪除的記錄行數
    sql           定義一部分sql
    resultMap     用來描述由資料庫結果集來載入物件
 
select元素
<select>元素用來對映SQL的select語句。例如:
<!-- 根據id查詢一條記錄 -->
< select id= "selectStudentById" parameterType= "Integer" resultType= "com.xiaoxie.pojo.Student" >
    select id,name,age from student where 1=1 and id=#{id}
</ select >
在上面的配置中parameterType指定為Integer,表明引數是一個Integer型別
resultType指定為com.xiaoxie.pojo.Student,表明返回的是一個Student類的物件
sql語句中#{id},表示傳入的引數是id屬性的值
select元素的常用屬性
id:它與Mapper的名稱空間組合使用,是一個唯一的識別符號,供MyBatis呼叫
parameterType:它表示傳入sql語句的引數型別的全限定名或別名。它是一個可選屬性,MyBatis可以推斷出具體傳入語句的引數
resultType:指定返回的型別(全限定名或別名)如果是集合型別,返回的是集合元素的型別,返回時可以使用resultType或resultMap之一
resultMap:它是對映集的引用,與<resultMap元素一起使用>,返回時可以使用reusltType或resultMap之一
flushCache:用於設定在呼叫sql語句後時否要求MyBatis清空之前查詢的本地快取和二級快取,預設是false
useCache:啟動二級快取開關,預設是true
timeout:設定超時時間,單位是秒,如果超時則會丟擲異常
fetchSize:獲取記錄的總條數設定
statementType:告訴MyBatis使用哪個JDBC的Statement工作,可取值:STATEMENT(Statement)、PREPARED(PreparedStatement)、CALLABLE(CallbleStatement)
resutlSetType:針對JDBC的ResultSet介面而言,它的值可以設定為FORWARD_ONLY(只可以向前訪問)、SCROLL_SENSITIVE(雙向滾動,但不及時更新)、SCROLL_INSENSITIVE(雙向滾動,及時更新)
關於多個引數傳遞的問題
方式一:使用Map做為引數傳遞
在實際的應用開發過程中,查詢sql語句通常需要多個查詢引數(多個條件),這個時候可以在parameterType屬性中指定為map
在Mapper中做如下查詢定義
<!-- 測試多引數傳遞 -->
< select id= "selectStudentByNameAndAge" resultType= "com.xiaoxie.pojo.Student" parameterType= "map" >
        select id,name,age from student
        where 1=1
        and name=#{name}
        and age=#{age}
</ select >
這裡可以看到parameterType指定的為map
在對應的Dao介面中對以上標識定義介面方法與之對應
//測試多引數查詢,條件:姓名、年齡
List<Student> selectStudentByNameAndAge(Map<String,Object> param);
在測試類中新增如下方法進行測試
System. out.println( "測試多條件引數:");
//Map條件
Map<String,Object> param = new HashMap<>();
param.put( "name", "張飛");
param.put( "age",26);
StudentDao dao = (StudentDao) context.getBean( "studentDao");
List<Student> stu = dao.selectStudentByNameAndAge( param);
for (Student student : stu) {
     System. out.println( student);
}
從上面我們可以看到我們定義了一個map,map的key對應的就是查詢語句中的引數名稱
方式二:使用Java Bean來傳遞引數
對於引數複雜的情況下我們使用Map的方式傳遞引數是比較複雜的需要對引數進行初始化
使用JavaBean作為引數則是專門為引數建立一個JavaBean的類
在com.xiaoxie.dao.param包下建立一個基礎的引數pojo類
package com.xiaoxie.dao.param;
public class StudnetNameAndAgeParam {
       private String name;
       private Integer age;
      
       public String getName() {
             return name;
      }
       public void setName(String name) {
             this. name = name;
      }
       public Integer getAge() {
             return age;
      }
       public void setAge(Integer age) {
             this. age = age;
      }
      
}
在Mapper中做如下定義
<!-- 測試多引數的傳遞,這裡使用JavaBean的方式傳入多個引數 -->
< select id= "selectStudentByNameAndAgeUseJavaBean" resultType= "com.xiaoxie.pojo.Student" parameterType= "com.xiaoxie.dao.param.StudnetNameAndAgeParam" >
     select id,name,age from student
      where 1=1
      and name=#{name}
      and age=#{age}
 </ select >
在dao介面中定時對應的介面方法
//測試多引數查詢,條件:姓名、年齡,作用JavaBean
List<Student> selectStudentByNameAndAgeUseJavaBean(StudnetNameAndAgeParam param);
在測試類的main方法中做如下測試可以把建立的JavaBean物件作為引數傳遞進去
System. out.println( "使用JavaBean作為引數傳遞");
StudnetNameAndAgeParam beanParam = new StudnetNameAndAgeParam();
beanParam.setName( "劉備");
beanParam.setAge(30);
stu = dao.selectStudentByNameAndAgeUseJavaBean( beanParam);
for (Student student : stu) {
    System. out.println( student);
}
對於多引數的傳遞方式是使用Map還是使用JavaBean,這個可以根據實際的情況靈活確定,如果覺得引數過多則可以選擇JavaBean這種方式
 
insert元素
<insert>元素用於對映插入語句,在MyBatis插入完成後會返回一個整數表示它執行影響的行數。它的屬性與select元素的不只屬性大部分都是一樣的,有如下幾個特有的屬性
keyProperty:這個屬性是把插入或更新操作時返回值賦給po類的某個屬性上,通常來說會設定為主鍵對應的屬性,如果是聯合主鍵,可以用逗號把多個值隔開
keyColumn:這個屬性是用來設定第幾例為主鍵,當主鍵不是第一列的時候是需要設定的,如果是聯合主鍵則可以使用多個值用逗號隔開
useGeneratedKeys:這個屬性是MyBatis使用JDBC的getGeneratedKeys()方法獲取資料庫內部產生的主鍵,預設值是false
對於mysql、sqlServer資料庫一般會採用自動遞增值作為主鍵,在insert資料完成後,有時是需要剛和新增資料的主鍵值的,這個時候則可以在mapper中對於insert元素新增keyProperty和useGeneratedKeys屬性
 
自動回填主鍵
在mapper中定義insert元素如下
<!-- 新增一條記錄 -->
< insert id= "addStudent" parameterType= "com.xiaoxie.pojo.Student" keyProperty= "id" useGeneratedKeys= "true" >
    insert into student(name,age) values(#{name},#{age})
</ insert >
這裡表示主鍵值是id屬性
通過如下方式新增記錄
Student stu1 = new Student( "劉備",30);
int rows = studentDao.addStudent( stu1);
System. out.println( "===============新增"+ rows + "條記錄==============");
System. out.println( "新增記錄的主鍵為:" + stu1.getId());
那麼新增記錄的主鍵會回寫到stu1的id屬性上
 
自主設定主鍵
有些資料庫是不支援自增主鍵的這個時候則需要自主去設定主鍵,這個時候可以使用<selectKey>元素來實現
在Mapper中需要做如下定義
< insert id= "addStudent" parameterType= "com.xiaoxie.pojo.Student" >
     < selectKey keyProperty= "id" resultType= "Integer" order= "BEFORE" >
        select if(max(id) is null,1,max(id)+1) as mid from student
     </ selectKey >
        insert into student(id,name,age) values(#{id},#{name},#{age})
</ insert >
注:一般對於mySql這樣的資料庫是不可以這樣操作的,因為原表的資料可能會被刪除記錄,這個時候max出來的主鍵是不正確的,非要使用則需要使用額外表來做記錄主鍵當前的遞增值
<selectKey>元素中的keyProperty屬性表表示查詢到的返回給到Po類的哪個屬性,order表示在什麼時機執行(可選值:BEFORE、AFTER)這裡的之前和之後是相對於執行insert語句來說的。
 
update元素和delete元素
它們分別對應的是sql語句中的update和delete,其屬性與insert、select屬性差不多,它在執行後也會返回一個整數,表示影響到了資料庫多少行記錄
mapper中的定義可以為如下
<!-- 修改一條記錄 -->
< update id= "updateStudentById" parameterType= "com.xiaoxie.pojo.Student" >
     update student set name=#{name},age=#{age} where 1=1 and id=#{id}
</ update >
<!-- 刪除一條記錄 -->
< delete id= "deleteStudentById" parameterType= "Integer" >
     delete from student where id=#{id}
</ delete >
 
sql元素
這個元素主要是用來自定義sql程式碼片段,這些sql片段可以在後續的配置中進行引用
如下示例:可以把需要使用到的列名用sql元素進行定義,在後續需要使用的地方進行引用即可
<!-- 使用 sql 標籤自定義部分 sql 語句供其他配置中引用 -->
< sql id= "studentColumns" > id,name,age </ sql >
< select id= "selectStudentByColumns" resultType= "com.xiaoxie.pojo.Student" >
    select < include refid= "studentColumns" /> from student
</ select >
 
resultMap元素
它是MyBatis中最為重要的元素,主要用來定義對映規則、級聯的更新以及定義型別轉化器……
<resultMap>元素的結構說明:
<resultMap type="" id="">
    <constructor>    <!-- 類在例項化的時候用來注入結果到構造方法 -->
        <idArg/>    <!-- ID引數,結果為ID -->
        <arg/>    <!-- 注入到構造方法的一個普通結果 -->
    </constructor>
    <id/>    <!-- 用來表示哪個列是主鍵 -->
    <result/>    <!-- 注入到欄位或JavaBean屬性的普通結果 -->
    <association property=""/>    <!-- 用於一對一關聯 -->
    <collection property=""/>    <!-- 用於一對多、多對多關聯 -->
    <discriminator javaType="">    <!-- 使用結果值來決定使用哪個結果對映 -->
        <case value=""/>    <!-- 基於某些值的結果對映 -->
    </discriminator>
</resultMap>
簡單說明:
resultMap的type屬性表示需要的pojo,id屬性則表示resultMap的唯一值
子元素<constructor>用來配置構造方法(在POJO中沒有定義無參構造時使用)
子元素<id>表示哪個列為主鍵
子元素<result>表示POJO和資料表普通列的對映關係
子元素<association>、<collection>、<discriminator>用在級聯的情況
 
使用Map來儲存結果集
指的是在配置select元素時,指定resultType="map"
在Mapper中可以做如下定義
<!-- 使用Map來儲存結果集 -->
< select id= "selectStudentUseMap" resultType= "map" >
     select * from student
</ select >
在Dao介面中新增對應的介面方法
//使用Map來儲存查詢的結果集
List<Map<String,Object>> selectStudentUseMap();
在測試類中新增如下程式碼進行測試
System. out.println( "使用Map來儲存查詢返回的結果集");
List<Map<String, Object>> studentMap  = dao.selectStudentUseMap();
Set<Map.Entry<String, Object>> entrySet = null;
for (Map<String, Object> map : studentMap) {
//遍歷map
entrySet = map.entrySet();
for (Map.Entry<String, Object> entry : entrySet) {
    System. out.println( entry.getKey() + ":" + entry.getValue());
}
 System. out.println( "------------");
}
這裡我們通過測試的結果可以看到,當使用map做結果集的儲存時,map中的key為欄位名,value則是查詢結果返回的對應欄位的值,使用這種方式對於少量的記錄(比如一條記錄)很方便,但是可讀性並不高。
 
使用POJO儲存結果集
使用POJO進行結果集的儲存有兩個方面是比較好的
第一,可以使用resultType屬性
第二,當有複雜的對映或級聯,這個時候可以使用select元素的resultMap屬性配置對映集合
在mapper中配置resultMap元素
<!-- resultMap元素進行對映 -->
       < resultMap type= "com.xiaoxie.pojo.Student" id= "studentResult" >
             < id property= "id" column= "id" /> <!-- 指定主鍵 -->
             < result property= "name" column= "name" />
             < result property= "age" column= "age" />
       </ resultMap >
      
       < select id= "selectStudentUseResultMap" resultMap= "studentResult" >
            select * from student
       </ select >
在Dao介面中新增對應的介面方法
//使用resultMap進行結果集的對映
      List<Student> selectStudentUseResultMap();
在測試類中新增如下程式碼進行測試
System. out.println( "使用resultMap進行結果集的對映");
     List<Student> resultMap = dao.selectStudentUseResultMap();
      for (Student student : resultMap) {
         System. out.println( student);
     }
 
級聯查詢
級聯查詢,它有三種:一對一級聯,一對多級聯,多對多級聯
優點:可以方便地獲取關聯資料
缺點:過多的級聯會增加資料庫系統的複雜性,同時影響系統的效能
實際開發過程中需要根據實際的情況做取捨,一般不建議做資料庫級別的級聯,關聯的關係放在程式邏輯中即可
何為級聯查詢?
當有一個表A,它其中有一個欄位做外來鍵引用了表B的主鍵,這個時候通過表A的外來鍵把表B對應的記錄查詢出來,這個就是級聯查詢
 
一對一級聯查詢
MyBatis中對於一對一級聯查詢的處理方式是,通過<resultMap>元素的子元素<association>元素處理,在這個子元素中通常使用以下屬性
property:指定對映到實體類的物件屬性
column:指定表中對應的欄位
javaType:指定對映到實體物件屬性的型別
select:指定引入巢狀查詢的子sql語句
 
在資料庫中建立如下兩張表
-- 身份證表
create table idcard(
    id BIGINT(19) NOT NULL auto_increment COMMENT '主鍵ID',
    code varchar(18) NOT NULL DEFAULT '' COMMENT '身份證號碼',
    PRIMARY key(id)
);
 
-- 身份資訊表
CREATE TABLE `person` (
  `id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `name` varchar(20) DEFAULT '' COMMENT '姓名',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT '年齡',
  `address` varchar(512) DEFAULT '' COMMENT '常住地址',
  `idcard_id` bigint(19) DEFAULT NULL COMMENT 'idcard表的id',
  PRIMARY KEY (`id`),
  KEY `idcard` (`idcard_id`),
  CONSTRAINT `idcard` FOREIGN KEY (`idcard_id`) REFERENCES `idcard` (`id`)
);
 
新增POJO實體類:IdCard,Person
IdCard:
package com.xiaoxie.pojo;
public class IdCard {
       private Integer id;
       private String code;
      
       /*Setter與Getter*/
       public Integer getId() {
             return id;
      }
       public void setId(Integer id) {
             this. id = id;
      }
       public String getCode() {
             return code;
      }
       public void setCode(String code) {
             this. code = code;
      }
      
       @Override
       public String toString() {
             return "idCard[id=+"+ id+ ",code="+ code+ "]";
      }
}
Person:
package com.xiaoxie.pojo;
public class Person {
       private Integer id;
       private String name;
       private Integer age;
       private String address;
       private IdCard idcard;   //身份證資訊
      
      
       public Integer getId() {
             return id;
      }
       public void setId(Integer id) {
             this. id = id;
      }
       public String getName() {
             return name;
      }
       public void setName(String name) {
             this. name = name;
      }
       public Integer getAge() {
             return age;
      }
       public void setAge(Integer age) {
             this. age = age;
      }
       public String getAddress() {
             return address;
      }
       public void setAddress(String address) {
             this. address = address;
      }
       public IdCard getIdcard() {
             return idcard;
      }
       public void setIdcard(IdCard idcard) {
             this. idcard = idcard;
      }
      
       @Override
       public String toString() {
             return "Person[id=+"+ id+ ",name="+ name+ ",age="+ age+ ",address="+ address+ ",idcard="+ idcard+ "]";
      }
}
 
新增Mapper對映檔案
IdCardMapper:
<? xml version= "1.0" encoding= "UTF-8" ?>
<! DOCTYPE mapper
  PUBLIC "-// mybatis.org//DTD Mapper 3.0//EN"
  < mapper namespace= "com.xiaoxie.dao.IdCardDao" >
       < select id= "selectIdCardById" parameterType= "Integer" resultType= "com.xiaoxie.pojo.IdCard" >
            select * from idcard where id = #{id}
       </ select >
  </ mapper >
 
PersonMapper:
<? xml version= "1.0" encoding= "UTF-8" ?>
<! DOCTYPE mapper
  PUBLIC "-// mybatis.org//DTD Mapper 3.0//EN"
  < mapper namespace= "com.xiaoxie.dao.PersonDao" >
       <!-- 一對一級聯,執行兩個 sql 進行查詢 -->
       < resultMap type= "com.xiaoxie.pojo.Person" id= "idCardAdnPerson_1" >
             < id property= "id" column= "id" />      <!-- 指定ID主鍵列 -->
             <!-- 普通列的對映關係 -->
             < result property= "name" column= "name" />
             < result property= "age" column= "age" />
             < result property= "address" column= "address" />
             <!-- 一對一的級聯查詢 -->
             < association property= "idcard" column= "idcard_id" javaType= "com.xiaoxie.pojo.IdCard" select= "com.xiaoxie.dao.IdCardDao.selectIdCardById" />
       </ resultMap >
       < select id= "selectPersonById_1" parameterType= "Integer" resultMap= "idCardAdnPerson_1" >
            select * from person where id=#{id}
       </ select >
      
       <!-- 一對一級聯,執行一個 sql 語句 -->
       < resultMap type= "com.xiaoxie.pojo.Person" id= "idCardAdnPerson_2" >
             < id property= "id" column= "id" />      <!-- 指定主鍵列 -->
             <!-- 普通列的對映關係 -->
             < result property= "name" column= "name" />
             < result property= "age" column= "age" />
             < result property= "address" column= "address" />
             <!-- 一對一的級聯查詢 -->
             < association property= "idcard" javaType= "com.xiaoxie.pojo.IdCard" >
                   < id property= "id" column= "idcard_id" />
                   < result property= "code" column= "code" />
             </ association >
       </ resultMap >
       < select id= "selectPersonById_2" parameterType= "Integer" resultMap= "idCardAdnPerson_2" >
            select p.*,c.code from person p left join idcard c on p.idcard_id = c.id
            where p.id=#{id}
       </ select >
      
       <!-- 一對一級聯,根據id查詢個人資訊,連線查詢(使用 pojo 儲存查詢結結果) -->
       < select id= "selectPersonById_3" parameterType= "Integer" resultType= "com.xiaoxie.dao.result.IdCardAdnPerson" >
            select p.*,c.code from person p left join idcard c on p.idcard_id = c.id
            where p.id=#{id}
       </ select >
  </ mapper >
 
對應新增Dao介面
idCardDao:
package com.xiaoxie.dao;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.pojo.IdCard;
@Repository( "idCardDao")
@Mapper
public interface IdCardDao {
      IdCard selectIdCardById(Integer id);
}
 
PersonDao:
package com.xiaoxie.dao;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.dao.result.IdCardAdnPerson;
import com.xiaoxie.pojo.Person;
@Repository( "personDao")
@Mapper
public interface PersonDao {
      Person selectPersonById_1(Integer id);
      Person selectPersonById_2(Integer id);
      IdCardAdnPerson selectPersonById_3(Integer id);
}
 
新增實體類來儲存MyBatis查詢返回的結果
IdCardAdnPerson:
package com.xiaoxie.dao.result;
public class IdCardAdnPerson {
       private Integer id;
       private String name;
       private Integer age;
       private String address;
       private String code;
       public Integer getId() {
             return id;
      }
       public void setId(Integer id) {
             this. id = id;
      }
       public String getName() {
             return name;
      }
       public void setName(String name) {
             this. name = name;
      }
       public Integer getAge() {
             return age;
      }
       public void setAge(Integer age) {
             this. age = age;
      }
       public String getAddress() {
             return address;
      }
       public void setAddress(String address) {
             this. address = address;
      }
       public String getCode() {
             return code;
      }
       public void setCode(String code) {
             this. code = code;
      }
      
       @Override
       public String toString() {
             return "IdCardAdnPerson[id=+"+ id+ ",name="+ name+ ",age="+ age+ ",address="+ address+ ",code="+ code+ "]";
      }
}
 
在MyBatis的核心配置檔案中把Mapper的對映新增進去
<? xml version= "1.0" encoding= "UTF-8" ?>
<! DOCTYPE configuration PUBLIC "-// mybatis.org//DTD Config 3.0//EN" " http://mybatis.org/dtd/mybatis-3-config.dtd " >
< configuration >
       <!-- 在MyBatis進行巢狀查詢時,使用延遲載入可以提高一定的效能所以在這裡可把把這個配置開啟 -->
       < settings >
             <!-- 延遲載入 -->  
             < setting name= "lazyLoadingEnabled" value= "true" />
             <!-- 按需載入 -->
             < setting name= "aggressiveLazyLoading" value= "false" />
       </ settings >
      
       <!-- 查詢對映檔案 -->
       < mappers >
             < mapper resource= "com/xiaoxie/dao/mybatis/StudentMapper.xml" />
             < mapper resource= "com/xiaoxie/dao/mybatis/IdCardMapper.xml" />
             < mapper resource= "com/xiaoxie/dao/mybatis/PersonMapper.xml" />
       </ mappers >
      
</ configuration >
 
新增Controller類
package com.xiaoxie.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import com.xiaoxie.dao.PersonDao;
import com.xiaoxie.dao.result.IdCardAdnPerson;
import com.xiaoxie.pojo.Person;
@Transactional
@Controller( "personController1")
public class PersonController1 {
       @Autowired
       private PersonDao personDao;
      
       public void test() {
            Person P1 = personDao.selectPersonById_1(1);
            System. out.println( P1);
            System. out.println( "----------------------");
            Person p2 = personDao.selectPersonById_2(1);
            System. out.println( p2);
            System. out.println( "-----------------------");
            IdCardAdnPerson p3 = personDao.selectPersonById_3(1);
            System. out.println( p3);
      }
}
 
在測試類中呼叫
ApplicationContext context = new ClassPathXmlApplicationContext( "spring-config.xml");
PersonController1 personController1 = (PersonController1) context.getBean( "personController1");
personController1.test();
注意:上述的例子中使用了資料庫的外來鍵使用資料庫的兩個表有了級聯關係,實際上在資料庫中沒有這種強制的級聯關係,只要邏輯上是這樣的也是可以使用上面的這種方式做級聯查詢的(實際應用中應該這種方式是更常見的,實際上這樣設計會更合理些)
 
一對多級聯查詢
一對多級聯是指的一個實體表A的記錄對應關聯實體表B中的多條記錄,比如一個使用者購物時會產生多個訂單,那麼使用者與訂單是一對多的關係。
 
新增兩個資料庫表
-- user表
CREATE TABLE `user` (
  `id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `user_code` varchar(50) NOT NULL DEFAULT '' COMMENT '使用者編碼',
  `user_name` varchar(50) NOT NULL DEFAULT '' COMMENT '使用者名稱稱',
  PRIMARY KEY (`id`)
)
-- order表
CREATE TABLE `order` (
  `id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `order_no` varchar(25) NOT NULL DEFAULT '' COMMENT '訂單編號',
  `user_code` varchar(50) NOT NULL DEFAULT '' COMMENT '使用者編碼',
  PRIMARY KEY (`id`)
)
 
新增POJO實體類
Order:
package com.xiaoxie.pojo;
public class Order {
       private Integer id;
       private String order_no;
       private String user_code;
      
      
       public Integer getId() {
             return id;
      }
       public void setId(Integer id) {
             this. id = id;
      }
       public String getOrder_no() {
             return order_no;
      }
       public void setOrder_no(String order_no) {
             this. order_no = order_no;
      }
       public String getUser_code() {
             return user_code;
      }
       public void setUser_code(String user_code) {
             this. user_code = user_code;
      }
      
       @Override
       public String toString() {
             return "Order[id=+"+ id+ ",order_no="+ order_no+ ",user_code="+ user_code+ "]";
      }
}
 
User:
package com.xiaoxie.pojo;
import java.util.List;
public class User {
       private Integer id;
       private String user_code;
       private String user_name;
       private List<Order> orders;
      
       public Integer getId() {
             return id;
      }
       public void setId(Integer id) {
             this. id = id;
      }
       public String getUser_code() {
             return user_code;
      }
       public void setUser_code(String user_code) {
             this. user_code = user_code;
      }
       public String getUser_name() {
             return user_name;
      }
       public void setUser_name(String user_name) {
             this. user_name = user_name;
      }
       public List<Order> getOrders() {
             return orders;
      }
       public void setOrders(List<Order> orders) {
             this. orders = orders;
      }
       @Override
       public String toString() {
             return "User[id=+"+ id+ ",user_code="+ user_code+ ",user_name="+ user_name+ ",orders:"+ orders+ "]";
      }
      
}
 
新增Mapper對映檔案
OrderMapper.xml:
<? xml version= "1.0" encoding= "UTF-8" ?>
<! DOCTYPE mapper
  PUBLIC "-// mybatis.org//DTD Mapper 3.0//EN"
  < mapper namespace= "com.xiaoxie.dao.OrderDao" >
       < select id= "selectOrderByUserCode" parameterType= "String" resultType= "com.xiaoxie.pojo.Order" >
            select * from `order` where user_code = #{user_code}
       </ select >
  </ mapper >
 
UserMapper:
<? xml version= "1.0" encoding= "UTF-8" ?>
<! DOCTYPE mapper
  PUBLIC "-// mybatis.org//DTD Mapper 3.0//EN"
  < mapper namespace= "com.xiaoxie.dao.UserDao" >
       < resultMap type= "com.xiaoxie.pojo.User" id= "userAndOrders_1" >
             < id property= "id" column= "id" />
             < result property= "user_code" column= "user_code" />
             < result property= "user_name" column= "user_name" />
             <!-- 一對多的級聯,這裡面ofType表示集合的元素型別,把user_code作為引數傳入(巢狀查詢) -->
             < collection property= "orders" ofType= "com.xiaoxie.pojo.Order" column= "user_code" select= "com.xiaoxie.dao.OrderDao.selectOrderByUserCode" />
       </ resultMap >
       < select id= "selectUserOrdersById_1" parameterType= "Integer" resultMap= "userAndOrders_1" >
            select * from user where id=#{id}
       </ select >
      
       <!-- 一對多的級聯,一個查詢巢狀結果 -->
       < resultMap type= "com.xiaoxie.pojo.User" id= "userAndOrders_2" >
             < id property= "id" column= "id" />
             < result property= "user_code" column= "user_code" />
             < result property= "user_name" column= "user_code" />
             < collection property= "orders" ofType= "com.xiaoxie.pojo.Order" >
                   < id property= "id" column= "id" />
                   < result property= "order_no" column= "order_no" />
             </ collection >
       </ resultMap >
       < select id= "selectUserOrdersById_2" parameterType= "Integer" resultMap= "userAndOrders_2" >
            select u.*,o.id,o.order_no,o.user_code from user u left join `order` o on u.user_code = o.user_code
            where u.id = #{id}
       </ select >
      
       <!-- 一對多級聯,使用 pojo 類儲存結果 -->
       < select id= "selectUserOrdersById_3" parameterType= "Integer" resultType= "com.xiaoxie.dao.result.UserAndOrders" >
            select u.*,o.order_no from user u left join `order` o on u.user_code = o.user_code
            where u.id = #{id}
       </ select >
  </ mapper >
 
新增對應的Dao介面
OrderDao:
package com.xiaoxie.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.pojo.Order;
@Repository( "orderDao")
@Mapper
public interface OrderDao {
      List<Order> selectOrderByUserCode(String user_code);
}
 
UserDao:
package com.xiaoxie.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.dao.result.UserAndOrders;
import com.xiaoxie.pojo.User;
@Mapper
@Repository( "userDao")
public interface UserDao {
      User selectUserOrdersById_1(Integer id);
      
      User selectUserOrdersById_2(Integer id);
      
      List<UserAndOrders> selectUserOrdersById_3(Integer id);
}
 
新增實體類來儲存MyBatis返回的結果
UserAndOrders:
package com.xiaoxie.dao.result;
public class UserAndOrders {
       private Integer id;
       private String user_code;
       private String user_name;
       private String order_no;
       public Integer getId() {
             return id;
      }
       public void setId(Integer id) {
             this. id = id;
      }
       public String getUser_code() {
             return user_code;
      }
       public void setUser_code(String user_code) {
             this. user_code = user_code;
      }
       public String getUser_name() {
             return user_name;
      }
       public void setUser_name(String user_name) {
             this. user_name = user_name;
      }
       public String getOrder_no() {
             return order_no;
      }
       public void setOrder_no(String order_no) {
             this. order_no = order_no;
      }
      
       @Override
       public String toString() {
             return "UserOrders[id=+"+ id+ ",user_code="+ user_code+ ",user_name="+ user_name+ ",order_no:"+ order_no+ "]";
      }
}
 
在MyBatis的核心配置檔案中新增對這兩個Mapper對映檔案的掃描
<? xml version= "1.0" encoding= "UTF-8" ?>
<! DOCTYPE configuration PUBLIC "-// mybatis.org//DTD Config 3.0//EN" " http://mybatis.org/dtd/mybatis-3-config.dtd " >
< configuration >
       <!-- 在MyBatis進行巢狀查詢時,使用延遲載入可以提高一定的效能所以在這裡可把把這個配置開啟 -->
       < settings >
             <!-- 延遲載入 -->  
             < setting name= "lazyLoadingEnabled" value= "true" />
             <!-- 按需載入 -->
             < setting name= "aggressiveLazyLoading" value= "false" />
       </ settings >
      
       <!-- 查詢對映檔案 -->
       < mappers >
             < mapper resource= "com/xiaoxie/dao/mybatis/StudentMapper.xml" />
             < mapper resource= "com/xiaoxie/dao/mybatis/IdCardMapper.xml" />
             < mapper resource= "com/xiaoxie/dao/mybatis/PersonMapper.xml" />
             < mapper resource= "com/xiaoxie/dao/mybatis/OrderMapper.xml" />
             < mapper resource= "com/xiaoxie/dao/mybatis/UserMapper.xml" />
       </ mappers >
      
</ configuration >
 
新增Controller類
UserOrderController:
package com.xiaoxie.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import com.xiaoxie.dao.UserDao;
import com.xiaoxie.dao.result.UserAndOrders;
import com.xiaoxie.pojo.User;
@Controller( "userOrderController")
@Transactional
public class UserOrderController {
       @ Autowired
       private UserDao userDao;
      
       public void test() {
            User userOrders1 = userDao.selectUserOrdersById_1(1);
            System. out.println( userOrders1);
            System. out.println( "-------------------------------");
            User userOrder2 = userDao.selectUserOrdersById_2(1);
            System. out.println( "-------------------------------");
            List<UserAndOrders> userOrder3 = userDao.selectUserOrdersById_3(1);
            System. out.println( userOrder3);
      }
}
 
在測試類中進行測試:
ApplicationContext context = new ClassPathXmlApplicationContext( "spring-config.xml");
UserOrderController userOrderController = (UserOrderController) context.getBean( "userOrderController");
userOrderController.test();
 
多對多級聯查詢
MyBatis是沒有對多對多的級聯實現的,因為多對多級聯是可以通過兩個一對多的級聯來實現的。
如我們的訂單表與訂單商品表一般就是多對多的關係,一個訂單A、訂單B可以有商品A、商品B
我們再向資料庫新增兩個表
-- order_detail表
CREATE TABLE `order_detail` (
  `id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `order_no` varchar(25) NOT NULL DEFAULT '' COMMENT '訂單編號',
  `goods_no` varchar(50) NOT NULL DEFAULT '' COMMENT '商品編碼',
  PRIMARY KEY (`id`)
);
-- goods表
CREATE TABLE `goods` (
  `id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
  `goods_no` varchar(50) NOT NULL DEFAULT '' COMMENT '商品編碼',
  PRIMARY KEY (`id`)
);
 
新增POJO實體類
Goods
package com.xiaoxie.pojo;
import java.util.List;
public class Goods {
       private Integer id;
       private String goods_no;
      
       //商品對應訂單是一個對多的關係
       private List<Order> orders;
      
      
       public Integer getId() {
             return id;
      }
       public void setId(Integer id) {
             this. id = id;
      }
       public String getGoods_no() {
             return goods_no;
      }
       public void setGoods_no(String goods_no) {
             this. goods_no = goods_no;
      }
      
      
       public List<Order> getOrders() {
             return orders;
      }
       public void setOrders(List<Order> orders) {
             this. orders = orders;
      }
       @Override
       public String toString() {
             return "Goods[id="+ id+ ",goods_no="+ goods_no+ ",orders="+ orders+ "]";
      }
}
 
修改Order實體類把對goods的一對多關係加入進去
package com.xiaoxie.pojo;
import java.util.List;
public class Order {
       private Integer id;
       private String order_no;
       private String user_code;
      
       private List<Goods> goods;
      
       public Integer getId() {
             return id;
      }
       public void setId(Integer id) {
             this. id = id;
      }
       public String getOrder_no() {
             return order_no;
      }
       public void setOrder_no(String order_no) {
             this. order_no = order_no;
      }
       public String getUser_code() {
             return user_code;
      }
       public void setUser_code(String user_code) {
             this. user_code = user_code;
      }
      
      
      
       public List<Goods> getGoods() {
             return goods;
      }
       public void setGoods(List<Goods> goods) {
             this. goods = goods;
      }
       @Override
       public String toString() {
             return "Order[id=+"+ id+ ",order_no="+ order_no+ ",user_code="+ user_code+ ",goods="+ goods+ "]";
      }
}
 
在OrderMapper的對映檔案中新增<resultMap>來處理多對多的查詢
<? xml version= "1.0" encoding= "UTF-8" ?>
<! DOCTYPE mapper
  PUBLIC "-// mybatis.org//DTD Mapper 3.0//EN"
  < mapper namespace= "com.xiaoxie.dao.OrderDao" >
       < select id= "selectOrderByUserCode" parameterType= "String" resultType= "com.xiaoxie.pojo.Order" >
            select * from `order` where user_code = #{user_code}
       </ select >
       <!-- 多對多的級聯 -->
       < resultMap type= "com.xiaoxie.pojo.Order" id= "OrdersAndGoods" >
             < id property= "id" column= "id" />
             < result property= "order_no" column= "order_no" />
             < result property= "user_code" column= "user_code" />
             <!-- 多對多 -->
             < collection property= "goods" ofType= "com.xiaoxie.pojo.Goods" >
                   < id property= "id" column= "id" />
                   < result property= "goods_no" column= "goods_no" />
             </ collection >
       </ resultMap >
       < select id= "selectAllOrderAndGoods" resultMap= "OrdersAndGoods" >
            select o.*,g.* from `order` o
            left join order_detail d on o.order_no = d.order_no
            left join goods g on d.goods_no = g.goods_no
       </ select >
  </ mapper >
 
新增對應的Dao介面方法
package com.xiaoxie.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.pojo.Order;
@Repository( "orderDao")
@Mapper
public interface OrderDao {
      List<Order> selectOrderByUserCode(String user_code);
      List<Order> selectAllOrderAndGoods();    //新增的多對多介面方法
}
 
新增Controller類
OrdersAndGoodsController:
package com.xiaoxie.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import com.xiaoxie.dao.OrderDao;
import com.xiaoxie.pojo.Order;
@Controller( "ordersAndGoodsController")
@Transactional
public class OrdersAndGoodsController {
       @Autowired
       private OrderDao orderDao;
       public void test() {
            List<Order> orders = orderDao.selectAllOrderAndGoods();
            System. out.println( orders);
      }
}
 
在測試類中進行測試
ApplicationContext context = new ClassPathXmlApplicationContext( "spring-config.xml");
OrdersAndGoodsController ogc = (OrdersAndGoodsController) context .getBean( "ordersAndGoodsController" );
ogc.test();

相關文章