天天看點

Mybatis代碼生成器Mybatis-Generator使用詳解

前提

最近在做創業項目的時候因為有比較多的新需求,需要頻繁基于

DDL

生成

Mybatis

适合的實體、

Mapper

接口和映射檔案。其中,代碼生成器是

MyBatis Generator(MBG)

,用到了

Mybatis-Generator-Core

相關依賴,這裡通過一篇文章詳細地分析這個代碼生成器的使用方式。本文編寫的時候使用的

Mybatis-Generator

版本為

1.4.0

,其他版本沒有進行過調研。

引入插件

Mybatis-Generator

的運作方式有很多種:

  • 基于

    mybatis-generator-core-x.x.x.jar

    和其

    XML

    配置檔案,通過指令行運作。
  • 通過

    Ant

    Task

    結合其

    XML

    配置檔案運作。
  • Maven

    插件運作。
  • Java

    代碼和其

    XML

  • Java

    代碼和程式設計式配置運作。
  • Eclipse Feature

    運作。

這裡隻介紹通過

Maven

插件運作和通過

Java

XML

配置檔案運作這兩種方式,兩種方式有個特點:都要提前編寫好

XML

配置檔案。個人感覺

XML

配置檔案相對直覺,後文會花大量篇幅去說明

XML

配置檔案中的配置項及其作用。這裡先注意一點:預設的配置檔案為

ClassPath:generatorConfig.xml

通過編碼和配置檔案運作

通過編碼方式去運作插件先需要引入

mybatis-generator-core

依賴,編寫本文的時候最新的版本為:

<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.4.0</version>
</dependency>
           

假設編寫好的

XML

配置檔案是

ClassPath

下的

generator-configuration.xml

,那麼使用代碼生成器的編碼方式大緻如下:

List<String> warnings = new ArrayList<>();
// 如果已經存在生成過的檔案是否進行覆寫
boolean overwrite = true;
File configFile = new File("ClassPath路徑/generator-configuration.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator generator = new MyBatisGenerator(config, callback, warnings);
generator.generate(null);
           

通過Maven插件運作

如果使用

Maven

插件,那麼不需要引入

mybatis-generator-core

依賴,隻需要引入一個

Maven

的插件

mybatis-generator-maven-plugin

<plugins>
    <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.4.0</version>
        <executions>
            <execution>
                <id>Generate MyBatis Artifacts</id>
                <goals>
                    <goal>generate</goal>
                </goals>
            </execution>
        </executions>
        <configuration>
            <!-- 輸出詳細資訊 -->
            <verbose>true</verbose>
            <!-- 覆寫生成檔案 -->
            <overwrite>true</overwrite>
            <!-- 定義配置檔案 -->
            <configurationFile>${basedir}/src/main/resources/generator-configuration.xml</configurationFile>
        </configuration>
    </plugin>
</plugins>
           

mybatis-generator-maven-plugin

的更詳細配置和可選參數可以參考:Running With Maven。插件配置完畢之後,使用下面的指令即可運作:

mvn mybatis-generator:generate
           

XML配置檔案詳解

XML

配置檔案才是

Mybatis-Generator

的核心,它用于控制代碼生成的所有行為。所有非标簽獨有的公共配置的

Key

可以在

mybatis-generator-core

PropertyRegistry

類中找到。下面是一個相對完整的配置檔案的模闆:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>

  <properties resource="db.properties"/>

  <classPathEntry location="/Program Files/IBM/SQLLIB/java/db2java.zip" />

  <context id="DB2Tables" targetRuntime="MyBatis3">

    <jdbcConnection driverClass="COM.ibm.db2.jdbc.app.DB2Driver"
        connectionURL="jdbc:db2:TEST"
        userId="db2admin"
        password="db2admin">
    </jdbcConnection>

    <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>

    <commentGenerator>
        <property name="suppressDate" value="true"/>
        <property name="suppressAllComments" value="true"/>
    </commentGenerator>

    <javaTypeResolver>
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>

    <javaModelGenerator targetPackage="test.model" targetProject="\MBGTestProject\src">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

    <sqlMapGenerator targetPackage="test.xml"  targetProject="\MBGTestProject\src">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>

    <javaClientGenerator type="XMLMAPPER" targetPackage="test.dao"  targetProject="\MBGTestProject\src">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>

    <table schema="DB2ADMIN" tableName="ALLTYPES" domainObjectName="Customer" >
      <property name="useActualColumnNames" value="true"/>
      <generatedKey column="ID" sqlStatement="DB2" identity="true" />
      <columnOverride column="DATE_FIELD" property="startDate" />
      <ignoreColumn column="FRED" />
      <columnOverride column="LONG_VARCHAR_FIELD" jdbcType="VARCHAR" />
    </table>

  </context>
</generatorConfiguration>
           

配置檔案中,最外層的标簽為

<generatorConfiguration>

,它的子标簽包括:

  • 0或者1個

    <properties>

    标簽,用于指定全局配置檔案,下面可以通過占位符的形式讀取

    <properties>

    指定檔案中的值。
  • 0或者N個

    <classPathEntry>

    标簽,

    <classPathEntry>

    隻有一個

    location

    屬性,用于指定資料源驅動包(

    jar

    或者

    zip

    )的絕對路徑,具體選擇什麼驅動包取決于連接配接什麼類型的資料源。
  • 1或者N個

    <context>

    标簽,用于運作時的解析模式和具體的代碼生成行為,是以這個标簽裡面的配置是最重要的。

下面分别列舉和分析一下

<context>

标簽和它的主要子标簽的一些屬性配置和功能。

context标簽

<context>

标簽在

mybatis-generator-core

中對應的實作類為

org.mybatis.generator.config.Context

,它除了大量的子标簽配置之外,比較主要的屬性是:

  • id

    Context

    示例的唯一

    ID

    ,用于輸出錯誤資訊時候作為唯一标記。
  • targetRuntime

    :用于執行代碼生成模式。
  • defaultModelType

    :控制

    Domain

    類的生成行為。執行引擎為

    MyBatis3DynamicSql

    MyBatis3Kotlin

    時忽略此配置,可選值:
    • conditional

      :預設值,類似

      hierarchical

      ,但是隻有一個主鍵的時候會合并所有屬性生成在同一個類。
    • flat

      :所有内容全部生成在一個對象中。
    • hierarchical

      :鍵生成一個XXKey對象,Blob等單獨生成一個對象,其他簡單屬性在一個對象中。

targetRuntime

屬性的可選值比較多,這裡做個簡單的小結:

屬性 功能描述

MyBatis3DynamicSql

預設值,相容

JDK8+

MyBatis 3.4.2+

,不會生成

XML

映射檔案,忽略

<sqlMapGenerator>

的配置項,也就是

Mapper

全部注解化,依賴于

MyBatis Dynamic SQL

類庫

MyBatis3Kotlin

行為類似于

MyBatis3DynamicSql

,不過相容

Kotlin

的代碼生成

MyBatis3

提供基本的基于動态

SQL

CRUD

方法和

XXXByExample

方法,會生成

XML

映射檔案

MyBatis3Simple

SQL

CRUD

XML

MyBatis3DynamicSqlV1

已經過時,不推薦使用

筆者偏向于把

SQL

檔案和代碼分離,是以一般選用

MyBatis3

MyBatis3Simple

。例如:

<context id="default" targetRuntime="MyBatis3">
           

<context>

标簽支援0或N個

<property>

<property>

的可選屬性有:

property屬性 預設值 備注

autoDelimitKeywords

是否使用分隔符号包覆資料庫關鍵字

false

例如

MySQL

中會使用反引号包覆關鍵字

beginningDelimiter

分隔符号的開始符号

"

endingDelimiter

分隔符号的結束号

"

javaFileEncoding

檔案的編碼

系統預設值

來源于

java.nio.charset.Charset

javaFormatter

類名和檔案格式化器

DefaultJavaFormatter

JavaFormatter

DefaultJavaFormatter

targetJava8

是否JDK8和啟動其特性

true

kotlinFileEncoding

Kotlin

檔案編碼

系統預設值

java.nio.charset.Charset

kotlinFormatter

Kotlin

DefaultKotlinFormatter

KotlinFormatter

DefaultKotlinFormatter

xmlFormatter

XML

檔案格式化器

DefaultXmlFormatter

XmlFormatter

DefaultXmlFormatter

jdbcConnection标簽

<jdbcConnection>

标簽用于指定資料源的連接配接資訊,它在

mybatis-generator-core

org.mybatis.generator.config.JDBCConnectionConfiguration

,主要屬性包括:

是否必須

driverClass

資料源驅動的全類名

Y

connectionURL

JDBC

的連接配接

URL

Y

userId

連接配接到資料源的使用者名

N

password

連接配接到資料源的密碼

N

commentGenerator标簽

<commentGenerator>

标簽是可選的,用于控制生成的實體的注釋内容。它在

mybatis-generator-core

org.mybatis.generator.internal.DefaultCommentGenerator

,可以通過可選的

type

屬性指定一個自定義的

CommentGenerator

實作。

<commentGenerator>

<property>

<property>

suppressAllComments

是否生成注釋

false

suppressDate

是否在注釋中添加生成的時間戳

false

dateFormat

配合

suppressDate

使用,指定輸出時間戳的格式

java.util.Date#toString()

addRemarkComments

是否輸出表和列的

Comment

資訊

false

筆者建議保持預設值,也就是什麼注釋都不輸出,生成代碼幹淨的實體。

javaTypeResolver标簽

<javaTypeResolver>

标簽是

<context>

的子标簽,用于解析和計算資料庫列類型和

Java

類型的映射關系,該标簽隻包含一個

type

屬性,用于指定

org.mybatis.generator.api.JavaTypeResolver

接口的實作類。

<javaTypeResolver>

<property>

<property>

forceBigDecimals

是否強制把所有的數字類型強制使用

java.math.BigDecimal

類型表示

false

useJSR310Types

是否支援

JSR310

,主要是

JSR310

的新日期類型

false

如果

useJSR310Types

屬性設定為

true

,那麼生成代碼的時候類型映射關系如下(主要針對日期時間類型):

資料庫(JDBC)類型 Java類型

DATE

java.time.LocalDate

TIME

java.time.LocalTime

TIMESTAMP

java.time.LocalDateTime

TIME_WITH_TIMEZONE

java.time.OffsetTime

TIMESTAMP_WITH_TIMEZONE

java.time.OffsetDateTime

引入

mybatis-generator-core

後,可以檢視

JavaTypeResolver

的預設實作為

JavaTypeResolverDefaultImpl

,從它的源碼可以得知一些映射關系:

BIGINT --> Long
BIT --> Boolean
INTEGER --> Integer
SMALLINT --> Short
TINYINT --> Byte
......
           

有些時候,我們希望

INTEGER

SMALLINT

TINYINT

都映射為

Integer

,那麼我們需要覆寫

JavaTypeResolverDefaultImpl

的構造方法:

public class DefaultJavaTypeResolver extends JavaTypeResolverDefaultImpl {

    public DefaultJavaTypeResolver() {
        super();
        typeMap.put(Types.SMALLINT, new JdbcTypeInformation("SMALLINT",
                new FullyQualifiedJavaType(Integer.class.getName())));
        typeMap.put(Types.TINYINT, new JdbcTypeInformation("TINYINT",
                new FullyQualifiedJavaType(Integer.class.getName())));
    }
}
           

注意一點的是這種自定義實作

JavaTypeResolver

接口的方式使用程式設計式運作

MBG

會相對友善,如果需要使用

Maven

插件運作,那麼需要把上面的

DefaultJavaTypeResolver

類打包到插件中。

javaModelGenerator标簽

<javaModelGenerator标簽>

<context>

的子标簽,主要用于控制實體(

Model

)類的代碼生成行為。它支援的屬性如下:

targetPackage

生成的實體類的包名

Y

club.throwable.model

targetProject

生成的實體類檔案相對于項目(根目錄)的位置

Y

src/main/java

<javaModelGenerator标簽>

<property>

<property>

constructorBased

是否生成一個帶有所有字段屬性的構造函數

false

MyBatis3Kotlin

模式下忽略此屬性配置

enableSubPackages

是否允許通過

Schema

生成子包

false

如果為

true

,例如包名為

club.throwable

,如果

Schema

xyz

,那麼實體類檔案最終會生成在

club.throwable.xyz

目錄

exampleTargetPackage

生成的伴随實體類的

Example

類的包名
-

exampleTargetProject

Example

類檔案相對于項目(根目錄)的位置

immutable

是否不可變

false

true

,則不會生成

Setter

方法,所有字段都使用

final

修飾,提供一個帶有所有字段屬性的構造函數

rootClass

為生成的實體類添加父類

value

指定父類的全類名即可

trimStrings

Setter

方法是否對字元串類型進行一次

trim

操作

false

javaClientGenerator标簽

<javaClientGenerator>

<context>

的子标簽,主要用于控制

Mapper

接口的代碼生成行為。它支援的屬性如下:

type

Mapper

接口生成政策

Y

<context>

标簽的

targetRuntime

屬性為

MyBatis3DynamicSql

MyBatis3Kotlin

時此屬性配置忽略

targetPackage

生成的

Mapper

接口的包名

Y

club.throwable.mapper

targetProject

Mapper

接口檔案相對于項目(根目錄)的位置

Y

src/main/java

type

屬性的可選值如下:

  • ANNOTATEDMAPPER

    Mapper

    接口生成的時候依賴于注解和

    SqlProviders

    (也就是純注解實作),不會生成

    XML

    映射檔案。
  • XMLMAPPER

    Mapper

    接口生成接口方法,對應的實作代碼生成在

    XML

    映射檔案中(也就是純映射檔案實作)。
  • MIXEDMAPPER

    Mapper

    接口生成的時候複雜的方法實作生成在

    XML

    映射檔案中,而簡單的實作通過注解和

    SqlProviders

    實作(也就是注解和映射檔案混合實作)。

注意兩點:

  • <context>

    targetRuntime

    屬性指定為

    MyBatis3Simple

    的時候,

    type

    隻能選用

    ANNOTATEDMAPPER

    XMLMAPPER

  • <context>

    targetRuntime

    MyBatis3

    type

    可以選用

    ANNOTATEDMAPPER

    XMLMAPPER

    MIXEDMAPPER

<javaClientGenerator>

<property>

<property>

enableSubPackages

Schema

false

true

club.throwable

Schema

xyz

,那麼

Mapper

接口檔案最終會生成在

club.throwable.xyz

useLegacyBuilder

是否通過

SQL Builder

生成動态

SQL

false

rootInterface

為生成的

Mapper

接口添加父接口

value

指定父接口的全類名即可

sqlMapGenerator标簽

<sqlMapGenerator>

<context>

XML

映射檔案的代碼生成行為。它支援的屬性如下:

targetPackage

XML

映射檔案的包名

Y

mappings

targetProject

XML

映射檔案相對于項目(根目錄)的位置

Y

src/main/resources

<sqlMapGenerator>

<property>

<property>

enableSubPackages

Schema

false

plugin标簽

<plugin>

<context>

的子标簽,用于引入一些插件對代碼生成的一些特性進行擴充,該标簽隻包含一個

type

org.mybatis.generator.api.Plugin

接口的實作類。内置的插件實作見Supplied Plugins。例如:引入

org.mybatis.generator.plugins.SerializablePlugin

插件會讓生成的實體類自動實作

java.io.Serializable

接口并且添加

serialVersionUID

屬性。

table标簽

<table>

<context>

的子标簽,主要用于配置要生成代碼的資料庫表格,定制一些代碼生成行為等等。它支援的屬性衆多,列舉如下:

tableName

資料庫表名稱

Y

t_order

schema

資料庫

Schema

N

catalog

Catalog

N

alias

表名稱标簽

N

如果指定了此值,則查詢列的時候結果格式為

alias_column

domainObjectName

表對應的實體類名稱,可以通過

.

指定包路徑

N

如果指定了

bar.User

,則包名為

bar

,實體類名稱為

User

mapperName

表對應的

Mapper

接口類名稱,可以通過

.

N

bar.UserMapper

bar

Mapper

接口類名稱為

UserMapper

sqlProviderName

動态

SQL

提供類

SqlProvider

的類名稱

N

enableInsert

是否允許生成

insert

方法

N

預設值為

true

,執行引擎為

MyBatis3DynamicSql

MyBatis3Kotlin

時忽略此配置

enableSelectByPrimaryKey

selectByPrimaryKey

N

true

MyBatis3DynamicSql

MyBatis3Kotlin

enableSelectByExample

selectByExample

N

true

MyBatis3DynamicSql

MyBatis3Kotlin

enableUpdateByPrimaryKey

updateByPrimaryKey

N

true

MyBatis3DynamicSql

MyBatis3Kotlin

enableDeleteByPrimaryKey

deleteByPrimaryKey

N

true

MyBatis3DynamicSql

MyBatis3Kotlin

enableDeleteByExample

deleteByExample

N

true

MyBatis3DynamicSql

MyBatis3Kotlin

enableCountByExample

countByExample

N

true

MyBatis3DynamicSql

MyBatis3Kotlin

enableUpdateByExample

updateByExample

N

true

MyBatis3DynamicSql

MyBatis3Kotlin

selectByPrimaryKeyQueryId

value

指定對應的主鍵列提供清單查詢功能

N

執行引擎為

MyBatis3DynamicSql

MyBatis3Kotlin

selectByExampleQueryId

value

指定對應的查詢

ID

提供清單查詢功能

N

MyBatis3DynamicSql

MyBatis3Kotlin

modelType

覆寫

<context>

defaultModelType

N

<context>

defaultModelType

escapeWildcards

是否對通配符進行轉義

N

delimitIdentifiers

标記比對表名稱的時候是否需要使用分隔符去标記生成的SQL

N

delimitAllColumns

是否所有的列都添加分隔符

N

false

,如果設定為

true

,所有列名會添加起始和結束分隔符

<table>

<property>

<property>

constructorBased

是否為實體類生成一個帶有所有字段的構造函數

false

MyBatis3Kotlin

的時候此屬性忽略

ignoreQualifiersAtRuntime

是否在運作時忽略别名

false

true

,則不會在生成表的時候把

schema

catalog

作為表的字首

immutable

實體類是否不可變

false

MyBatis3Kotlin

modelOnly

是否僅僅生成實體類

false

rootClass

如果配置此屬性,則實體類會繼承此指定的超類

-

如果有主鍵屬性會把主鍵屬性在超類生成

rootInterface

如果配置此屬性,則實體類會實作此指定的接口

-

MyBatis3Kotlin

MyBatis3DynamicSql

runtimeCatalog

指定運作時的

Catalog

-

當生成表和運作時的表的

Catalog

不一樣的時候可以使用該屬性進行配置

runtimeSchema

Schema

-

Schema

runtimeTableName

指定運作時的表名稱

-

當生成表和運作時的表的表名稱不一樣的時候可以使用該屬性進行配置

selectAllOrderByClause

指定字句内容添加到

selectAll()

方法的

order by

子句之中

-

MyBatis3Simple

的時候此屬性才适用

trimStrings

實體類的字元串類型屬性會做

trim

處理

-

MyBatis3Kotlin

useActualColumnNames

是否使用列名作為實體類的屬性名

false

useColumnIndexes

XML

映射檔案中生成的

ResultMap

使用列索引定義而不是列名稱

false

MyBatis3Kotlin

MyBatis3DynamicSql

useCompoundPropertyNames

是否把列名和列備注拼接起來生成實體類屬性名

false

<table>

标簽還支援衆多的非

property

的子标簽:

  • 0或1個

    <generatedKey>

    用于指定主鍵生成的規則,指定此标簽後會生成一個

    <selectKey>

    标簽:
<!-- column:指定主鍵列 -->
<!-- sqlStatement:查詢主鍵的SQL語句,例如填寫了MySql,則使用SELECT LAST_INSERT_ID() -->
<!-- type:可選值為pre或者post,pre指定selectKey标簽的order為BEFORE,post指定selectKey标簽的order為AFTER -->
<!-- identity:true的時候,指定selectKey标簽的order為AFTER -->
<generatedKey column="id" sqlStatement="MySql" type="post" identity="true" />
           
  • <domainObjectRenamingRule>

    用于指定實體類重命名規則:
<!-- searchString中正則命中的實體類名部分會替換為replaceString -->
<domainObjectRenamingRule searchString="^Sys" replaceString=""/>
<!-- 例如 SysUser會變成User -->
<!-- 例如 SysUserMapper會變成UserMapper -->
           
  • <columnRenamingRule>

    用于指定列重命名規則:
<!-- searchString中正則命中的列名部分會替換為replaceString -->
<columnRenamingRule searchString="^CUST_" replaceString=""/>
<!-- 例如 CUST_BUSINESS_NAME會變成BUSINESS_NAME(useActualColumnNames=true) -->
<!-- 例如 CUST_BUSINESS_NAME會變成businessName(useActualColumnNames=false) -->
           
  • 0或N個

    <columnOverride>

    用于指定具體列的覆寫映射規則:
<!-- column:指定要覆寫配置的列 -->
<!-- property:指定要覆寫配置的屬性 -->
<!-- delimitedColumnName:是否為列名添加定界符,例如`{column}` -->
<!-- isGeneratedAlways:是否一定生成此列 -->
<columnOverride column="customer_name" property="customerName" javaType="" jdbcType="" typeHandler="" delimitedColumnName="" isGeneratedAlways="">
   <!-- 覆寫table或者javaModelGenerator級别的trimStrings屬性配置 -->
   <property name="trimStrings" value="true"/>
<columnOverride/>
           
  • <ignoreColumn>

    用于指定忽略生成的列:
<ignoreColumn column="version" delimitedColumnName="false"/>
           

實戰

如果需要深度定制一些代碼生成行為,建議引入

mybatis-generator-core

并且通過程式設計式執行代碼生成方法,否則可以選用

Maven

插件。假設我們在本地資料

local

有一張

t_order

表如下:

CREATE TABLE `t_order`
(
    id           BIGINT UNSIGNED PRIMARY KEY COMMENT '主鍵',
    order_id     VARCHAR(64)    NOT NULL COMMENT '訂單ID',
    create_time  DATETIME       NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '建立時間',
    amount       DECIMAL(10, 2) NOT NULL DEFAULT 0 COMMENT '金額',
    order_status TINYINT        NOT NULL DEFAULT 0 COMMENT '訂單狀态',
    UNIQUE uniq_order_id (`order_id`)
) COMMENT '訂單表';
           

假設項目的結構如下:

mbg-sample
  - main
   - java
    - club
     - throwable
   - resources
           

下面會基于此前提舉三個例子。編寫基礎的

XML

配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
    <!-- 驅動包絕對路徑 -->
    <classPathEntry
            location="I:\Develop\Maven-Repository\mysql\mysql-connector-java\5.1.48\mysql-connector-java-5.1.48.jar"/>

    <context id="default" targetRuntime="這裡選擇合适的引擎">

        <property name="javaFileEncoding" value="UTF-8"/>

        <!-- 不輸出注釋 -->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/local"
                        userId="root"
                        password="root">
        </jdbcConnection>


        <!-- 不強制把所有的數字類型轉化為BigDecimal -->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>

        <javaModelGenerator targetPackage="club.throwable.entity" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaModelGenerator>

        <sqlMapGenerator targetPackage="mappings" targetProject="src/main/resources">
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <javaClientGenerator type="這裡選擇合适的Mapper類型" targetPackage="club.throwable.dao" targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <table tableName="t_order"
               enableCountByExample="false"
               enableDeleteByExample="false"
               enableSelectByExample="false"
               enableUpdateByExample="false"
               domainObjectName="Order"
               mapperName="OrderMapper">
            <generatedKey column="id" sqlStatement="MySql"/>
        </table>
    </context>
</generatorConfiguration>
           

純注解

使用純注解需要引入

mybatis-dynamic-sql

<dependency>
    <groupId>org.mybatis.dynamic-sql</groupId>
    <artifactId>mybatis-dynamic-sql</artifactId>
    <version>1.1.4</version>
</dependency>
           

需要修改兩個位置:

<context id="default" targetRuntime="MyBatis3DynamicSql">
...

<javaClientGenerator type="ANNOTATEDMAPPER"
...
           

運作結果會生成三個類:

// club.throwable.entity
public class Order {
    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    private Long id;

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    private String orderId;

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    private Date createTime;

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    private BigDecimal amount;

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    private Byte orderStatus;

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public Long getId() {
        return id;
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public void setId(Long id) {
        this.id = id;
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public String getOrderId() {
        return orderId;
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public Date getCreateTime() {
        return createTime;
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public BigDecimal getAmount() {
        return amount;
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public Byte getOrderStatus() {
        return orderStatus;
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public void setOrderStatus(Byte orderStatus) {
        this.orderStatus = orderStatus;
    }
}

// club.throwable.dao
public final class OrderDynamicSqlSupport {
    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public static final Order order = new Order();

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public static final SqlColumn<Long> id = order.id;

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public static final SqlColumn<String> orderId = order.orderId;

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public static final SqlColumn<Date> createTime = order.createTime;

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public static final SqlColumn<BigDecimal> amount = order.amount;

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public static final SqlColumn<Byte> orderStatus = order.orderStatus;

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    public static final class Order extends SqlTable {
        public final SqlColumn<Long> id = column("id", JDBCType.BIGINT);

        public final SqlColumn<String> orderId = column("order_id", JDBCType.VARCHAR);

        public final SqlColumn<Date> createTime = column("create_time", JDBCType.TIMESTAMP);

        public final SqlColumn<BigDecimal> amount = column("amount", JDBCType.DECIMAL);

        public final SqlColumn<Byte> orderStatus = column("order_status", JDBCType.TINYINT);

        public Order() {
            super("t_order");
        }
    }
}

@Mapper
public interface OrderMapper {
    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    BasicColumn[] selectList = BasicColumn.columnList(id, orderId, createTime, amount, orderStatus);

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    @SelectProvider(type=SqlProviderAdapter.class, method="select")
    long count(SelectStatementProvider selectStatement);

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    @DeleteProvider(type=SqlProviderAdapter.class, method="delete")
    int delete(DeleteStatementProvider deleteStatement);

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    @InsertProvider(type=SqlProviderAdapter.class, method="insert")
    @SelectKey(statement="SELECT LAST_INSERT_ID()", keyProperty="record.id", before=true, resultType=Long.class)
    int insert(InsertStatementProvider<Order> insertStatement);

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    @SelectProvider(type=SqlProviderAdapter.class, method="select")
    @Results(id="OrderResult", value = {
        @Result(column="id", property="id", jdbcType=JdbcType.BIGINT, id=true),
        @Result(column="order_id", property="orderId", jdbcType=JdbcType.VARCHAR),
        @Result(column="create_time", property="createTime", jdbcType=JdbcType.TIMESTAMP),
        @Result(column="amount", property="amount", jdbcType=JdbcType.DECIMAL),
        @Result(column="order_status", property="orderStatus", jdbcType=JdbcType.TINYINT)
    })
    Optional<Order> selectOne(SelectStatementProvider selectStatement);

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    @SelectProvider(type=SqlProviderAdapter.class, method="select")
    @Results(id="OrderResult", value = {
        @Result(column="id", property="id", jdbcType=JdbcType.BIGINT, id=true),
        @Result(column="order_id", property="orderId", jdbcType=JdbcType.VARCHAR),
        @Result(column="create_time", property="createTime", jdbcType=JdbcType.TIMESTAMP),
        @Result(column="amount", property="amount", jdbcType=JdbcType.DECIMAL),
        @Result(column="order_status", property="orderStatus", jdbcType=JdbcType.TINYINT)
    })
    List<Order> selectMany(SelectStatementProvider selectStatement);

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    @UpdateProvider(type=SqlProviderAdapter.class, method="update")
    int update(UpdateStatementProvider updateStatement);

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default long count(CountDSLCompleter completer) {
        return MyBatis3Utils.countFrom(this::count, order, completer);
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default int delete(DeleteDSLCompleter completer) {
        return MyBatis3Utils.deleteFrom(this::delete, order, completer);
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default int deleteByPrimaryKey(Long id_) {
        return delete(c -> 
            c.where(id, isEqualTo(id_))
        );
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default int insert(Order record) {
        return MyBatis3Utils.insert(this::insert, record, order, c ->
            c.map(id).toProperty("id")
            .map(orderId).toProperty("orderId")
            .map(createTime).toProperty("createTime")
            .map(amount).toProperty("amount")
            .map(orderStatus).toProperty("orderStatus")
        );
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default int insertSelective(Order record) {
        return MyBatis3Utils.insert(this::insert, record, order, c ->
            c.map(id).toProperty("id")
            .map(orderId).toPropertyWhenPresent("orderId", record::getOrderId)
            .map(createTime).toPropertyWhenPresent("createTime", record::getCreateTime)
            .map(amount).toPropertyWhenPresent("amount", record::getAmount)
            .map(orderStatus).toPropertyWhenPresent("orderStatus", record::getOrderStatus)
        );
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default Optional<Order> selectOne(SelectDSLCompleter completer) {
        return MyBatis3Utils.selectOne(this::selectOne, selectList, order, completer);
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default List<Order> select(SelectDSLCompleter completer) {
        return MyBatis3Utils.selectList(this::selectMany, selectList, order, completer);
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default List<Order> selectDistinct(SelectDSLCompleter completer) {
        return MyBatis3Utils.selectDistinct(this::selectMany, selectList, order, completer);
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default Optional<Order> selectByPrimaryKey(Long id_) {
        return selectOne(c ->
            c.where(id, isEqualTo(id_))
        );
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default int update(UpdateDSLCompleter completer) {
        return MyBatis3Utils.update(this::update, order, completer);
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    static UpdateDSL<UpdateModel> updateAllColumns(Order record, UpdateDSL<UpdateModel> dsl) {
        return dsl.set(id).equalTo(record::getId)
                .set(orderId).equalTo(record::getOrderId)
                .set(createTime).equalTo(record::getCreateTime)
                .set(amount).equalTo(record::getAmount)
                .set(orderStatus).equalTo(record::getOrderStatus);
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    static UpdateDSL<UpdateModel> updateSelectiveColumns(Order record, UpdateDSL<UpdateModel> dsl) {
        return dsl.set(id).equalToWhenPresent(record::getId)
                .set(orderId).equalToWhenPresent(record::getOrderId)
                .set(createTime).equalToWhenPresent(record::getCreateTime)
                .set(amount).equalToWhenPresent(record::getAmount)
                .set(orderStatus).equalToWhenPresent(record::getOrderStatus);
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default int updateByPrimaryKey(Order record) {
        return update(c ->
            c.set(orderId).equalTo(record::getOrderId)
            .set(createTime).equalTo(record::getCreateTime)
            .set(amount).equalTo(record::getAmount)
            .set(orderStatus).equalTo(record::getOrderStatus)
            .where(id, isEqualTo(record::getId))
        );
    }

    @Generated("org.mybatis.generator.api.MyBatisGenerator")
    default int updateByPrimaryKeySelective(Order record) {
        return update(c ->
            c.set(orderId).equalToWhenPresent(record::getOrderId)
            .set(createTime).equalToWhenPresent(record::getCreateTime)
            .set(amount).equalToWhenPresent(record::getAmount)
            .set(orderStatus).equalToWhenPresent(record::getOrderStatus)
            .where(id, isEqualTo(record::getId))
        );
    }
}
           

極簡XML映射檔案

極簡

XML

映射檔案生成隻需要簡單修改配置檔案:

<context id="default" targetRuntime="MyBatis3Simple">
...

<javaClientGenerator type="XMLMAPPER"
...
           

生成三個檔案:

// club.throwable.entity
public class Order {
    private Long id;

    private String orderId;

    private Date createTime;

    private BigDecimal amount;

    private Byte orderStatus;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    public Byte getOrderStatus() {
        return orderStatus;
    }

    public void setOrderStatus(Byte orderStatus) {
        this.orderStatus = orderStatus;
    }
}

// club.throwable.dao
public interface OrderMapper {
    int deleteByPrimaryKey(Long id);

    int insert(Order record);

    Order selectByPrimaryKey(Long id);

    List<Order> selectAll();

    int updateByPrimaryKey(Order record);
}

// mappings
<?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="club.throwable.dao.OrderMapper">
    <resultMap id="BaseResultMap" type="club.throwable.entity.Order">
        <id column="id" jdbcType="BIGINT" property="id"/>
        <result column="order_id" jdbcType="VARCHAR" property="orderId"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="amount" jdbcType="DECIMAL" property="amount"/>
        <result column="order_status" jdbcType="TINYINT" property="orderStatus"/>
    </resultMap>
    <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
        delete
        from t_order
        where id = #{id,jdbcType=BIGINT}
    </delete>
    <insert id="insert" parameterType="club.throwable.entity.Order">
        <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Long">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into t_order (order_id, create_time, amount,
        order_status)
        values (#{orderId,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP}, #{amount,jdbcType=DECIMAL},
        #{orderStatus,jdbcType=TINYINT})
    </insert>
    <update id="updateByPrimaryKey" parameterType="club.throwable.entity.Order">
        update t_order
        set order_id     = #{orderId,jdbcType=VARCHAR},
            create_time  = #{createTime,jdbcType=TIMESTAMP},
            amount       = #{amount,jdbcType=DECIMAL},
            order_status = #{orderStatus,jdbcType=TINYINT}
        where id = #{id,jdbcType=BIGINT}
    </update>
    <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select id, order_id, create_time, amount, order_status
        from t_order
        where id = #{id,jdbcType=BIGINT}
    </select>
    <select id="selectAll" resultMap="BaseResultMap">
        select id, order_id, create_time, amount, order_status
        from t_order
    </select>
    <resultMap id="BaseResultMap" type="club.throwable.entity.Order">
        <id column="id" jdbcType="BIGINT" property="id"/>
        <result column="order_id" jdbcType="VARCHAR" property="orderId"/>
        <result column="create_time" jdbcType="TIMESTAMP" property="createTime"/>
        <result column="amount" jdbcType="DECIMAL" property="amount"/>
        <result column="order_status" jdbcType="TINYINT" property="orderStatus"/>
    </resultMap>
    <delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
        delete
        from t_order
        where id = #{id,jdbcType=BIGINT}
    </delete>
    <insert id="insert" parameterType="club.throwable.entity.Order">
        <selectKey keyProperty="id" order="BEFORE" resultType="java.lang.Long">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into t_order (id, order_id, create_time,
        amount, order_status)
        values (#{id,jdbcType=BIGINT}, #{orderId,jdbcType=VARCHAR}, #{createTime,jdbcType=TIMESTAMP},
        #{amount,jdbcType=DECIMAL}, #{orderStatus,jdbcType=TINYINT})
    </insert>
    <update id="updateByPrimaryKey" parameterType="club.throwable.entity.Order">
        update t_order
        set order_id     = #{orderId,jdbcType=VARCHAR},
            create_time  = #{createTime,jdbcType=TIMESTAMP},
            amount       = #{amount,jdbcType=DECIMAL},
            order_status = #{orderStatus,jdbcType=TINYINT}
        where id = #{id,jdbcType=BIGINT}
    </update>
    <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
        select id, order_id, create_time, amount, order_status
        from t_order
        where id = #{id,jdbcType=BIGINT}
    </select>
    <select id="selectAll" resultMap="BaseResultMap">
        select id, order_id, create_time, amount, order_status
        from t_order
    </select>
</mapper>
           

程式設計式自定義類型映射

筆者喜歡把所有的非長整型的數字,統一使用

Integer

接收,是以需要自定義類型映射。編寫映射器如下:

public class DefaultJavaTypeResolver extends JavaTypeResolverDefaultImpl {

    public DefaultJavaTypeResolver() {
        super();
        typeMap.put(Types.SMALLINT, new JdbcTypeInformation("SMALLINT",
                new FullyQualifiedJavaType(Integer.class.getName())));
        typeMap.put(Types.TINYINT, new JdbcTypeInformation("TINYINT",
                new FullyQualifiedJavaType(Integer.class.getName())));
    }
}
           

此時最好使用程式設計式運作代碼生成器,修改

XML

<javaTypeResolver type="club.throwable.mbg.DefaultJavaTypeResolver">
        <property name="forceBigDecimals" value="false"/>
</javaTypeResolver>
...
           

運作方法代碼如下:

public class Main {

    public static void main(String[] args) throws Exception {
        List<String> warnings = new ArrayList<>();
        // 如果已經存在生成過的檔案是否進行覆寫
        boolean overwrite = true;
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(Main.class.getResourceAsStream("/generator-configuration.xml"));
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator generator = new MyBatisGenerator(config, callback, warnings);
        generator.generate(null);
    }
}
           

資料庫的

order_status

TINYINT

類型,生成出來的檔案中的

orderStatus

字段全部替換使用

Integer

類型定義。

小結

本文相對詳盡地介紹了

Mybatis Generator

的使用方式,具體分析了

XML

配置檔案中主要标簽以及标簽屬性的功能。因為

Mybatis

Java

ORM

架構體系中還會有一段很長的時間處于主流地位,了解

Mybatis Generator

可以簡化

CRUD

方法模闆代碼、實體以及

Mapper

接口代碼生成,進而解放大量生産力。

Mybatis Generator

有不少第三方的擴充,例如

tk.mapper

mybatis-plus

自身的擴充,可能附加的功能不一樣,但是基本的使用是一緻的。

參考資料:

  • MyBatis Generator

原文連結

  • Github Page:http://throwable.club/2019/12/16/mybatis-generator-usage
  • Coding Page:http://throwable.coding.me/2019/12/16/mybatis-generator-usage

(本文完 c-5-d e-a-20191216 1:00)

技術公衆号(《Throwable》),不定期推送筆者原創技術文章(絕不抄襲或者轉載):

Mybatis代碼生成器Mybatis-Generator使用詳解