MyBatis 與 SpringBoot 整合:註解和xml兩種使用方式介紹

solocoder發表於2018-12-13

MyBatis 是一款優秀的持久層框架,它支援定製化 SQL、儲存過程以及高階對映。MyBatis 避免了幾乎所有的 JDBC 程式碼和手動設定引數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和對映原生資訊,將介面和 Java 的 POJOs(Plain Old Java Objects,普通的 Java物件)對映成資料庫中的記錄。


無論是使用註解還是 xml 對映檔案配置方式,在使用之前有兩步是必須的:

  1. 引入依賴

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    複製程式碼
  2. 在啟動類上加註解 @MapperScan 指明 mapper 類的位置

    @MapperScan("com.solo.coderiver.project.mapper")
    public class ProjectApplication {
        public static void main(String[] args) {
            SpringApplication.run(ProjectApplication.class, args);
        }
    }
    複製程式碼

一、註解方式

相比於 xml 對映方式,註解方式明顯更簡潔,但沒有 xml 方式靈活。目前大部分公司還是主要用 xml 方式。

package com.solo.coderiver.project.mapper;

import com.solo.coderiver.project.dataobject.ProjectInfo;
import org.apache.ibatis.annotations.*;

/**
 * 註解方式使用 mybatis 增刪改查
 */
@Mapper
public interface ProjectMapper {

    @Select("SELECT * FROM project_info WHERE project_id = #{id}")
    @Results({
            @Result(property = "projectId", column = "project_id"),
            @Result(property = "projectName", column = "project_name"),
            @Result(property = "projectAvatar", column = "project_avatar"),
            @Result(property = "projectDifficulty", column = "project_difficulty"),
            @Result(property = "categoryType", column = "category_type"),
            @Result(property = "categoryName", column = "category_name"),
            @Result(property = "projectProgress", column = "project_progress"),
            @Result(property = "projectStatus", column = "project_status"),
            @Result(property = "projectIntroduce", column = "project_introduce"),
            @Result(property = "projectCreatorId", column = "project_creator_id"),
            @Result(property = "teamId", column = "team_id"),
    })
    ProjectInfo findProjectById(String id);

    @Insert("INSERT INTO " +
            "project_info(project_id, project_name, project_avatar, project_difficulty," +
            " category_type, category_name, project_progress, project_status, " +
            "project_introduce, project_creator_id, team_id) " +
            "VALUES(#{projectId}, #{projectName}, #{projectAvatar}, #{projectDifficulty}," +
            "#{categoryType}, #{categoryName}, #{projectProgress}, #{projectStatus}," +
            "#{projectIntroduce}, #{projectCreatorId}, #{teamId})")
    int insertProject(ProjectInfo info);


    @Update("UPDATE project_info set project_name = #{name} where project_id = #{id}")
    int updateProjectName(String id, String name);

    @Delete("DELETE FROM project_info WHERE project_id = #{id}")
    int deleteProject(String id);

}
複製程式碼

直接將 sql 語句寫在註解裡,免去了配置 xml 檔案。但有個顯而易見的缺點是如果 sql 太長了,像欄位多的 @Insert ,如果換行需要用 + 連線,不利於後期維護。

MyBatis 支援的註解屬性表:

