MyBatis框架核心之Mapper文件使用resultMap及多表查詢

resultMap簡介

MyBatis是基於“數據庫結構不可控”的思想建立的,也就是我們希望數據庫遵循第三範式或BCNF,但實際事與願違,那麼結果集映射就是MyBatis爲我們提供這種理想與現實間轉換的手段了,而resultMap就是結果集映射的配置標籤了。

 

1.從SQL查詢結果到領域模型實體                  

  在深入ResultMap標籤前,我們需要了解從SQL查詢結果集到JavaBean或POJO實體的過程。

  1. 通過JDBC查詢得到ResultSet對象

  2. 遍歷ResultSet對象並將每行數據暫存到HashMap實例中,以結果集的字段名或字段別名爲鍵,以字段值爲值

  3. 根據ResultMap標籤的type屬性通過反射實例化領域模型

  4. 根據ResultMap標籤的type屬性和id、result等標籤信息將HashMap中的鍵值對,填充到領域模型實例中並返回

 

2.使用場景

       在項目的實際開發中,有可能會遇到這樣兩種情況。

1.   實體類中的屬性名與列名不相同,不能改但。導致不能自動裝配值

2.   多表查詢的返回值中可能需要其他對象,或者數組(一對一和一對多)

 

二、resultMap標籤解釋

標籤及屬性介紹

<resultMap > 標籤:

      id屬性 ,resultMap標籤的標識。

          type屬性 ,返回值的全限定類名,或類型別名。

     autoMapping屬性 ,值範圍true(默認值)|false, 設置是否啓動自動映射功能,自動映射功能就是自動查找與字段名小寫同名的屬性名,並調用setter方法。而設置爲false後,則需要在`resultMap`內明確註明映射關係纔會調用對應的setter方法。

 

 

 <resultMap>可以設置的子標籤映射:

       1).id標籤  :ID 結果,將結果集標記爲ID,以方便全局調用(適用於指定主鍵)

column 數據庫的列名

Property需要裝配的屬性名

      2).result標籤:將查詢到的列的值,反射到指定的JavaBean的 屬性上

column 數據庫的列名

Property 需要裝配的屬性名

      3).association標籤:複雜類型  , 多表查詢(一對一)時,將根據外鍵或某列信息查詢出的對象,直接裝配給某個resultMap指定的屬性。

column 數據庫的列名

Property 需要裝配的屬性名

select  指定用來多表查詢的sqlmapper

 

      4).Collection標籤:複雜類型,多表查詢(一對多),將查詢出的結果集合直接裝配給某個對應的集合

column 數據庫的列名

Property 需要裝配的屬性名

javaType 指定用什麼類型接受返回的值(必要)

select  指定用來多表查詢的sqlmapper

      5).constructor– 用來將結果反射給一個實例化好的類的構造器

a) idArg –ID 參數;將結果集標記爲ID,以方便全局調用
b) arg –反射到構造器的通常結果

      6).discriminator – 使用一個結果值以決定使用哪個resultMap

a) case – 基本一些值的結果映射的case 情形

i. nestedresult mappings –一個case 情形本身就是一個結果映射,因此也可以包括一些相同的元素,也可以引用一個外部resultMap。

 

 

<resultMap type="" id="">
<id column="" property=""/>
<result column="" property="" />
       <association property="" column="" select=""></association>
       <collection property="" column="" javaType="" select=""></collection>
       <constructor></constructor>
    </resultMap>
 

     

 

三、各標籤使用

1.id、result

       id、result是最簡單的映射,id爲主鍵映射;result其他基本數據庫表字段到實體類屬性的映射。

實體字段                    表的列名     
sid                            stuid
sname                      stuname
gid                            gid
grade                       grade  
<resultMap type="student" id="studentMap" autoMapping="true">
    <!-- 主鍵建議用id標籤   -->
    <id column="stuid" property="sid"/>
    <!-- 其他列可以用result標籤 -->
    <result column="stuname" property="sname"/>
</resultMap>
 
 

id、result語句屬性配置細節:

 

屬性
描述
 
property
需要映射到JavaBean 的屬性名稱。
 
column
數據表的列名或者標籤別名。
 
javaType
一個完整的類名,或者是一個類型別名。如果你匹配的是一個JavaBean,那MyBatis 通常會自行檢測到。然後,如果你是要映射到一個HashMap,那你需要指定javaType 要達到的目的。
 
