天天看點

mybatis 自動生成代碼(mybatis generator)

pom.xml 檔案配置

引入

mybatis generator

<properties>
  <mysql.connector.version>5.1.44</mysql.connector.version>
  <mybatis.generator.version>1.3.5</mybatis.generator.version>
  <mybatis.spring.version>1.3.1</mybatis.spring.version>
  <mybatis.version>3.4.4</mybatis.version>
</properties>

<build>
  <plugins>
   <plugin>
     <groupId>org.mybatis.generator</groupId>
     <artifactId>mybatis-generator-maven-plugin</artifactId>
     <version>${mybatis.generator.version}</version>
     <configuration>
       <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
       <verbose>true</verbose>
       <overwrite>true</overwrite>
     </configuration>
     <dependencies>
       <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <version>${mysql.connector.version}</version>
       </dependency>
       <dependency>
         <groupId>org.mybatis.generator</groupId>
         <artifactId>mybatis-generator-core</artifactId>
         <version>${mybatis.generator.version}</version>
       </dependency>
       <dependency>
         <groupId>com.github.oceanc</groupId>
         <artifactId>mybatis3-generator-plugin</artifactId>
         <version>0.4.0</version>
       </dependency>
     </dependencies>
   </plugin>
  </plugins>
 <resources>
   <resource>
     <!--src/main/java 下的 xml 檔案打包時也加入-->
     <directory>src/main/java</directory>
     <includes>
       <include>**/*.xml</include>
     </includes>
   </resource>
   <resource>
     <directory>src/main/resources</directory>
     <filtering>true</filtering>
     <includes>
       <include>**/*.*</include>
     </includes>
     <excludes>
       <!--<exclude>test/**/*.*</exclude> -->
     </excludes>
   </resource>
 </resources>
</build>           
  1. <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>

    自動生成代碼的核心配置檔案

    generatorConfig.xml

    的路徑
  2. mysql-connector-java

    生成哪種資料庫的代碼,不可省略
  3. com.github.oceanc

    引入第三方的

    jar

    ,能夠生成常用的查詢文法
  4. resources

    标簽配置是為了将

    mybatis 文法 xml 檔案

    打包進

    war

    包,缺少

    xml

    檔案代碼是無法執行的
  5. org.mybatis.generator

    自動生成可執行代碼的核心

    jar

    ,不可缺少

org.mybatis.generator 自帶生成代碼插件

  • org.mybatis.generator.plugins.CachePlugin

    二級緩存相關,需要更深入了解一下

  • org.mybatis.generator.plugins.CaseInsensitiveLikePlugin

    對字元串比對生成大小寫敏感的方法

  • org.mybatis.generator.plugins.EqualsHashCodePlugin

    重寫

    model

    equals

    hashCode

    方法,這對比兩個對象的值是否相等很在意義。這個重寫可以通過

    Intellj

    自帶的幫助方法解決,

    ALT+Insert

    -->

    equals() and hashCode()

    。各處統一使用相同的生成方法即可
  • org.mybatis.generator.plugins.FluentBuilderMethodsPlugin

    生成

    withXxx()

    方法,可以簡潔的指派

    new MyDomain().withFoo("Test").withBar(4711);

    。這個功能可以通過

    Intellj

    的插件

    InnerBuilder

    實作,使用

    Builder

    模式。
  • org.mybatis.generator.plugins.MapperConfigPlugin

    MyBatis 3.x

    架構使用的配置檔案。感覺沒有什麼用處,生成的配置檔案也沒有生成什麼有用的東西
  • org.mybatis.generator.plugins.RenameExampleClassPlugin

    mybatis

    預設生成的查詢類是以

    Example

    結尾,往往都使用如下配置改成

    Criteria

    結尾
<plugin type="org.mybatis.generator.plugins.RenameExampleClassPlugin">
    <property name="searchString" value="Example$" />
    <property name="replaceString" value="Criteria" />
