溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

04. Mybatis的resultMap基本應(yīng)用

發(fā)布時(shí)間:2020-07-19 00:52:02 來(lái)源:網(wǎng)絡(luò) 閱讀:4704 作者:IMUKL8 欄目:軟件技術(shù)

resultMap 元素是 MyBatis 中最重要最強(qiáng)大的元素。它就是讓你遠(yuǎn)離 90%的需要從結(jié)果集中取出數(shù)據(jù)的 JDBC 代碼的那個(gè)東西,而且在一些情形下允許你做一些 JDBC 不支持的事情。事實(shí)上,編寫相似于對(duì)復(fù)雜語(yǔ)句聯(lián)合映射這些等同的代碼,也許可以跨過(guò)上千行的代碼。

ResultMap 的設(shè)計(jì)就是簡(jiǎn)單語(yǔ)句不需要明確的結(jié)果映射,而很多復(fù)雜語(yǔ)句確實(shí)需要描述它們的關(guān)系。

你已經(jīng)看到簡(jiǎn)單映射語(yǔ)句的示例了,但沒(méi)有明確的 resultMap。比如:

<select id=”selectUsers” parameterType=”int” resultType=”hashmap”>
select id, username, hashedPassword
from some_table
where id = #{id}
</select>

這樣一個(gè)語(yǔ)句簡(jiǎn)單作用于所有列被自動(dòng)映射到 HashMap 的鍵上,這由resultType 屬性指定。這在很多情況下是有用的,但是 HashMap 不能很好描述一個(gè)領(lǐng)域模型。那樣你的應(yīng)用程序?qū)?huì)使用 JavaBeans 或 POJOs來(lái)作為領(lǐng)域模型。MyBatis 對(duì)兩者都支持??纯聪旅孢@個(gè) JavaBean:

package com.someapp.model;
public class User {
    private int id;
    private String username;
    private String hashedPassword;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getHashedPassword() {
        return hashedPassword;
    }
    public void setHashedPassword(String hashedPassword) {
        this.hashedPassword = hashedPassword;
    }
}

基于 JavaBean 的規(guī)范,上面這個(gè)類有 3 個(gè)屬性:id,username 和hashedPassword。這些在 select 語(yǔ)句中會(huì)精確匹配到列名。這樣的一個(gè) JavaBean 可以被映射到結(jié)果集,就像映射到 HashMap 一樣簡(jiǎn)單。

<select id=”selectUsers” parameterType=”int”
resultType=”com.someapp.model.User”>
select id, username, hashedPassword
from some_table
where id = #{id}
</select>

要記住類型別名是你的伙伴。使用它們你可以不用輸入類的全路徑。比如:

<!-- 在XML配置文件中-->
<typeAlias type=”com.someapp.model.User” alias=”User”/>
<!-- 在SQL映射的XML文件中-->
<select id=”selectUsers” parameterType=”int”
resultType=”User”>
select id, username, hashedPassword
from some_table
where id = #{id}
</select>

這些情況下,MyBatis 會(huì)在幕后自動(dòng)創(chuàng)建一個(gè) ResultMap,基于屬性名來(lái)映射列到JavaBean 的屬性上。如果列名沒(méi)有精確匹配,你可以在列名上使用 select 字句的別名(一個(gè)標(biāo)準(zhǔn)的 SQL 特性)來(lái)匹配標(biāo)簽。比如:

<select id=”selectUsers” parameterType=”int” resultType=”User”>
select
user_id as “id”,
user_name as “userName”,
hashed_password as “hashedPassword”
from some_table
where id = #{id}
</select>

那么如何試用外部的 resultMap:

<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>

引用它的語(yǔ)句使用 resultMap 屬性就行了(注意我們?nèi)サ袅?resultType 屬性)。比如:

<select id=”selectUsers” parameterType=”int”
resultMap=”userResultMap”>
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>

ReulstMap只是字段與屬性之間的映射關(guān)系的集合,那么,真正的字段與屬性之間的映射關(guān)系,ResultMapping(org.apache.ibatis.mapping.ResultMapping)屬性映射來(lái)描述。

ResultMap數(shù)據(jù)結(jié)構(gòu)如下:

package org.apache.ibatis.mapping;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import org.apache.ibatis.session.Configuration;

/**
 * 結(jié)果映射,保存著表與對(duì)象之間的映射關(guān)系
 *
 */