jdbcType
數據表支持的類型列表。這個屬性只在insert,update 或delete 的時候針對允許空的列有用。JDBC 需要這項,但MyBatis 不需要。如果你是直接針對JDBC 編碼,且有允許空的列,而你要指定這項。
 
typeHandler
使用這個屬性可以覆寫類型處理器。這項值可以是一個完整的類名,也可以是一個類型別名。
 
 

支持的JDBC類型
       爲了將來的引用,MyBatis支持下列JDBC 類型,通過JdbcType 枚舉:
BIT,FLOAT,CHAR,TIMESTAMP,OTHER,UNDEFINED,TINYINT,REAL,VARCHAR,BINARY,BLOB,NVARCHAR,SMALLINT,DOUBLE,LONGVARCHAR,VARBINARY,CLOB,NCHAR,INTEGER,NUMERIC,DATE,LONGVARBINARY,BOOLEAN,NCLOB,BIGINT,DECIMAL,TIME,NULL,CURSOR

 

2.association聯合

聯合元素用來處理“一對一”的關係。

需要指定映射的Java實體類的屬性,屬性的javaType(通常MyBatis 自己會識別)。對應的數據庫表的列名稱。如果想覆寫的話返回結果的值,需要指定typeHandler。
不同情況需要告訴MyBatis 如何加載一個聯合。MyBatis 可以用兩種方式加載:

1. select: 執行一個其它映射的SQL 語句返回一個Java實體類型。較靈活;
2. resultsMap: 使用一個嵌套的結果映射來處理通過join查詢結果集,映射成Java實體類型。

 

 

 

  association 標籤

    property=多表查詢裝配的屬性名

    column=通過那一列關聯

    select=指定查詢語句,如果查詢語句在其他的namespace中,則需要寫全namespace.方法id

 

例如:在獲取某個學生的信息的同時又想獲取所屬班級的所有信息

學生實體中的字段
package cn.et.fuqiang.resultMap.entity;
 
public class Student {
     private Integer sid;//學生id
     private String sname; //學生姓名
     private Integer gid; //班級id
 
     private Grade grade; //所屬班級的所有信息
}
 
班級實體的字段
package cn.et.fuqiang.resultMap.entity;
 
import java.util.List;
 
public class Grade {
    private Integer id;//班級id
    private String gname;//班級名稱
       public Grade() {
       super();
       // TODO Auto-generated constructor stub
    }
   
}
 
 

在Student的字段中,Grade班級實體,需要查詢班級表才能獲取,學生表中沒有該列(所以需要使用多表查詢中的 association標籤)

想要使用多表查詢先要定義一個resultMap,簡單來說其實相當於一個工廠,可以指定某個實體中那些屬性可以裝哪一列的值,並且還要定義類似於子查詢的方法。

<!-- 使用resultMap 多表查詢 -->
<resultMap type="student" id="gradeInStudent">
    <!-- 主鍵建議用id標籤   -->
    <id column="stuid" property="sid"/>
       
    <!-- 其他列可以用result標籤 -->
    <result column="stuname" property="sname"/>
    <!-- 多對一  多表查詢
        返回一個班級實體-->
    <association property="grade" column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById"></association>
</resultMap>
在namespace="cn.et.fuqiang.resultMap.xml.GradeXmlInterface下的方法
<select id="queryGradeById" resultMap="gradeMap">
       select * from grade where gid=#{0}
</select>
在這個resultMap中

type:是上面定義的學生類(使用了別名,如果沒有別名可以寫全類名)

id:是這個resultMap的標識

因爲表中的字段與實體中的不同,所以使用了下面兩個子標籤,將表的列與實體的屬性做了映射。這樣才能將表中的值獲取到實體的屬性中。

<!-- 主鍵建議用id標籤   -->

<id column="stuid" property="sid"/>

       

<!-- 其他列可以用result標籤 -->

<result column="stuname" property="sname"/>

因爲實體中還要一個班級實體,但表中沒有該字段,所以就要用多表查詢

<!-- 多對一 多表查詢返回一個班級實體-->

<association property="grade"column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById"></association>

(這裏的property是實體中的字段,而column=gid就相當於關聯的條件了

而select就是該關聯條件對應的查詢方法)

 

主sql

在這裏我們使用resultMap定義返回的類型,這樣就會自動將所有內容,裝到對應的屬性上

然後直接使用該主sql,查詢,獲取的值就會自動對應到其中。

 

<select id="quserAllStudent" resultMap="gradeInStudent">
   select * from student
</select>
 

 

 