註解 使用物件 相對應的 XML 描述
@CacheNamespace <cache> 為給定的名稱空間(比如類)配置快取。屬性有:implemetation, eviction, flushInterval, size, readWrite, blockingproperties
@Property N/A <property> 指定引數值或佔位值(placeholder)(能被 mybatis-config.xml內的配置屬性覆蓋)。屬性有:name, value。(僅在MyBatis 3.4.2以上版本生效)
@CacheNamespaceRef <cacheRef> 參照另外一個名稱空間的快取來使用。屬性有:value, name。如果你使用了這個註解,你應設定 value 或者 name 屬性的其中一個。value 屬性用於指定 Java 型別而指定名稱空間(名稱空間名就是指定的 Java 型別的全限定名),name 屬性(這個屬性僅在MyBatis 3.4.2以上版本生效)直接指定了名稱空間的名字。
@ConstructorArgs 方法 <constructor> 收集一組結果傳遞給一個結果物件的構造方法。屬性有:value,它是形式引數陣列。
@Arg N/A <arg> <idArg> 單引數構造方法,是 ConstructorArgs 集合的一部分。屬性有:id, column, javaType, jdbcType, typeHandler, selectresultMap。id 屬性是布林值,來標識用於比較的屬性,和<idArg> XML 元素相似。
@TypeDiscriminator 方法 <discriminator> 一組例項值被用來決定結果對映的表現。屬性有:column, javaType, jdbcType, typeHandlercases。cases 屬性是例項陣列。
@Case N/A <case> 單獨例項的值和它對應的對映。屬性有:value, type, results。results 屬性是結果陣列,因此這個註解和實際的 ResultMap 很相似,由下面的 Results 註解指定。
@Results 方法 <resultMap> 結果對映的列表,包含了一個特別結果列如何被對映到屬性或欄位的詳情。屬性有:value, id。value 屬性是 Result 註解的陣列。這個 id 的屬性是結果對映的名稱。
@Result N/A <result> <id> 在列和屬性或欄位之間的單獨結果對映。屬性有:id, column, javaType, jdbcType, typeHandler, one, many。id 屬性是一個布林值,來標識應該被用於比較(和在 XML 對映中的<id>相似)的屬性。one 屬性是單獨的聯絡,和 <association> 相似,而 many 屬性是對集合而言的,和<collection>相似。它們這樣命名是為了避免名稱衝突。
@One N/A <association> 複雜型別的單獨屬性值對映。屬性有:select,已對映語句(也就是對映器方法)的全限定名,它可以載入合適型別的例項。fetchType會覆蓋全域性的配置引數 lazyLoadingEnabled。注意 聯合對映在註解 API中是不支援的。這是因為 Java 註解的限制,不允許迴圈引用。
@Many N/A <collection> 對映到複雜型別的集合屬性。屬性有:select,已對映語句(也就是對映器方法)的全限定名,它可以載入合適型別的例項的集合,fetchType 會覆蓋全域性的配置引數 lazyLoadingEnabled。注意 聯合對映在註解 API中是不支援的。這是因為 Java 註解的限制,不允許迴圈引用
@MapKey 方法 這是一個用在返回值為 Map 的方法上的註解。它能夠將存放物件的 List 轉化為 key 值為物件的某一屬性的 Map。屬性有: value,填入的是物件的屬性名,作為 Map 的 key 值。
@Options 方法 對映語句的屬性 這個註解提供訪問大範圍的交換和配置選項的入口,它們通常在對映語句上作為屬性出現。Options 註解提供了通俗易懂的方式來訪問它們,而不是讓每條語句註解變複雜。屬性有:useCache=true, flushCache=FlushCachePolicy.DEFAULT, resultSetType=FORWARD_ONLY, statementType=PREPARED, fetchSize=-1, timeout=-1, useGeneratedKeys=false, keyProperty="id", keyColumn="", resultSets=""。值得一提的是, Java 註解無法指定 null 值。因此,一旦你使用了 Options 註解,你的語句就會被上述屬性的預設值所影響。要注意避免預設值帶來的預期以外的行為。 注意: keyColumn 屬性只在某些資料庫中有效(如 Oracle、PostgreSQL等)。請在插入語句一節檢視更多關於 keyColumnkeyProperty 兩者的有效值詳情。
@Insert @Update @Delete @Select 方法 <insert> <update> <delete> <select> 這四個註解分別代表將會被執行的 SQL 語句。它們用字串陣列(或單個字串)作為引數。如果傳遞的是字串陣列,字串之間先會被填充一個空格再連線成單個完整的字串。這有效避免了以 Java 程式碼構建 SQL 語句時的“丟失空格”的問題。然而,你也可以提前手動連線好字串。屬性有:value,填入的值是用來組成單個 SQL 語句的字串陣列。
@InsertProvider @UpdateProvider @DeleteProvider @SelectProvider 方法 <insert> <update> <delete> <select> 允許構建動態 SQL。這些備選的 SQL 註解允許你指定類名和返回在執行時執行的 SQL 語句的方法。(自從MyBatis 3.4.6開始,你可以用 CharSequence 代替 String 來返回型別返回值了。)當執行對映語句的時候,MyBatis 會例項化類並執行方法,類和方法就是填入了註解的值。你可以把已經傳遞給對映方法了的物件作為引數,"Mapper interface type" 和 "Mapper method" 會經過 ProviderContext (僅在MyBatis 3.4.5及以上支援)作為引數值。(MyBatis 3.4及以上的版本,支援多引數傳入)屬性有: type, methodtype 屬性需填入類。method 需填入該類定義了的方法名。注意 接下來的小節將會討論類,能幫助你更輕鬆地構建動態 SQL。
@Param 引數 N/A 如果你的對映方法的形參有多個,這個註解使用在對映方法的引數上就能為它們取自定義名字。若不給出自定義名字,多引數(不包括 RowBounds 引數)則先以 "param" 作字首,再加上它們的引數位置作為引數別名。例如 #{param1}, #{param2},這個是預設值。如果註解是 @Param("person"),那麼引數就會被命名為 #{person}
@SelectKey 方法 <selectKey> 這個註解的功能與 <selectKey> 標籤完全一致,用在已經被 @Insert@InsertProvider@Update@UpdateProvider 註解了的方法上。若在未被上述四個註解的方法上作 @SelectKey 註解則視為無效。如果你指定了 @SelectKey 註解,那麼 MyBatis 就會忽略掉由 @Options 註解所設定的生成主鍵或設定(configuration)屬性。屬性有:statement 填入將會被執行的 SQL 字串陣列,keyProperty 填入將會被更新的引數物件的屬性的值,before 填入 truefalse 以指明 SQL 語句應被在插入語句的之前還是之後執行。resultType 填入 keyProperty 的 Java 型別和用 StatementPreparedStatementCallableStatement 中的 STATEMENTPREPAREDCALLABLE 中任一值填入 statementType。預設值是 PREPARED
@ResultMap 方法 N/A 這個註解給 @Select 或者 @SelectProvider 提供在 XML 對映中的 <resultMap> 的id。這使得註解的 select 可以複用那些定義在 XML 中的 ResultMap。如果同一 select 註解中還存在 @Results 或者 @ConstructorArgs,那麼這兩個註解將被此註解覆蓋。
@ResultType 方法 N/A 此註解在使用了結果處理器的情況下使用。在這種情況下,返回型別為 void,所以 Mybatis 必須有一種方式決定物件的型別,用於構造每行資料。如果有 XML 的結果對映,請使用 @ResultMap 註解。如果結果型別在 XML 的 <select> 節點中指定了,就不需要其他的註解了。其他情況下則使用此註解。比如,如果 @Select 註解在一個將使用結果處理器的方法上,那麼返回型別必須是 void 並且這個註解(或者@ResultMap)必選。這個註解僅在方法返回型別是 void 的情況下生效。
@Flush 方法 N/A 如果使用了這個註解,定義在 Mapper 介面中的方法能夠呼叫 SqlSession#flushStatements() 方法。(Mybatis 3.3及以上)