</plugin>           
  • org.mybatis.generator.plugins.RowBoundsPlugin

    分頁

  • org.mybatis.generator.plugins.SerializablePlugin

    繼承序列化

  • org.mybatis.generator.plugins.SqlMapConfigPlugin

    iBATIS 2.x

<plugin type = "org.mybatis.generator.plugins.SqlMapConfigPlugin">
    <property name="fileName" value="test.xml"/>
    <property name="targetPackage" value="com.nd.mybatis"/>
    <property name="targetProject" value="src/main/java"/>
</plugin>           
  • org.mybatis.generator.plugins.ToStringPlugin

    model

    toString

    方法。這個可以通過

    Intellj

    完成,

    ALT+insert

    toString()

  • org.mybatis.generator.plugins.VirtualPrimaryKeyPlugin

    如果表沒有主鍵,

    mybatis

    有部分方法不會生成,配置幾個虛拟的主鍵,即使在資料庫中并不是主鍵也可以配置。配置方案
<table tableName="foo">
    <property name="virtualKeyColumns" value="ID1, ID2" />
</table>           

com.github.oceanc 支援生成代碼插件

受sql dialect的限制,多數plugin目前僅支援 Mysql 5.x

Download

在 Maven Central 上最新的釋出版本是:

<dependency>
    <groupId>com.github.oceanc</groupId>
    <artifactId>mybatis3-generator-plugin</artifactId>
    <version>0.4.0</version>
</dependency>           

mybatis3-generator-plugins目前提供了如下可用插件:

  • com.github.oceanc.mybatis3.generator.plugin.BatchInsertPlugin
  • com.github.oceanc.mybatis3.generator.plugin.JacksonAnnotationPlugin
  • com.github.oceanc.mybatis3.generator.plugin.JacksonToJsonPlugin
  • com.github.oceanc.mybatis3.generator.plugin.LombokAnnotationPlugin
  • com.github.oceanc.mybatis3.generator.plugin.MinMaxPlugin
  • com.github.oceanc.mybatis3.generator.plugin.OptimisticLockAutoIncreasePlugin
  • com.github.oceanc.mybatis3.generator.plugin.PaginationPlugin
  • com.github.oceanc.mybatis3.generator.plugin.SliceTablePlugin
  • com.github.oceanc.mybatis3.generator.plugin.SumSelectivePlugin
  • com.github.oceanc.mybatis3.generator.plugin.UpdateSqlTextOfUpdateSelectivePlugin
  • com.github.oceanc.mybatis3.generator.plugin.WhereSqlTextPlugin

使用

MyBatis GeneratorXML Configuration File

中添加你需要用到的

<plugin>

元素:

<context id="MysqlTables" targetRuntime="MyBatis3">
  <plugin type = "com.github.oceanc.mybatis3.generator.plugin.BatchInsertPlugin" />
  <plugin type = "com.github.oceanc.mybatis3.generator.plugin.JacksonAnnotationPlugin" />
  <plugin type = "com.github.oceanc.mybatis3.generator.plugin.JacksonToJsonPlugin" />
  <plugin type = "com.github.oceanc.mybatis3.generator.plugin.LombokAnnotationPlugin" />
  <plugin type = "com.github.oceanc.mybatis3.generator.plugin.MinMaxPlugin" />
  <plugin type = "com.github.oceanc.mybatis3.generator.plugin.OptimisticLockAutoIncreasePlugin" />
  <plugin type = "com.github.oceanc.mybatis3.generator.plugin.PaginationPlugin" />
  <plugin type = "com.github.oceanc.mybatis3.generator.plugin.SliceTablePlugin" />
  <plugin type = "com.github.oceanc.mybatis3.generator.plugin.SumSelectivePlugin" />
  <plugin type = "com.github.oceanc.mybatis3.generator.plugin.UpdateSqlTextOfUpdateSelectivePlugin" />
  <plugin type = "com.github.oceanc.mybatis3.generator.plugin.WhereSqlTextPlugin" />
</context>           

為了舉例,假設我們建立一張簡單的賬戶表,并命名為

Account

。DDL如下:

CREATE TABLE `Account` (
  `id`            bigint(16)    NOT NULL,
  `create_time`   timestamp     NOT NULL,
  `name`          varchar(64)   DEFAULT NULL,
  `age`           tinyint(3)    DEFAULT NULL,
  `version`       int(11)       NOT NULL,
  PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;           

如果使用了

SliceTablePlugin

,為了其産生的作用始終有效,推薦将其放置在自定義

<plugin>

聲明的第一條,如上例所示。這麼做的原因是,其他

plugin(BatchInsertPlugin、MinMaxPlugin、和SumSelectivePlugin)

會通過檢測

SliceTablePlugin

是否應用,來适配其效果。

SliceTablePlugin

當資料庫單表資料量過大時,可以通過水準拆分把單表分解為多表。例如,若

Account

表中的預期資料量過大時(也許5、6億),可以把一張

Account

表分解為多張

Account

表。SliceTablePlugin并不會自動建立和執行拆分後的DDL,是以必須手工建立DDL并按下述約定修改表名。

SliceTablePlugin

目前支援兩種分表命名方式:

  • 對指定列取模。拆分後的表名為

    原表名_N

    。如:Account_0、Account_1、Account_3 ......
  • 對指定的時間類型列,按單自然月拆分。拆分後的表名為

    原表名_yyyyMM

    。如:Account_201501、Account_201502 ...... Account_201512

Xml config

  1. 利用取模

    假如通過

    Account

    表的

    id

    字段做分表,計劃拆分為

    97

    張表。在

    <table>

    元素中添加兩個

    <property>

<table tableName="Account_0" domainObjectName="Account">
    <property name="sliceMod" value="97"/>
    <property name="sliceColumn" value="id"/>
</table>           
  • sliceMod

    指按取模方式分表,

    97

    是取模的模數
  • sliceColumn

    指取模所使用的列,

    id

    是具體列名
  1. 利用自然月

    Account

    create_time

    字段做拆分:
<table tableName="Account_0" domainObjectName="Account">
    <property name="sliceMonth" value="1"/>
    <property name="sliceColumn" value="create_time"/>
</table>           
  • sliceMonth

    指按自然月分表,

    1

    指按單個自然月。Note:目前隻支援按單月分表,此處的值 1 無實際意義
  • sliceColumn

    指時間類型的列,

    create_time

Java sample

  • insert
AccountMapper mapper = ...
Account record = new Account();
record.setAge(33);
record.setId(101);
record.setCreateTime(new Date());
mapper.insert(record);
// or mapper.insertSelective(record)           
  • 通過取模分表時,必須調用

    setId

    并傳入合适的參數
  • 通過自然月分表時,必須調用

    setCreateTime

  • read
AccountMapper mapper = ...
AccountExample example = new AccountExample();
example.partitionFactorId(id).createCriteria().andAgeEqualTo(33);
// or example.partitionFactorCreateTime(new Date()).createCriteria().andAgeEqualTo(33);
List<Account> as = mapper.selectByExample(example);           
  • 通過取模分表時,

    partitionFactorId

    方法表示分表因子是

    Id

    字段,必須調用該方法并傳入合适的參數
  • 通過自然月分表時,

    partitionFactorCreateTime

    createTime

  • update
AccountMapper mapper = ...
Account record = new Account();
record.setAge(33);
AccountExample example = new AccountExample();
example.partitionFactorId(id).createCriteria().andAgeEqualTo(33);
// or example.partitionFactorCreateTime(new Date()).createCriteria().andAgeEqualTo(33);
mapper.updateByExampleSelective(record, example);           

上例的用法和read操作一樣,在example對象上必須調用

partitionFactorId

partitionFactorCreateTime

方法。

除此之外,還可以用如下方式進行update:

AccountMapper mapper = ...
Account record = new Account();
record.setCreateTime(new Date());
record.setId(101);
// or record.setCreateTime(new Date());
AccountExample example = new AccountExample();
example.createCriteria().andAgeEqualTo(33);
mapper.updateByExampleSelective(record, example);           

由于在record對象調用了

setId

setCreateTime

,就無須在example對象指定分表因子。

  • delete
AccountMapper mapper = ...
AccountExample example = new AccountExample();
example.partitionFactorId(id).createCriteria().andAgeEqualTo(33);
// or example.partitionFactorCreateTime(new Date()).createCriteria().andVersionEqualTo(0);
mapper.deleteByExample(example);           
  • partitionFactorId

    方法表示分表因子是Id字段,必須調用該方法并傳入合适的參數
  • partitionFactorCreateTime

    方法表示分表因子是createTime字段,必須調用該方法并傳入合适的參數
  • other

    當無法獲得分表因子的值時、或者确定所操作的表名時,可以通過:

  • record.setTableNameSuffix(...)

    取代

    record.setId(...)

    record.setId(...)

  • example.setTableNameSuffix(...)

    example.partitionFactorId(...)

WARNING:由于

setTableNameSuffix

的參數是

String

類型,在

Mybatis3

mapper xml

中生成

${}

變量,這種變量不會做

sql

轉義,而直接嵌入到

sql

語句中。如果以使用者輸入作為

setTableNameSuffix

的參數,會導緻潛在的

SQL Injection

攻擊,需謹慎使用。

BatchInsertPlugin

plugin

是為了增加批量

insert

的功能。特别适用于初始化資料、或遷移資料。

AccountMapper mapper = ...
List<Account> as = new ArrayList<>();
as.add(...)
mapper.batchInsert(as);           

SliceTablePlugin

,則需要對

List

中每一個

Account

執行個體設定分表因子:

AccountMapper mapper = ...
List<Account> as = new ArrayList<>();
for (Account account : accounts) {
    account.setId(...);
    // or account.setCreateTime(...);
}
mapper.batchInsert(as);           
  • setId

  • setCreateTime

JacksonAnnotationPlugin

plugin

是為生成的

model

類添加

jackson

@JsonProperty

@JsonIgnore

、 和

@JsonFormat

注解。若使用此插件,需要額外依賴

jackson 2.5 +

。(這個插件沒有什麼必要啊,手工添加更加友善)

<table>

元素中添加四個

<property>

(可選的):

<table tableName="Account_0" domainObjectName="Account">
    <property name="jacksonColumns" value="name,age"/>
    <property name="jacksonProperties" value="nickName,realAge"/>
    <property name="jacksonFormats" value="create_time@yyyy-MM-dd HH:mm:ss"/>
    <property name="jacksonIgnores" value="id,version"/>
</table>           
  • jacksonColumns

    指需要添加

    @JsonProperty

    注解的列,由

    ,

    分割的列名組成。該

    <property>

    必須和

    jacksonProperties

    成對出現
  • jacksonProperties

    @JsonProperty

    注解所需的參數值,由

    ,

    分割,這些值的數量和順序必須和

    jacksonColumns

    的值一一對應。該

    <property>

    jacksonColumns

  • jacksonFormats

    @JsonFormat

    注解的列,值由

    ,

    分割的鍵值對組成,鍵值對由

    @

    分割,鍵為列名,值為

    @JsonFormat

    所需參數
  • jacksonIgnores

    @JsonIgnore

    ,

    分割的列名組成

public class Account implements Serializable {
    @JsonIgnore
    private Long id;
    @JsonProperty("nickName")
    private String name;
    @JsonProperty("realAge")
    private Integer age;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    @JsonIgnore
    private Long version;
}           

JacksonToJsonPlugin

plugin

model

toJson

toJson

方法的實作依賴于

。若使用此插件,需要額外依賴

jackson 2.5 +

。(這個插件沒有必要使用,并且會有性能的問題,因為生成的

toJson

方法每次被調用都重新

new ObjectMapper

ObjectMapper

是線程安全的,完全可以做一個全局的變量)

public class Account implements Serializable {
    public String toJson() throws IOException {
        ... ...
    }
}           

LombokAnnotationPlugin

plugin

model

lombok

注解,避免了

java bean

中繁瑣的

setter

getter

。生成的代碼看起來也更幹淨、緊湊。若使用此插件,需要額外依賴

lombok

。(這個插件可用,可不用,因為手工添加幾個注解也是簡單的)

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.16.8</version>
</dependency>           

@Data
public class Account implements Serializable {
    private Long id;
    ... ...
}           

MinMaxPlugin

<table>

<property>

(可選的)。(這個支援不好,生成的方法名稱不友好,以逗号分隔沒有起到作用)

<table tableName="Account_0" domainObjectName="Account">
    <property name="minColumns" value="id,version"/>
    <property name="maxColumns" value="id,version"/>
</table>           
  • minColumns

    是可進行min操作的列,值由

    ,

  • maxColumns

    是可進行max操作的列,值由

    ,

AccountMapper mapper = ...
AccountExample example = new AccountExample();
example.createCriteria().andAgeEqualTo(33);
long min = mapper.minIdByExample(example);
long max = mapper.maxIdByExample(example);           

SliceTablePlugin

,别忘了對分表因子指派:

example.partitionFactorCreateTime(...)

OptimisticLockAutoIncreasePlugin

在處理并發寫操作時,如果資料競争較低,通常會采用樂觀鎖,避免并發問題的同時獲得較好的性能。實作樂觀鎖的一般方式是在資料中添加版本資訊,就像

Account

表中的

version

列。當寫操作成功時,版本資訊也相應遞增。該

plugin

就是解決

version

自動遞增問題的,可以避免手動對

version+1

<table>

元素中添加一個

<property>

<table tableName="Account_0" domainObjectName="Account">
    <property name="optimisticLockColumn" value="version"/>
</table>           
  • optimisticLockColumn

    指具有版本資訊語義的列,

    version

    是具體的列名

AccountMapper mapper = ...
Account record = new Account();
record.setName("tom");
// record.setVersion(1) 無須手工對version進行指派
AccountExample example = new AccountExample();
example.createCriteria().andAgeEqualTo(33);
mapper.updateByExampleSelective(record, example);           

SliceTablePlugin

example.partitionFactorId(...)

PaginationPlugin

這個與

org.mybatis.generator.plugins.RowBoundsPlugin

的功能是類似的

AccountMapper mapper = ...
AccountExample example = new AccountExample();
example.page(0, 2).createCriteria().andAgeEqualTo(33);
List<Account> as = mapper.selectByExample(example);           

SliceTablePlugin

example.partitionFactorCreateTime(...)

SumSelectivePlugin

AccountMapper mapper = ...
AccountExample example = new AccountExample();
example.sumAge().createCriteria().andVersionEqualTo(0);
long sum = mapper.sumByExample(example);           

SliceTablePlugin

example.partitionFactorCreateTime(...)

UpdateSqlTextOfUpdateSelectivePlugin

AccountMapper mapper = ...
Account record = new Account();
record.setUpdateSql("version = version + 1")
record.setAge(33);
AccountExample example = new AccountExample();
example.createCriteria().andAgeEqualTo(22);
mapper.updateByExampleSelective(record.setAge, example);           

SliceTablePlugin

example.partitionFactorCreateTime(...)

setUpdateSql

String

Mybatis3

mapper xml

${}

sql

sql

setUpdateSql

SQL Injection

WhereSqlTextPlugin

AccountMapper mapper = ...
int v = 1;
AccountExample example = new AccountExample();
example.createCriteria().andAgeEqualTo(33).addConditionSql("version =" + v + " + 1");
List<Account> as = mapper.selectByExample(example);           

SliceTablePlugin

example.partitionFactorCreateTime(...)

setUpdateSql

String

Mybatis3

mapper xml

${}

sql

sql

setUpdateSql

SQL Injection

本篇部落客要來自:

https://github.com/oceanc/mybatis3-generator-plugins

,其他部分内容來自官網:

http://www.mybatis.org/generator/reference/plugins.html