public class ResultMap {
        // 對(duì)應(yīng)<resultMap>的id屬性
    private String id;
    // 對(duì)應(yīng)<resultMap>的type屬性
    private Class<?> type;
    // 對(duì)應(yīng)除<discriminator>元素外的所有屬性映射關(guān)系
    private List<ResultMapping> resultMappings;
    // 對(duì)應(yīng)所有屬性映射中帶有ID標(biāo)志的映射關(guān)系,包括<id>元素和<constructor>的<idArg>子元素
    private List<ResultMapping> idResultMappings;
    // 對(duì)應(yīng)所有屬性映射中帶有Constructor標(biāo)志的映射關(guān)系,包括<constructor>所有子元素
    private List<ResultMapping> constructorResultMappings;
    // 對(duì)應(yīng)所有屬性映射中不帶有Constructor標(biāo)志的映射關(guān)系
    private List<ResultMapping> propertyResultMappings;
    // 對(duì)應(yīng)所有屬性映射中的column屬性的集合
    private Set<String> mappedColumns;
    // 鑒別器,對(duì)應(yīng)<discriminator>元素
    private Discriminator discriminator;
    // 是否含有嵌套的結(jié)果映射,
    // 如果某個(gè)屬性映射存在resultMap屬性,且不存在resultSet屬性,則為true
    private boolean hasNestedResultMaps;
    // 是否含有嵌套查詢,
    // 如果某個(gè)屬性映射存在select屬性,則為true
    private boolean hasNestedQueries;
    // 是否開(kāi)啟自動(dòng)映射
    private Boolean autoMapping;
}

而且在ResultMap類內(nèi)部有一個(gè)靜態(tài)的構(gòu)造類,如下:

  //靜態(tài)內(nèi)部類,建造者模式
  public static class Builder {
    private ResultMapping resultMapping = new ResultMapping();

    public Builder(Configuration configuration, String property, String column, TypeHandler<?> typeHandler) {
      this(configuration, property);
      resultMapping.column = column;
      resultMapping.typeHandler = typeHandler;
    }

    public Builder(Configuration configuration, String property, String column, Class<?> javaType) {
      this(configuration, property);
      resultMapping.column = column;
      resultMapping.javaType = javaType;
    }

    public Builder(Configuration configuration, String property) {
      resultMapping.configuration = configuration;
      resultMapping.property = property;
      resultMapping.flags = new ArrayList<ResultFlag>();
      resultMapping.composites = new ArrayList<ResultMapping>();
      resultMapping.lazy = configuration.isLazyLoadingEnabled();
    }

    public Builder javaType(Class<?> javaType) {
      resultMapping.javaType = javaType;
      return this;
    }

    public Builder jdbcType(JdbcType jdbcType) {
      resultMapping.jdbcType = jdbcType;
      return this;
    }

    public Builder nestedResultMapId(String nestedResultMapId) {
      resultMapping.nestedResultMapId = nestedResultMapId;
      return this;
    }

    public Builder nestedQueryId(String nestedQueryId) {
      resultMapping.nestedQueryId = nestedQueryId;
      return this;
    }

    public Builder resultSet(String resultSet) {
      resultMapping.resultSet = resultSet;
      return this;
    }

    public Builder foreignColumn(String foreignColumn) {
      resultMapping.foreignColumn = foreignColumn;
      return this;
    }

    public Builder notNullColumns(Set<String> notNullColumns) {
      resultMapping.notNullColumns = notNullColumns;
      return this;
    }

    public Builder columnPrefix(String columnPrefix) {
      resultMapping.columnPrefix = columnPrefix;
      return this;
    }

    public Builder flags(List<ResultFlag> flags) {
      resultMapping.flags = flags;
      return this;
    }

    public Builder typeHandler(TypeHandler<?> typeHandler) {
      resultMapping.typeHandler = typeHandler;
      return this;
    }

    public Builder composites(List<ResultMapping> composites) {
      resultMapping.composites = composites;
      return this;
    }

    public Builder lazy(boolean lazy) {
      resultMapping.lazy = lazy;
      return this;
    }

    public ResultMapping build() {
      // lock down collections
      resultMapping.flags = Collections.unmodifiableList(resultMapping.flags);
      resultMapping.composites = Collections.unmodifiableList(resultMapping.composites);
      resolveTypeHandler();
      validate();
      return resultMapping;
    }

    //一些驗(yàn)證邏輯,驗(yàn)證result map有沒(méi)有寫錯(cuò)
    private void validate() {
      // Issue #697: cannot define both nestedQueryId and nestedResultMapId
      if (resultMapping.nestedQueryId != null && resultMapping.nestedResultMapId != null) {
        throw new IllegalStateException("Cannot define both nestedQueryId and nestedResultMapId in property " + resultMapping.property);
      }
      // Issue #5: there should be no mappings without typehandler
      if (resultMapping.nestedQueryId == null && resultMapping.nestedResultMapId == null && resultMapping.typeHandler == null) {
        throw new IllegalStateException("No typehandler found for property " + resultMapping.property);
      }
      // Issue #4 and GH #39: column is optional only in nested resultmaps but not in the rest
      if (resultMapping.nestedResultMapId == null && resultMapping.column == null && resultMapping.composites.isEmpty()) {
        throw new IllegalStateException("Mapping is missing column attribute for property " + resultMapping.property);
      }
      if (resultMapping.getResultSet() != null) {
        int numColums = 0;
        if (resultMapping.column != null) {
          numColums = resultMapping.column.split(",").length;
        }
        int numForeignColumns = 0;
        if (resultMapping.foreignColumn != null) {
          numForeignColumns = resultMapping.foreignColumn.split(",").length;
        }
        if (numColums != numForeignColumns) {
          throw new IllegalStateException("There should be the same number of columns and foreignColumns in property " + resultMapping.property);
        }
      }
    }

    private void resolveTypeHandler() {
      if (resultMapping.typeHandler == null && resultMapping.javaType != null) {
        Configuration configuration = resultMapping.configuration;
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType, resultMapping.jdbcType);
      }
    }

    public Builder column(String column) {
      resultMapping.column = column;
      return this;
    }
  }

