MyBatis GeneratorのExampleクラスなど解説
MyBatis Generatorを使うと、MyBatisを利用する時に必要な煩雑な作業(SQL記述、XML作成、Entityクラス作成など)を自動化してくれます。
このMyBatis Generatorですが、デフォルトで****Exampleというクラスまで作成されるようになっています。少し使ってみると、「ああ、and条件とかを表すためのクラスなのね」と分かるのですが、情報不足で詳しいことが分かりません。
勿論公式にExample Class Usage Notesという情報はありますが、英語だし・・・というわけで使い方を調査・解説します。
テーブル
まずは検証用に適当なテーブルとビューを作成します。
/* Create Tables */
-- 生産者
CREATE TABLE PRODUCER
(
PRODUCER_ID BIGINT UNSIGNED NOT NULL,
PRODUCER_NAME VARCHAR(30),
PRIMARY KEY (PRODUCER_ID)
) COMMENT = '生産者';
-- 製品
CREATE TABLE PRODUCT
(
PRODUCT_ID BIGINT UNSIGNED NOT NULL,
PRODUCT_NAME VARCHAR(30),
PRODUCER_ID BIGINT UNSIGNED NOT NULL,
PRIMARY KEY (PRODUCT_ID)
) COMMENT = '製品';
/* Create Foreign Keys */
ALTER TABLE PRODUCT
ADD FOREIGN KEY (PRODUCER_ID)
REFERENCES PRODUCER (PRODUCER_ID)
ON UPDATE RESTRICT
ON DELETE RESTRICT
;
/* Create Views */
-- 製品詳細
CREATE VIEW PRODUCT_DETAIL AS SELECT
PRODUCT.PRODUCT_ID,
PRODUCT.PRODUCT_NAME,
PRODUCT.PRODUCER_ID,
PRODUCER.PRODUCER_NAME
FROM PRODUCT, PRODUCER
WHERE PRODUCT.PRODUCER_ID=PRODUCER.PRODUCER_ID;
Generater起動
上記のテーブルに対してGeneratorを実行します。ここでできたMapperクラスとそのXMLを検証して行きます。
検証対象
Mapperを見るといくつかメソッドが用意されています。例えばProducerMapperは以下のようになっています。 これらを見ていくことにします。
- int countByExample(ProducerExample example)
- int deleteByExample(ProducerExample example)
- int deleteByPrimaryKey(Long producerId)
- int insert(Producer record)
- int insertSelective(Producer record)
- List selectByExample(ProducerExample example)
- Producer selectByPrimaryKey(Long producerId)
- int updateByExampleSelective(@Param(“record”) Producer record, @Param(“example”) ProducerExample example)
- int updateByExample(@Param(“record”) Producer record, @Param(“example”) ProducerExample example)
- int updateByPrimaryKeySelective(Producer record)
- int updateByPrimaryKey(Producer record)
ByPrimaryKey系
これは単純です。例えばdeleteByPrimaryKeyのXMLは以下のようになっています。
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long">
<!--
WARNING - @mbggenerated
This element is automatically generated by MyBatis Generator, do not modify.
This element was generated on Mon Sep 16 01:28:20 JST 2013.
-->
delete from producer
where PRODUCER_ID = #{producerId,jdbcType=BIGINT}
</delete>
単純に主キーを仮引数にもつ削除メソッドです。
ByExample系
問題のExampleです。まずは公式の情報を翻訳してみることにします。英語苦手ですが・・・。
The example class specifies how to build a dynamic where clause.
Exampleクラスは動的WHERE句を構築する方法を規定します。
The example classes contain an inner static class called Criteria that holds a list of conditions that will be anded together in the where clause.
Exampleクラスは、WHERE句にANDで結合される条件のリストを持つCriteriaという静的内部クラスを含みます。
なるほど、まあようするにExampleクラスはWHEREなわけです。では実際に使っていきます。説明の都合上、ここではDBのVIEWが元となっているProductDetailMapperのメソッドを検証します。あらかじめVIEW(DB)に以下のデータが投入されていることとします。
PRODUCT_ID | PRODUCT_NAME | PRODUCER_ID | PRODUCER_NAME |
100 | PRODUCT_1 | 0 | PRODUCER_A |
101 | PRODUCT_2 | 0 | PRODUCER_A |
102 | PRODUCT_3 | 0 | PRODUCER_A |
103 | PRODUCT_4 | 0 | PRODUCER_A |
104 | PRODUCT_5 | 2 | PRODUCER_C |
105 | PRODUCT_6 | 2 | PRODUCER_C |
106 | PRODUCT_7 | 2 | PRODUCER_C |
107 | PRODUCT_8 | 2 | PRODUCER_C |
108 | PRODUCT_9 | 3 | PRODUCER_D |
109 | PRODUCT_10 | 3 | PRODUCER_D |
110 | PRODUCT_11 | 4 | PRODUCER_E |
この状態で以下のコードを実行します。
public void run() {
ProductDetailExample example = new ProductDetailExample();
example.createCriteria().andProducerNameEqualTo("PRODUCER_A");
List<ProductDetail> list = mapper.selectByExample(example);
for (ProductDetail obj : list) {
System.out.println(obj);
}
}
実行結果は以下です。
ProductDetail [Hash = -1390806030, productId=100, productName=PRODUCT_1, producerId=0, producerName=PRODUCER_A, serialVersionUID=1]
ProductDetail [Hash = -1390775278, productId=101, productName=PRODUCT_2, producerId=0, producerName=PRODUCER_A, serialVersionUID=1]
ProductDetail [Hash = -1390744526, productId=102, productName=PRODUCT_3, producerId=0, producerName=PRODUCER_A, serialVersionUID=1]
ProductDetail [Hash = -1390713774, productId=103, productName=PRODUCT_4, producerId=0, producerName=PRODUCER_A, serialVersionUID=1]
なるほど、まずはPrimaryKeyでない条件で検索できました。AND条件1つです。
では次に検索条件をもう一つ追加してみます。
public void run() {
ProductDetailExample example = new ProductDetailExample();
example.createCriteria().andProducerNameEqualTo("PRODUCER_A").andProductNameEqualTo("PRODUCT_1");
List<ProductDetail> list = mapper.selectByExample(example);
for (ProductDetail obj : list) {
System.out.println(obj);
}
ProductDetail [Hash = -1390806030, productId=100, productName=PRODUCT_1, producerId=0, producerName=PRODUCER_A, serialVersionUID=1]
1レコードに絞れました。AND条件が2つです。
ORもできるようなので、試してみることにします。PRODUCER_AのPRODUCT_1とPRODUCER_CのPRODUCT_5の2レコードを抽出します。orメソッドを利用します。
public void run() {
ProductDetailExample example = new ProductDetailExample();
example.createCriteria().andProducerNameEqualTo("PRODUCER_A").andProductNameEqualTo("PRODUCT_1");
example.or().andProducerNameEqualTo("PRODUCER_C").andProductNameEqualTo("PRODUCT_5");
List<ProductDetail> list = mapper.selectByExample(example);
for (ProductDetail obj : list) {
System.out.println(obj);
}
}
ProductDetail [Hash = -1390806030, productId=100, productName=PRODUCT_1, producerId=0, producerName=PRODUCER_A, serialVersionUID=1]
ProductDetail [Hash = -1390682958, productId=104, productName=PRODUCT_5, producerId=2, producerName=PRODUCER_C, serialVersionUID=1]
抽出できました。
上記のコードではor()メソッドを利用しました。ここでもし、間違えてcreateCriteriaを実行した場合は?上記の引用通り、Exampleクラス内ではCriteriaをリストで持っていますが、生成されたCriteriaはこのリストに追加されないため、コードは意図通り動きません。
上記のコードではcreateCriteriaを利用しましたが、実は1つ目のcreateCriteriaはorでも正しく動作します。orでも内部ではCriteriaを生成します。常にorで良いかもしれません。
Selective系
insertとupdateにSelectiveがあります。これは一体何なんでしょうか。XMLを見ると分かりました。nullの要素のことを言っているようです。Selectiveではパラメータのクラス内のメンバ変数がnullである場合は、それについては関与しない、というもののようです。
例えばupdateしとうとしたときにメンバ変数がnullだとして、果たしてそれはDB上でNULLにしたいのか、それともDB上の値には関与してほしくないのか・・・このSelectiveはDB上の値はNULLにしないということでしょう。逆にSelectiveでない方のメソッドではメンバ変数がnullの場合はDB上の値もNULLにするということでしょう。
実際にコードを実行して確かめてみます。
ここで分かりやすさのために、PRODUCERテーブルのPRODUCER_NAME列のデフォルト値’NONAME’を設けておきます。
まずは以下のコードを実行します。Selectiveでないバージョン。
public void run() {
Producer record = new Producer();
record.setProducerId(900L);
record.setProducerName(null);
producerMapper.insert(record);
}
PRODUCER_ID | PRODUCER_NAME |
0 | PRODUCER_A |
1 | PRODUCER_B |
2 | PRODUCER_C |
3 | PRODUCER_D |
4 | PRODUCER_E |
900 | (NULL) |
PRODUCER_NAME列がNULLということはDEFAULTは発動していません。これは意図通りと言えるかと思います。SelectiveしないinsertでNULLの値を意図的に入れたわけですから。
では次はSelectiveバージョンを試してみます。
public void run() {
Producer record = new Producer();
record.setProducerId(901L);
record.setProducerName(null);
producerMapper.insertSelective(record);
}
PRODUCER_ID | PRODUCER_NAME |
0 | PRODUCER_A |
1 | PRODUCER_B |
2 | PRODUCER_C |
3 | PRODUCER_D |
4 | PRODUCER_E |
900 | (NULL) |
901 | NONAME |
こちらはDEFAULTが発動してNONAMEになりました。
以上で調査と解説は終了です。