二、xml 方式使用

2.1 快速實現

xml 配置方式使用 MyBatis 主要步驟有以下三步:

  1. 配置 application.yml
  2. 編寫 mapper xml 檔案
  3. 寫 mapper java 程式碼

1. 配置 application.yml

首先在 application.yml 中配置資料庫實體物件的位置和mapper檔案的位置,配置了 type-aliases-package 後就可以在 xml 檔案中直接寫類名,而不用寫全限定類名啦。

mybatis:
  type-aliases-package: com.solo.coderiver.project.dataobject
  mapper-locations: classpath:mapper/*.xml
複製程式碼

如果不配置 mapper-locations,會報如下錯誤:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.solo.coderiver.project.mapper.ProjectCategoryMapper.insert
複製程式碼

2. 編寫 mapper xml 對映檔案

以專案型別 ProjectCategory 為例,新增 MyBatis 的增刪改查實現

ProjectCategoryMapper.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="com.solo.coderiver.project.mapper.ProjectCategoryMapper" >

    <resultMap id="BaseResultMap" type="ProjectCategory">
        <id column="category_id" property="categoryId" jdbcType="INTEGER"/>
        <result column="category_name" property="categoryName" jdbcType="VARCHAR" />
        <result column="category_type" property="categoryType" jdbcType="INTEGER" />
    </resultMap>

    <insert id="insert" parameterType="ProjectCategory">
        insert into project_category(category_id, category_name, category_type)
        values(#{categoryId, jdbcType=INTEGER}, #{categoryName, jdbcType=VARCHAR}, #{categoryType, jdbcType=INTEGER})
    </insert>

    <delete id="deleteByType" parameterType="java.lang.Integer">
        delete from project_category
        where category_type = #{type, jdbcType=INTEGER}
    </delete>

    <update id="updateByType" parameterType="ProjectCategory">
        update project_category
        set category_name = #{categoryName}
        where category_type = #{categoryType}
    </update>

    <select id="selectByType" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select * from project_category
        where category_type = #{categoryType}
    </select>
</mapper>
複製程式碼

後面會講配置的詳細屬性。

3. 寫 mapper java 程式碼

ProjectCategoryMapper.java

@Mapper
public interface ProjectCategoryMapper {

    int insert(ProjectCategory category);

    int deleteByType(Integer type);

    int updateByType(ProjectCategory category);

    ProjectCategory selectByType(Integer type);
}
複製程式碼

新建一個介面,方法名要跟 ProjectCategoryMapper.xml<insert><delete><update><select> 等 sql 操作語句的 id 保持一致,否則會報錯。

然後再在類名上加註解 @Mapper 就可以啦。

ProjectCategory物件

package com.solo.coderiver.project.dataobject;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
@Data
public class ProjectCategory {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer categoryId;

    private String categoryName;

    private Integer categoryType;

    public ProjectCategory() {
    }

    public ProjectCategory(Integer categoryType, String categoryName) {
        this.categoryName = categoryName;
        this.categoryType = categoryType;
    }
}
複製程式碼

2.2 xml 對映檔案詳解

SQL 對映檔案有很少的幾個頂級元素(按照它們應該被定義的順序):

  • cache – 給定名稱空間的快取配置。
  • cache-ref – 其他名稱空間快取配置的引用。
  • resultMap – 是最複雜也是最強大的元素,用來描述如何從資料庫結果集中來載入物件。
  • parameterMap – 已廢棄!老式風格的引數對映。內聯引數是首選,這個元素可能在將來被移除,這裡不會記錄。
  • sql – 可被其他語句引用的可重用語句塊。
  • insert – 對映插入語句
  • update – 對映更新語句
  • delete – 對映刪除語句
  • select – 對映查詢語句

select

查詢語句是 MyBatis 中最常用的元素之一,光能把資料存到資料庫中價值並不大,如果還能重新取出來才有用,多數應用也都是查詢比修改要頻繁。對每個插入、更新或刪除操作,通常對應多個查詢操作。這是 MyBatis 的基本原則之一,也是將焦點和努力放到查詢和結果對映的原因。

上文中實現了一個簡單的 select 語句:

<select id="selectByType" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select * from project_category
    where category_type = #{categoryType}
</select>
複製程式碼

這個語句被稱作 selectByType,接受一個 int(或 Integer)型別的引數,並返回一個 BaseResultMap 定義的 ProjectCategory 物件。這條語句的作用是根據傳入的型別查詢專案型別資訊。

注意引數符號:#{categoryType} , 它表明接收傳入的引數 categoryType

select 的屬性表:

<select
  id="selectPerson"
  parameterType="int"
  parameterMap="deprecated"
  resultType="hashmap"
  resultMap="personResultMap"
  flushCache="false"
  useCache="true"
  timeout="10000"
  fetchSize="256"
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY">
複製程式碼
屬性 描述
id 在名稱空間中唯一的識別符號,可以被用來引用這條語句。
parameterType 將會傳入這條語句的引數類的完全限定名或別名。這個屬性是可選的,因為 MyBatis 可以通過 TypeHandler 推斷出具體傳入語句的引數,預設值為 unset。
parameterMap 這是引用外部 parameterMap 的已經被廢棄的方法。使用內聯引數對映和 parameterType 屬性。
resultType 從這條語句中返回的期望型別的類的完全限定名或別名。注意如果是集合情形,那應該是集合可以包含的型別,而不能是集合本身。使用 resultType 或 resultMap,但不能同時使用。
resultMap 外部 resultMap 的命名引用。結果集的對映是 MyBatis 最強大的特性,對其有一個很好的理解的話,許多複雜對映的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同時使用。
flushCache 將其設定為 true,任何時候只要語句被呼叫,都會導致本地快取和二級快取都會被清空,預設值:false。
useCache 將其設定為 true,將會導致本條語句的結果被二級快取,預設值:對 select 元素為 true。
timeout 這個設定是在丟擲異常之前,驅動程式等待資料庫返回請求結果的秒數。預設值為 unset(依賴驅動)。
fetchSize 這是嘗試影響驅動程式每次批量返回的結果行數和這個設定值相等。預設值為 unset(依賴驅動)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一個。這會讓 MyBatis 分別使用 Statement,PreparedStatement 或 CallableStatement,預設值:PREPARED。
resultSetType FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一個,預設值為 unset (依賴驅動)。
databaseId 如果配置了 databaseIdProvider,MyBatis 會載入所有的不帶 databaseId 或匹配當前 databaseId 的語句;如果帶或者不帶的語句都有,則不帶的會被忽略。
resultOrdered 這個設定僅針對巢狀結果 select 語句適用:如果為 true,就是假設包含了巢狀結果集或是分組了,這樣的話當返回一個主結果行的時候,就不會發生有對前面結果集的引用的情況。這就使得在獲取巢狀的結果集的時候不至於導致記憶體不夠用。預設值:false
resultSets 這個設定僅對多結果集的情況適用,它將列出語句執行後返回的結果集並每個結果集給一個名稱,名稱是逗號分隔的。

像本文的例子一樣,select 常用的屬性其實就是 idparameterTyperesultMap 三個,其他的瞭解即可。

insert, update 和 delete

資料變更語句 insert,update 和 delete 的實現非常接近:

<insert
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  keyProperty=""
  keyColumn=""
  useGeneratedKeys=""
  timeout="20">

<update
  id="updateAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

<delete
  id="deleteAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">
複製程式碼

屬性表

屬性 描述
id 名稱空間中的唯一識別符號,可被用來代表這條語句。
parameterType 將要傳入語句的引數的完全限定類名或別名。這個屬性是可選的,因為 MyBatis 可以通過 TypeHandler 推斷出具體傳入語句的引數,預設值為 unset。
parameterMap 這是引用外部 parameterMap 的已經被廢棄的方法。使用內聯引數對映和 parameterType 屬性。
flushCache 將其設定為 true,任何時候只要語句被呼叫,都會導致本地快取和二級快取都會被清空,預設值:true(對應插入、更新和刪除語句)。
timeout 這個設定是在丟擲異常之前,驅動程式等待資料庫返回請求結果的秒數。預設值為 unset(依賴驅動)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一個。這會讓 MyBatis 分別使用 Statement,PreparedStatement 或 CallableStatement,預設值:PREPARED。
useGeneratedKeys (僅對 insert 和 update 有用)這會令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法來取出由資料庫內部生成的主鍵(比如:像 MySQL 和 SQL Server 這樣的關聯式資料庫管理系統的自動遞增欄位),預設值:false。
keyProperty (僅對 insert 和 update 有用)唯一標記一個屬性,MyBatis 會通過 getGeneratedKeys 的返回值或者通過 insert 語句的 selectKey 子元素設定它的鍵值,預設:unset。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。
keyColumn (僅對 insert 和 update 有用)通過生成的鍵值設定表中的列名,這個設定僅在某些資料庫(像 PostgreSQL)是必須的,當主鍵列不是表中的第一列的時候需要設定。如果希望得到多個生成的列,也可以是逗號分隔的屬性名稱列表。
databaseId 如果配置了 databaseIdProvider,MyBatis 會載入所有的不帶 databaseId 或匹配當前 databaseId 的語句;如果帶或者不帶的語句都有,則不帶的會被忽略。

三個更新操作常用的屬性只有兩個:idparameterType,其他的瞭解即可。

Result Maps

resultMap 元素是 MyBatis 中最重要最強大的元素。它可以讓你從 90% 的 JDBC ResultSets 資料提取程式碼中解放出來,並在一些情形下允許你做一些 JDBC 不支援的事情。 實際上,在對複雜語句進行聯合對映的時候,它很可能可以代替數千行的同等功能的程式碼。 ResultMap 的設計思想是,簡單的語句不需要明確的結果對映,而複雜一點的語句只需要描述它們的關係就行了。

下面是 resultMap 元素的概念檢視:

resultMap
  • constructor

    - 用於在例項化類時,注入結果到構造方法中

    • idArg - ID 引數;標記出作為 ID 的結果可以幫助提高整體效能
    • arg - 將被注入到構造方法的一個普通結果
  • id – 一個 ID 結果;標記出作為 ID 的結果可以幫助提高整體效能

  • result – 注入到欄位或 JavaBean 屬性的普通結果

  • association

    – 一個複雜型別的關聯;許多結果將包裝成這種型別

    • 巢狀結果對映 – 關聯可以指定為一個 resultMap 元素,或者引用一個
  • collection

    – 一個複雜型別的集合

    • 巢狀結果對映 – 集合可以指定為一個 resultMap 元素,或者引用一個
  • discriminator

    – 使用結果值來決定使用哪個

    resultMap

    • case

      – 基於某些值的結果對映

      • 巢狀結果對映 – 一個 case 也是一個對映它本身的結果,因此可以包含很多相同的元素,或者它可以參照一個外部的 resultMap
屬性 描述
id 當前名稱空間中的一個唯一標識,用於標識一個result map.
type 類的完全限定名, 或者一個型別別名 (內建的別名可以參考上面的表格).
autoMapping 如果設定這個屬性,MyBatis將會為這個ResultMap開啟或者關閉自動對映。這個屬性會覆蓋全域性的屬性 autoMappingBehavior。預設值為:unset。

為了方便理解,再把上面專案中定義的 resultMap 貼出來

<resultMap id="BaseResultMap" type="ProjectCategory">
    <id column="category_id" property="categoryId" jdbcType="INTEGER"/>
    <result column="category_name" property="categoryName" jdbcType="VARCHAR" />
    <result column="category_type" property="categoryType" jdbcType="INTEGER" />
</resultMap>
複製程式碼

id 和 result 標籤中的屬性表如下:

屬性 描述
property 對映到列結果的欄位或屬性。如果用來匹配的 JavaBeans 存在給定名字的屬性,那麼它將會被使用。否則 MyBatis 將會尋找給定名稱 property 的欄位。 無論是哪一種情形,你都可以使用通常的點式分隔形式進行復雜屬性導航。比如,你可以這樣對映一些簡單的東西: “username” ,或者對映到一些複雜的東西: “address.street.number” 。
column 資料庫中的列名,或者是列的別名。一般情況下,這和 傳遞給 resultSet.getString(columnName) 方法的引數一樣。
javaType 一個 Java 類的完全限定名,或一個型別別名(參考上面內建型別別名 的列表) 。如果你對映到一個 JavaBean,MyBatis 通常可以斷定型別。 然而,如果你對映到的是 HashMap,那麼你應該明確地指定 javaType 來保證期望的行為。
jdbcType JDBC 型別,所支援的 JDBC 型別參見這個表格之後的“支援的 JDBC 型別”。 只需要在可能執行插入、更新和刪除的允許空值的列上指定 JDBC 型別。這是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 程式設計,你需要對可能為 null 的值指定這個型別。
typeHandler 我們在前面討論過的預設型別處理器。使用這個屬性,你可以覆蓋默 認的型別處理器。這個屬性值是一個型別處理 器實現類的完全限定名,或者是型別別名。

關於 jdbcType

發現有些小夥伴在寫對映檔案的時候,都習慣性的把所有的需要傳入的引數都加上 jdbcType="",那到底什麼情況下需要指明,什麼情況下不需要指明呢?

查閱官方文件看到了官方的描述:

如果一個列允許 null 值,並且會傳遞值 null 的引數,就必須要指定 JDBC Type。
複製程式碼

也就是說只有當一個列允許 null 並有可能傳入 null 時,才必須要指定 JDBC Type,其他情況是不需要指定的。當然指定了也沒錯,就是多寫點程式碼。

明白了什麼時候需要指定,那還有個問題,java 中的型別跟 jdbcType 的型別如何對應呢?如果對應關係寫錯了也會報錯。下面就整理出了兩者的對應關係:

JDBCTypeJavaType 對應關係

  JDBCType            JavaType
    CHAR                String
    VARCHAR             String
    LONGVARCHAR         String
    NUMERIC             java.math.BigDecimal
    DECIMAL             java.math.BigDecimal
    BIT                 boolean
    BOOLEAN             boolean
    TINYINT             byte
    SMALLINT            short
    INTEGER             int
    BIGINT              long
    REAL                float
    FLOAT               double
    DOUBLE              double
    BINARY              byte[]
    VARBINARY           byte[]
    LONGVARBINARY       byte[]
    DATE                java.sql.Date
    TIME                java.sql.Time
    TIMESTAMP           java.sql.Timestamp
    CLOB                Clob
    BLOB                Blob
    ARRAY               Array
    DISTINCT            mapping of underlying type
    STRUCT              Struct
    REF                 Ref
    DATALINK            java.net.URL[color=red][/color]
複製程式碼

2.3 補充

如果查詢返回的結果是個列表怎麼辦?如何提取出可複用的 sql 呢?

日常使用中還有這兩個常見的應用場景,下面就以專案成員表 ProejctMember 來演示一下。

一個專案可以對應多個成員,所以根據專案 id 查詢成員的話肯定查出來多條資料。

ProjectMemberMapper.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="com.solo.coderiver.project.mapper.ProjectMemberMapper">

    <resultMap id="BaseResultMap" type="ProjectMember">
        <id column="id" property="id" jdbcType="INTEGER"/>
        <result column="project_id" property="projectId" jdbcType="VARCHAR"/>
        <result column="project_name" property="projectName" jdbcType="VARCHAR"/>
        <result column="project_avatar" property="projectAvatar" jdbcType="VARCHAR"/>
        <result column="user_id" property="userId" jdbcType="VARCHAR"/>
        <result column="user_name" property="userName" jdbcType="VARCHAR"/>
        <result column="user_avatar" property="userAvatar" jdbcType="VARCHAR"/>
        <result column="user_role" property="userRole" jdbcType="INTEGER"/>
        <result column="role_name" property="roleName" jdbcType="VARCHAR"/>
        <result column="status" property="status" jdbcType="INTEGER"/>
    </resultMap>

    <sql id="baseColumns">
        id, project_id, project_name, project_avatar, user_id, user_name,
        user_avatar, user_role, role_name, status
    </sql>

    <select id="selectByProjectId" parameterType="java.lang.String" resultMap="BaseResultMap">
        select
        <include refid="baseColumns"/>
        from project_member
        where project_id = #{projectId, jdbcType=VARCHAR}
    </select>
</mapper>
複製程式碼

ProjectMemberMapper.java

package com.solo.coderiver.project.mapper;

import com.solo.coderiver.project.dataobject.ProjectMember;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface ProjectMemberMapper {

    List<ProjectMember> selectByProjectId(String projectId);
}
複製程式碼

返回結果是列表的話,對映檔案的 result 還是隻給定 List 內的型別即可。

<sql> 標籤來提出出可複用的 sql 語句

<sql id="baseColumns">
    id, project_id, project_name, project_avatar, user_id, user_name,
    user_avatar, user_role, role_name, status
</sql>
複製程式碼

在需要用到的地方用 <include> 標籤引入 sql

<select id="selectByProjectId" parameterType="java.lang.String"    		resultMap="BaseResultMap">
    select
    <include refid="baseColumns"/>
    from project_member
    where project_id = #{projectId, jdbcType=VARCHAR}
</select>
複製程式碼

然後在 service 中引用 mapper

package com.solo.coderiver.project.service.mybatis;

import com.solo.coderiver.project.dataobject.ProjectMember;
import com.solo.coderiver.project.mapper.ProjectMemberMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class ProjectMemberServiceMyBatisImpl {

    @Autowired
    ProjectMemberMapper mapper;

    public List<ProjectMember> findByProjectId(String projectId){
        return mapper.selectByProjectId(projectId);
    }
}
複製程式碼

單元測試

package com.solo.coderiver.project.service.mybatis;

import com.solo.coderiver.project.ProjectApplicationTests;
import com.solo.coderiver.project.dataobject.ProjectMember;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

import static org.junit.Assert.*;

public class ProjectMemberServiceMyBatisImplTest extends ProjectApplicationTests {

    @Autowired
    ProjectMemberServiceMyBatisImpl service;

    @Test
    public void findByProjectId() {
        List<ProjectMember> list = service.findByProjectId("1541062468073593543");
        Assert.assertNotEquals(0, list.size());
    }
}
複製程式碼

以上就是 MyBatis 在 SpringBoot 中的簡單使用方式介紹。


程式碼出自開源專案 coderiver,致力於打造全平臺型全棧精品開源專案。

coderiver 中文名 河碼,是一個為程式設計師和設計師提供專案協作的平臺。無論你是前端、後端、移動端開發人員,或是設計師、產品經理,都可以在平臺上釋出專案,與志同道合的小夥伴一起協作完成專案。

coderiver河碼 類似程式設計師客棧,但主要目的是方便各細分領域人才之間技術交流,共同成長,多人協作完成專案。暫不涉及金錢交易。

計劃做成包含 pc端(Vue、React)、移動H5(Vue、React)、ReactNative混合開發、Android原生、微信小程式、java後端的全平臺型全棧專案,歡迎關注。

專案地址:github.com/cachecats/c…

您的鼓勵是我前行最大的動力,歡迎點贊,歡迎送小星星✨ ~

相關文章