ResultMapping數(shù)據(jù)結(jié)構(gòu)如下:

package org.apache.ibatis.mapping;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

/**
 * 字段映射 一個(gè)ResultMapping實(shí)例對(duì)應(yīng)ResultSet中一個(gè)字段到j(luò)avaBean中一個(gè)屬性的映射關(guān)系
 * 
 * sql元素中,除了<discriminator>子元素以外的其他元素都會(huì)生成此類型實(shí)例,
 * 其中包括:
 * <idArg><arg><id><result><association><collection>
 * 
 * 內(nèi)部包含Builder類,負(fù)責(zé)數(shù)據(jù)的整合和校驗(yàn),不負(fù)責(zé)傳入?yún)?shù)的解析
 * @author Administrator
 *
 */
public class ResultMapping {
    // 核心配置對(duì)象
    private Configuration configuration;
    // 屬性名,對(duì)應(yīng)元素的property屬性
    private String property;
    // 字段名,對(duì)應(yīng)元素的column屬性
    private String column;
    // 屬性的java類型,對(duì)應(yīng)元素的javaType屬性
    private Class<?> javaType;
    // 字段的jdbc類型,對(duì)應(yīng)元素的jdbcType屬性
    private JdbcType jdbcType;
    // 類型處理器,對(duì)應(yīng)元素的typeHandler屬性
    private TypeHandler<?> typeHandler;

    // 對(duì)應(yīng)元素的resultMap屬性應(yīng)用名稱空間后的結(jié)果
    private String nestedResultMapId;
    // 對(duì)應(yīng)元素的select屬性應(yīng)用名稱空間后的結(jié)果
    private String nestedQueryId;
    // 對(duì)應(yīng)元素的notNullColumn屬性拆分后的結(jié)果
    private Set<String> notNullColumns;
    // 對(duì)應(yīng)元素的columnPrefix屬性
    private String columnPrefix;
    // 處理后的標(biāo)志,標(biāo)志共兩個(gè):id和constructor
    private List<ResultFlag> flags;
    // 對(duì)應(yīng)元素的column屬性拆分后生成的結(jié)果,composites.size()>0會(huì)時(shí)column為null
    private List<ResultMapping> composites;
    // 對(duì)應(yīng)元素的resultSet屬性
    private String resultSet;
    // 對(duì)應(yīng)元素的foreignColumn屬性
    private String foreignColumn;
    // 是否延遲加載,對(duì)應(yīng)元素的fetchType屬性值,lazy則為true否則為false
    // 也直接從配置對(duì)象中讀取
    private boolean lazy;
}
向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI