SSM框架開發web項目系列(三) MyBatis之resultMap及關聯映射

  前言

  在上篇MyBatis基礎篇中咱們獨立使用MyBatis構建了一個簡單的數據庫訪問程序,能夠實現單表的基本增刪改查等操做,經過該實例咱們能夠初步瞭解MyBatis操做數據庫須要的一些組成部分(配置文件、實體類、SQL映射文件、Mapper接口等等)和重要對象(SqlSession、Mapper實例等等)。有了總體認知後,咱們就能夠進一步深刻學習MyBatis的使用,resultMap本文主要圍繞resultMap展開。html

  resultMap做爲MyBatis的Sql映射文件中重要的元素之一,主要用來實現複雜的結果映射,其子元素結構以下:java

  constructor

  constructor是構造器的意思,對反射有基本瞭解的都應該不會陌生。藉此咱們先回顧一下前面定義的一個簡單resulMap映射內容mysql

<resultMap type="person" id="personResultMap" >
    <id column="id" property="id" />
    <result column="name" property="name" />
    <result column="gender" property="gender" />
</resultMap>

  上面是一個典型的在resultMap中定義數據表與實體類的映射關係,type這裏用的別名指向Person類,id爲該映射的惟一標識,用於在後面咱們的定義語句中引用,內部的id和result分別對應主鍵和普通字段定義,column指數據表中字段名,property指實體類中屬性名。在以前的示例中,經過這個一個映射關係,咱們查詢出來的結果就轉化爲了一個Person類對象。web

  這個Person類對象並不是由咱們建立出來,而是由mybatis調用了Person類的默認無參構造函數建立對象,再調用set方法爲對象賦值,這樣才返回了咱們想要的結果。下面結合以前的示例,作一些改動以便觀察。sql

package com.mmm.pojo;
/**
 * Person實體類
 * */
public class Person {
    
    private String id;
    
    private String name;
    
    private String gender;
    
    public String getId() {
        return id;
    }
    
    public void setId(String id) {
        this.id = id;
        System.out.println("爲主鍵屬性id賦值");
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
        System.out.println("爲屬性name賦值");
    }
    
    public String getGender() {
        return gender;
    }
    
    public void setGender(String gender) {
        this.gender = gender;
        System.out.println("爲屬性gender賦值");
    }
    
    public Person() {
        System.out.println("調用無參構造建立對象");
    }
}
<select id="selectById"resultMap="personResultMap">
    select * from `person` where id = #{id}
</select>

  調用該查找方法後會看到控制檯輸出以下信息:數據庫

  說明確實是mybatis調用了這些方法幫咱們建立了對象,基於構造器。提及構造器,在咱們定義的pojo實體類中,簡單的私有屬性加上set/get方法聲明即完成,在java類中,會默認提供一個無參的構造方法,例如這裏的Person類apache

public Person(){}

  咱們使用語句 Person p = new Person();這裏即調用的該無參構造。方法均可以重載,構造方法也不例外。可是很重要的一點,咱們在重載構造方法後,本來默認提供的無參構造就沒法使用了,須要顯示聲明,這點下面會給出示例。resultMap中constructor就是基於重載的帶參構造方法建立對象,以下:session

<resultMap type="person" id="personResultMap" >
    <constructor>
        <idArg column="id" javaType="string" />
        <arg column="name" javaType="string" />
        <arg column="gender" javaType="string" />
    </constructor>
</resultMap>

  在實體類中添加劇載的帶參構造方法mybatis

public Person(String id, String name, String gender) {
    this.id = id;
    this.name = name;
    this.gender = gender;
        System.out.println("直接調用帶參構造函數建立對象");
}

  再次調用查找方法後會看到控制檯輸出以下信息:app

  這樣一來,使用constructor一樣拿到了數據。

  這個時候若是咱們把實體類中的默認無參構造方法的顯示聲明刪除,即只有一個帶參的構造方法,這樣咱們再把Sql映射文件中<resultMap>內容還原成文中一開始定義的典型<id><result>等,再調用方法,爲了更明顯,在地址欄中經過controller層層調用到該方法,報錯乍一看,一大串異常,其實標題末尾一句話就出來了

  

  不存在該方法,該方法指的就是Person實體類中的無參構造方法,在以前咱們沒有顯示聲明沒有問題,那是由於在類中若是沒有任何的構造方法聲明,會默認提供一個無參構造;可是咱們使用construtor的時候在實體類中重載了一個帶參的構造方法,使得本來默認的無參構造沒法使用了,這裏若是還要須要使用,能夠在實體類中顯示聲明。

   id、result

  前面的單表典型基本映射就是由這兩個元素組成,因此應該比較好理解。id和result都用於將表中字段和實體類中屬性創建起映射關係,區別是id是用於主鍵字段的映射,result用於普通字段映射。兩個元素中都有5個屬性供定義,分別是column、property、javaType、jdbcType、typeHandler。

  column:這裏用於單表查詢的話能夠簡單的理解爲數據表中字段,前面也是相似處理,可是實際上並非這麼簡單,例如咱們在寫SQL語句時常常會使用到別名,別名的好處就不復述了,特別是進行多表查詢時,這裏咱們在SQL映射文件的<select>語句中也可使用別名,這個別名與這裏的column就對應上了,不注意這點的話可能出問題,後面介紹關聯查詢會有示例。

  property:實體類中定義的與表字段對應的屬性

  javaType:類型這裏可使用全名,也可使用別名,例如上面的string就是一個別名。咱們在定義基本的實體類映射時,mybatis通常能夠自動肯定類型。

  jdbcType:這裏是指針對數據表字段方的jdbc類型,mybatis中支持下面的 JDBC 類型。

  

  typeHandler:在使用數據庫建立表,定義字段名,選擇字段類型時,例如MySql中字符串類型varchar,而後咱們使用mybatis映射到實體類中變成Java字符串類型String,這個轉換過程是怎麼實現的?其中mybatis就已經預約義了一些類型處理器,也就是這個typeHandler,咱們也能夠根據須要自定義一些類型處理器。

  association(對一)

  association意思爲聯繫、關聯,針對單個(一個)對象,典型的一對1、多對一關聯映射關係均可以經過該元素實現,下面是基本定義內容。

<association property="關聯的對象在本對象實體類中的屬性名" column="關聯表的主鍵在本表中的字段名" javaType="關聯對象的實體類類型(可用別名)">
    <id column="ID" property="id" />
    <result column="數據表字段名" property="實體類屬性名" />
  <result column="數據表字段名" property="實體類屬性名" />
  ...
</association>

  經過上面的關聯定義,而後在<select>元素中定義多表關聯查詢語句便可實現關聯查詢效果。這個<association>內部的<id><result>等元素,咱們不必定要這樣定義,也能夠經過額外定義一個resultMap填入這些元素,而後<association>有一個resultMap屬性,值就填額外的reslutMap的id,這樣也能夠實現關聯映射。除此以外,也能夠經過一個關聯的嵌套查詢語句實現,即額外定義一個針對關聯表的<select>元素,而後<association>有一個select屬性,值就填<select>的id,這樣一樣能實現。

  collection(對多)

  collection意爲集合,這裏主要用於關聯多個對象的映射關係定義,例如典型的一對多,下面是基本定義內容。

<collection property="關聯的對象在本對象實體類中的屬性名" column="本表的主鍵在關聯表中的字段名" ofType="關聯集合中對象的實體類類型(可用別名)">
    <id column="ID" property="id" />
    <result column="數據表字段名" property="實體類屬性名" />
    <result column="數據表字段名" property="實體類屬性名" />
  ...
</collection>

   相似上面的association,也能夠額外定義<select>元素,而後引入,從而實現關聯查詢。

  關聯映射應用得較多,關聯屬性也相對複雜一些,光憑文字和片斷描述很難快速理解,下面以一個一對多和多對一結合的示例來總體應用一次。

  關聯映射實例(一對多/多對一)

  背景關係

  

  這裏以部門做爲查詢的實體對象,從圖中不難理解,部門與公司是多對一關係,部門與員工是一對多關係,咱們查詢一個部門對象時,要把這個部門所屬的公司信息查出來,還要把部門下面的全部員工信息查出來。

  準備工做

  這裏對象分公司、部門、員工一共三類,因此首先準備三張數據表,結構以下

 

 這裏要理解一點,基於主鍵關聯,不管是一對多,仍是多對一,在「多「」的一方表中,都有一個與「一」的一方表主鍵對應的字段,例如這裏的部門表中COMPANY_ID和員工表中的DEPT_ID,用來關聯對應表

  新建maven工程,這裏不須要建web項目,普通的java項目就行。pom依賴能夠參照SSM框架開發web項目系列(一) 環境搭建篇。總體結構以下所示

  

  而後新建三個對象實體類,以下所示

package com.mmm.pojo;
//公司
public class Company {
    private Integer id;              //主鍵
    private String companyName;     //公司名稱
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getCompanyName() {
        return companyName;
    }
    public void setCompanyName(String companyName) {
        this.companyName = companyName;
    }
    @Override
    public String toString() {
        return "Company [id=" + id + ", companyName=" + companyName + "]";
    }
}
package com.mmm.pojo;

import java.util.List;
//部門
public class Dept {
    private Integer id;             //主鍵
    private String deptName;    //部門名稱
    private Company company;    //部門所屬公司---------多對一
    private List<Emp> empList;    //部門下面員工---------一對多
    
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
    public Company getCompany() {
        return company;
    }
    public void setCompany(Company company) {
        this.company = company;
    }
    public List<Emp> getEmpList() {
        return empList;
    }
    public void setEmpList(List<Emp> empList) {
        this.empList = empList;
    }
    @Override
    public String toString() {
        return "Dept [id=" + id + ", deptName=" + deptName + ", company=" + company + ", empList=" + empList + "]";
    }
    
}
package com.mmm.pojo;
//員工實體類
public class Emp {
    private Integer id;            //主鍵
    private String name;        //員工姓名
    private String gender;        //員工性別
    
    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 String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    @Override
    public String toString() {
        return "Emp [id=" + id + ", name=" + name + ", gender=" + gender + "]";
    }
}

  配置文件(mybatis-config.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>
    <!-- 這裏能夠定義類的別名,在mapper.xml文件中應用會方便不少 -->
    <typeAliases>
        <typeAlias alias="emp" type="com.mmm.pojo.Emp" />
        <typeAlias alias="dept" type="com.mmm.pojo.Dept" />
        <typeAlias alias="company" type="com.mmm.pojo.Company" />
    </typeAliases>
    <!-- 環境配置 -->
    <environments default="envir">
        <environment id="envir">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/ssm?characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/mmm/mapper/deptMapper.xml"/>
    </mappers>
</configuration>

  mapper接口及對應sql映射文件

package com.mmm.mapper;
import com.mmm.pojo.Dept;
public interface DeptMapper {
    public Dept selectById(Integer id);
}
<?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.mmm.mapper.DeptMapper">
    <resultMap type="dept" id="deptResultMap">
        <!-- 下面的column就是前面說的要注意的,若是簡單的認爲是字段名會出問題 -->
        <id column="d_id" property="id" />
        <result column="d_name" property="deptName" />
        <!-- 一個公司下面多個部門,部門與公司,多對一關係 -->
        <association property="company" column="COMPANY_ID" javaType="company" resultMap="companyResultMap">
            <!-- <id column="ID" property="id" />
            <result column="COMPANY_NAME" property="companyName" /> -->
        </association>
        <!-- 一個部門下有多個員工,部門與員工,一對多關係 -->
        <collection property="empList" column="DEPT_ID" ofType="emp">
            <id column="ID" property="id" />
            <result column="EMP_NAME" property="name" />
            <result column="EMP_GENDER" property="gender" />
        </collection>
    </resultMap>
    
    <!-- 也能夠經過相似下面的定義而後引入到association中,association的resultMap屬性值對應下面resultMap的id -->
    <!-- <resultMap type="company" id="companyResultMap">
        <id column="ID" property="id" />
        <result column="COMPANY_NAME" property="companyName" />
    </resultMap> -->
    
    <!-- 根據主鍵id查找記錄 -->
    <select id="selectById" resultMap="deptResultMap">
        select e.*,d.ID AS d_id,d.DEPT_NAME AS d_name,c.* from `TBL_EMP` e, `TBL_DEPT` d, `TBL_COMPANY` c where d.ID = #{id} and d.COMPANY_ID = c.id and d.ID = e.DEPT_ID; 
    </select>
</mapper>

   最後,測試一下是否能實現關聯查詢

package com.mmm.test;

import java.io.IOException;
import java.io.Reader;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import com.mmm.mapper.DeptMapper;
import com.mmm.pojo.Dept;

public class TestMyBatis {
    
    @Test
    public void testCore() throws IOException {
        //直接實例SqlSessionFactoryBuilder對象
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //MyBatis配置文件路徑
        String path = "mybatis-config.xml";
        //經過路徑獲取輸入流
        Reader reader = Resources.getResourceAsReader(path);
        //經過reader構建sessionFactory
        SqlSessionFactory sessionFactory = builder.build(reader);
        //獲取SqlSession對象
        SqlSession sqlSession = sessionFactory.openSession();
        //獲取Mapper實例
        DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
        //根據id查找制定的部門,並關聯查詢出其公司和員工信息
        Dept dept = mapper.selectById(10001);
        System.out.println(dept);
    }
}

  控制檯輸出信息過長,複製下來粘貼以下內容:

  Dept [id=10001, deptName=資產部, company=Company [id=10003, companyName=公司一], empList=[Emp [id=10001, name=員工一, gender=男], Emp [id=10002, name=員工二, gender=男], Emp [id=10003, name=員工三, gender=男]]]

  能夠看到,至此基本實現了,查詢部門時,也關聯查詢出了所屬公司信息、下面全部部門員工信息。

  咱們在瀏覽網站,點擊連接跳轉,每一個頁面的刷新,大可能是不斷在請求查詢數據庫中的數據,固然也不全是查詢,例如會有日誌記錄,也是插入操做等等。查詢是一個比較重要的內容,怎樣關聯查詢,怎麼優化查詢語句,減小查詢次數,提升查詢速度等等都是須要去思考和學習的地方。

相關文章
相關標籤/搜索