以上代碼寫爲標準的sql語句

select  s.*,g.*from student s inner join grade g ons.gid=g.gid

 

 

3.  collection聚集

聚集元素用來處理“一對多”的關係。需要指定映射的Java實體類的屬性,屬性的javaType(一般爲ArrayList);列表中對象的類型ofType(Java實體類);對應的數據庫表的列名稱;
不同情況需要告訴MyBatis 如何加載一個聚集。MyBatis 可以用兩種方式加載:

1. select:執行一個其它映射的SQL 語句返回一個Java實體類型。較靈活;
2. resultsMap: 使用一個嵌套的結果映射來處理通過join查詢結果集,映射成Java實體類型。

 

 

 

Collection標籤

    property=多表查詢裝配的屬性名

    column=通過那一列關聯

    select=指定查詢語句,如果查詢語句在其他的namespace中,則需要寫全namespace.方法id

    javaType=java實體中接受的集合類型

 

例如,一個班級有多個學生。


 

學生實體中的字段
package cn.et.fuqiang.resultMap.entity;
 
public class Student {
     private Integer sid;//學生id
     private String sname; //學生姓名
     private Integer gid; //班級id
}
 
班級實體的字段
package cn.et.fuqiang.resultMap.entity;
 
import java.util.List;
 
public class Grade {
    private Integer id;//班級id
    private String gname;//班級名稱
      private List<Student> students;//該班級下所有的學生
    public Grade() {
       super();
       // TODO Auto-generated constructor stub
    }
   
}
 
 
在Grade實體中有個List<Student> students屬性,需要查詢學生表才能獲取,班級表中沒有該信息(所以要使用collection聚集查詢對應的結果集)

 

<!-- 使用resultMap 多表查詢 -->
    <resultMap type="grade" id="studentsInGrade">
        <!-- 主鍵適合用 id 標籤 -->
        <id column="gid" property="id" />
        <!-- 一對多     多表查詢     collection 可獲取一個集合
            property=多表查詢裝配的屬性名
            column=通過那一列關聯
            select=查詢語句
            javaType=java實體中接受的集合類型
        -->
       
    <collection property="students" column="gid" javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid"></collection>
    </resultMap>
在namespace="cn.et.fuqiang.resultMap.xml.StudentXmlInterface"下的查詢語句和類型
<resultMap type="student" id="studentMap" autoMapping="true">
         <!-- 主鍵建議用id標籤   -->
         <id column="stuid" property="sid"/>
         <!-- 其他列可以用result標籤 -->
         <result column="stuname" property="sname"/>
</resultMap>
     <!-- 使用多表查詢班級   
    注意: resultMap使用時應注意,如果該resultMap中配置了多表查詢,如果是互相查詢可能造成死循環
       -->
     <select id="quserUserByGid" resultMap="studentMap">
         select * from student where gid=#{0}
     </select>
    
注意:因爲實例中學生的字段名與表中的不同所以,又加了一個學生查詢專屬的resultMap
在這個resultMap中

type:是上面定義的學生類(使用了別名,如果沒有別名可以寫全類名)

id:是這個resultMap的標識

因爲表中的字段與實體中的不同,所以使用了下面的子標籤,將表的列與實體的屬性做了映射。這樣才能將表中的值獲取到實體的屬性中。

<!-- 主鍵適合用 id 標籤 -->

<id column="gid" property="id"/>

使用多表查詢獲取某個班級下的所有學生

    

<collection property="students" column="gid"javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid"></collection>

     </resultMap>

(這裏的property是實體中的字段,而column=gid就相當於關聯的條件了

而select就是該關聯條件對應的查詢方法)

 

 

主sql

<select id="queryAllGrade" resultMap="studentsInGrade">
      select * from grade
</select>
執行該映射時就會自動根據resultMap自動調用其中內嵌的sql語句查詢所有學生

 

 

 

 

所有代碼實例

Mybatis配置

mybatis.xml
<?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>
    <!-- 指定jdbc的配置文件位置 -->
    <properties resource="cn/et/fuqiang/resultMap/jdbc.properties"></properties>
    <!-- 配置類別名 -->
    <typeAliases>
    <!-- 使用該標籤將該包下的所有類統一起別名   默認爲類名首字母小寫           -->
       <package name="cn.et.fuqiang.resultMap.entity"/>
    </typeAliases>
    <!-- 配置jdbc環境 -->
    <environments default="development">
       <environment id="development">
           <transactionManager type="JDBC" />
           <!-- 配置數據庫連接信息 -->
           <dataSource type="POOLED">
              <property name="driver" value="${driverClass}" />
              <property name="url" value="${url}" />
              <property name="username" value="${user}" />
              <property name="password" value="${password}" />
           </dataSource>
       </environment>
    </environments>
    <!-- 使用接口映射  配置查詢語句 -->
    <mappers>
    <!-- 註冊接口   如果使用的是註解則不需要mappers文件-->
       <!-- 有兩種方式配置mapper
       第一種  如果有mapper.xml文件直接配置該文件
       <mapper resource="mapper 文件的類路徑">
       第二種  配置接口的全類名
       <mapper class="接口的全類名">
       -->
       <mapper class="cn.et.fuqiang.resultMap.xml.StudentXmlInterface"/>
       <mapper class="cn.et.fuqiang.resultMap.xml.GradeXmlInterface"/>
    </mappers>
 
</configuration>
 
Jdbc連接四要素配置

jdbc.properties
url=jdbc:oracle:thin:@localhost:1521:orcl
user=mybatis
password=mybatis
driverClass=oracle.jdbc.OracleDriver
兩個實體

student實體
package cn.et.fuqiang.resultMap.entity;
 
public class Student {
    private Integer sid;//學生id
    private String sname;//學生姓名
    private Integer gid;//班級id
    private Grade grade;//所屬班級的所有信息
    public Student() {
        super();
        // TODO Auto-generated constructor stub
    }
    public Student(Integer sid, String sname, Integer gid, Grade grade) {
        super();
        this.sid = sid;
        this.sname = sname;
        this.gid = gid;
        this.grade = grade;
    }
    public Integer getSid() {
        return sid;
    }
    public void setSid(Integer sid) {
        this.sid = sid;
    }
    public String getSname() {
        return sname;
    }
    public void setSname(String sname) {
        this.sname = sname;
    }
    public Integer getGid() {
        return gid;
    }
    public void setGid(Integer gid) {
        this.gid = gid;
    }
    public Grade getGrade() {
        return grade;
    }
    public void setGrade(Grade grade) {
        this.grade = grade;
    }
}
 
Grade實體
package cn.et.fuqiang.resultMap.entity;
 
import java.util.List;
 
public class Grade {
    private Integer id;//班級
    private String gname;//班級名稱
    private List<Student> students;//該班級下所有的學生
    public Grade() {
        super();
        // TODO Auto-generated constructor stub
    }
    public Grade(Integer id, String gname) {
        super();
        this.id = id;
        this.gname = gname;
    }
   
    public String getGname() {
        return gname;
    }
    public void setGname(String gname) {
        this.gname = gname;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public List<Student> getStudents() {
        return students;
    }
    public void setStudents(List<Student> students) {
        this.students = students;
    }
   
}
 
 

兩個接口映射的接口

GradeXmlInterface.java
package cn.et.fuqiang.resultMap.xml;
 
import java.util.List;
 
import cn.et.fuqiang.resultMap.entity.Grade;
 
public interface GradeXmlInterface {
    public Grade queryGradeById(Integer id);
    public List<Grade> queryAllGrade();
}
 
StudentXmlInterface.java
package cn.et.fuqiang.resultMap.xml;
 
import java.util.List;
 
import cn.et.fuqiang.resultMap.entity.Student;
 
public interface StudentXmlInterface {
    public Student queryStudentById(Integer sid);
    public List<Student> quserAllStudent();
    public List<Student> quserUserByGid();
}
 
 

兩個映射的mapper.xml

GradeXmlInterface.xml
<?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="cn.et.fuqiang.resultMap.xml.GradeXmlInterface">
    <!-- 當實體的屬性名與表名不一致時可使用 resultMap標籤指定對應的列值給對應的屬性
       autoMapping是設置是否自動裝配屬性名與表的列名對應的變量
    -->
    <resultMap type="grade" id="gradeMap" autoMapping="false">
       <!-- 主鍵適合用 id 標籤 -->
       <id column="gid" property="id"/>
       <!-- 其他的用result標籤 -->
       <result column="gname" property="gname"/>
    </resultMap>
    <select id="queryGradeById" resultMap="gradeMap">
       select * from grade where gid=#{0}
    </select>
   
    <!-- 使用resultMap 多表查詢 -->
    <resultMap type="grade" id="studentsInGrade">
       <!-- 主鍵適合用 id 標籤 -->
       <id column="gid" property="id" />
       <!-- 一對多     多表查詢     collection 可獲取一個集合
           property=多表查詢裝配的屬性名
           column=通過那一列關聯
           select=查詢語句
           javaType=java實體中接受的集合類型
       -->
      
       <collection property="students" column="gid" javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid"></collection>
    </resultMap>
 
       <!-- 使用多表查詢在查詢班級的同時根據上面的resultMap查出對應的所有學生 
       注意: resultMap使用時應注意,如果該resultMap中配置了多表查詢,如果是互相查詢可能造成死循環
      -->
    <select id="queryAllGrade" resultMap="studentsInGrade">
       select * from grade
    </select>
   
 
</mapper>
StudentXmlInterface.xml
<?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="cn.et.fuqiang.resultMap.xml.StudentXmlInterface">
    <!-- 當實體的屬性名與表名不一致時可使用 resultMap標籤指定對應的列值給對應的屬性
       autoMapping是設置是否自動裝配屬性名與表的列名對應的變量(不寫默認是true)
    -->
    <resultMap type="student" id="studentMap" autoMapping="true">
       <!-- 主鍵建議用id標籤   -->
       <id column="stuid" property="sid"/>
       <!-- 其他列可以用result標籤 -->
       <result column="stuname" property="sname"/>
    </resultMap>
    <select id="queryStudentById" resultMap="studentMap">
       select * from student where stuid=#{0}
    </select>
    <!-- 使用resultMap 多表查詢 -->
    <resultMap type="student" id="gradeInStudent">
       <!-- 主鍵建議用id標籤   -->
       <id column="stuid" property="sid"/>
      
       <!-- 其他列可以用result標籤 -->
       <result column="stuname" property="sname"/>
       <!-- 多對一     多表查詢     association
           property=多表查詢裝配的屬性名
           column=通過那一列關聯
           select=查詢語句
       -->
       <association property="grade" column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById"></association>
    </resultMap>
    <select id="quserAllStudent" resultMap="gradeInStudent">
       select * from student
    </select>
   
   
    <!-- 使用多表查詢班級   
       注意: resultMap使用時應注意,如果該resultMap中配置了多表查詢,如果是互相查詢可能造成死循環
      -->
    <select id="quserUserByGid" resultMap="studentMap">
       select * from student where gid=#{0}
    </select>
   
</mapper>
 

 

測試類

package cn.et.fuqiang.resultMap.xml;

 

import java.io.InputStream;

import java.util.List;

 

import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import org.junit.Test;

 

import cn.et.fuqiang.resultMap.entity.Grade;

import cn.et.fuqiang.resultMap.entity.Student;

 

 

public class ResultMapXmlTest {

   private static SqlSession session;

   private static GradeXmlInterface gradeInter=null;

   private static StudentXmlInterface studentInter=null;

   static{

       //mybatis的配置文件

       String resource = "mybatis.xml";

       //使用類加載器加載mybatis的配置文件(它也加載關聯的映射文件)

       InputStream is =GradeXmlInterface.class.getResourceAsStream(resource);

       //構建sqlSession的工廠

       SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);

       //使用MyBatis提供的Resources類加載mybatis的配置文件(它也加載關聯的映射文件)

       //Readerreader = Resources.getResourceAsReader(resource);

       //構建sqlSession的工廠

       //SqlSessionFactorysessionFactory = new SqlSessionFactoryBuilder().build(reader);

       //創建能執行映射文件中sql的sqlSession

       session = sessionFactory.openSession();

       gradeInter=session.getMapper(GradeXmlInterface.class);

       studentInter=session.getMapper(StudentXmlInterface.class);

   }

   //@Test

   public void queryGradeTest(){

       Grade grade=gradeInter.queryGradeById(1);

       System.out.println(grade.getId()+"------"+grade.getGname());

   }

   //@Test

   public void queryStudentTest(){

       Student student=studentInter.queryStudentById(1);

       System.out.println(student.getSid()+"------"+student.getSname());

   }

   //@Test

   public void queryGradeInStudent(){

       List<Student> students=studentInter.quserAllStudent();

       for (Student stu : students) {

            System.out.println(stu.getSid()+"------"+stu.getSname()+"-------"+stu.getGrade().getGname());

       }

       

   }

   @Test

   public void queryStudentInGrade(){

       List<Grade> students=gradeInter.queryAllGrade();

       for (Grade grade : students) {

            System.out.println(grade.getGname()+"班"+grade.getStudents().size()+"人");

       }

   }

}
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章