【MyBatis】spring bootでDB操作をできるだけ速く簡単に作成する【自動生成】
はじめに
spring boot
+ MyBatis
でDB
操作をするまでを、できる限り速く、できるだけ簡単に、難しい設定もなく作りたかったので、いろいろ調べた結果を記事にしました。
- 単純な
select
やinsert
をするだけで、いろいろ手動で作りたくない。 - カラム数が多いテーブルと、
Model
クラスをマッピングするだけで大変。 - この作業は、プログラムとしても単調でつまらなくて、虚無感が半端ない
- 必要なファイルとかを自動生成するようなツールを
Excel
で作ってた現場あったけど、しばらくたって後任(俺)が新規テーブルを作るかーって時に動かなくなってた。投げ捨てたくなった。
ってな感じで、すごく面倒な作業をやめたかったので自動生成できるようにしたかった。
目標
- 作成済みのテーブルから
Model
クラス、Mapper
インターフェース、xml
ファイルを自動生成。 -
DB
の命名規則はsnake_case
、Javaの命名規則はcamelCase
で作成されることを想定 - 自動生成したファイルでのDB操作の動作確認
⇒ 自動生成分はselect , update , delete, insert の基本操作のみ想定 - 手動で作成する分も自動生成したファイルを流用して、楽に作る
前提条件、実行環境とか
- いろいろ設定とかあるが、できるだけシンプルでわかりやすい構成しているので、詳細はMyBatis Generator参照してください。
-
SQL
はすべてXML
ファイルで定義するように作ってます。 - IDEは、STSを使ってます。
- DB構築されていること前提にしています。今回の記事では、
mySQL
を利用しています。
参考:Dockerでmysql構築 - 下記のようなテーブルを対象に作成しています。
自動生成対象のテーブルとデータ作成
CREATE TABLE tb_sample (
id int NOT NULL AUTO_INCREMENT
, product_name varchar (20)
, category_id varchar (8)
, PRIMARY KEY (id)
);
insert into tb_sample values(1, 'hogeA', 'A');
insert into tb_sample values(2,'hogeB', 'B');
insert into tb_sample values(3,'hogeC', 'C');
insert into tb_sample values(4,'fooA', 'A');
insert into tb_sample values(5,'fooB', 'B');
insert into tb_sample values(6,'sampleA', 'A');
insert into tb_sample values(7,'sampleB', 'C');
事前準備
MyBatis Generator インストール
[ヘルプ]⇒[eclipse マーケットプレイス]からMyBatis Generator
をインストール
spring boot プロジェクト作成
まだ、プロジェクトを用意していない人はここから作成
今回実施するのでとりあえず、必要最低限の↓のような感じで作成
依存関係はこんな感じ。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
runtimeOnly 'mysql:mysql-connector-java'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
自動生成
Generatorファイル作成
任意の場所にmybatis-generator-config.xml
を作成します。
(公式サイトのExample
から自動設定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>
<context id="demoTables" targetRuntime="MyBatis3">
<!-- DB接続設定 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost/hogedb"
userId="hoge"
password="passw0rd">
<property name="nullCatalogMeansCurrent" value="true" />
</jdbcConnection>
<!-- Select結果がマッピングされる、Javaモデルの出力設定 -->
<javaModelGenerator targetPackage="com.example.demo.db.model.generated" targetProject="demo/src/main/java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- SQLを定義したXMLファイルの出力設定 -->
<sqlMapGenerator targetPackage="com.example.demo.db.mapper.generated" targetProject="demo/src/main/resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- Mapperインターフェースの出力設定 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.example.demo.db.mapper.generated" targetProject="demo/src/main/java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 自動生成するテーブルの設定 -->
<table tableName="%"
enableInsert="true"
enableSelectByPrimaryKey="true"
enableUpdateByPrimaryKey="true"
enableDeleteByPrimaryKey="true"
enableSelectByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableCountByExample="false"
selectByPrimaryKeyQueryId="false"
selectByExampleQueryId="false"
modelType="flat">
</table>
</context>
</generatorConfiguration>
DB接続設定
jdbcConnection
にDB
接続情報を設定します。
【MySQLのみ】
nullCatalogMeansCurrent
のプロパティを設定しています。
ジェネレータが MySql 情報スキーマ(sys、information_schema、performance_schema など)のテーブルまで作ろうとしてしまうので、設定しときます。
詳細は:https://mybatis.org/generator/usage/mysql.html
モデル出力設定
javaModelGenerator
に、DB
からselect
した結果をマッピングするモデルクラスの出力設定をします。
SQL のXML定義ファイル出力設定
sqlMapGenerator
に、実行SQL
が定義してある、xml
ファイルの出力設定をします。
Mapperインターフェースの出力設定
javaClientGenerator
に、Mapper
インターフェースの出力設定をします。
すべてのSQL
はxml
ファイル経由で実行するように設定してます(type="XMLMAPPER"
)
sqlMapGenerator
のtargetPackage
とjavaClientGenerator
のtargetPackage
は、同じパッケージを合わせとくと、何も設定しないでも自動でxml
ファイルを読み込んでくれます。
(合わせない場合は、mybatis.mapper-locations
をapplication.properties
で設定する)
自動生成対象のテーブル設定
table
に、出力対象のテーブル設定と、メソッドの種類とかを設定します。
-
tableName="%"
ですべてのテーブルを対象にしています
⇒ すべてだと*(アスタリスク)かなと思って設定してたら少し嵌った… -
modelType="flat"
が一番シンプルに出力されます。(テーブルとモデルが1:1になる) -
enable~
,select~
で自動生成するメソッドの種類を指定しています
他にも自動生成でやれそうだが、複雑になる場合は要件毎に対応すべきかなと思うので、このぐらいで十分だと思っている。
注意点
-
実行環境が
MySQL
で確認しているが、少し特殊かもしれないです。
というのも、mySQL
はカタログとスキーマを適切にサポートしていないかららしい。なので、他の種類のDB
で試してみるときは、以下のような点を確認してみるといいかも…?(mysql
以外未確認)-
table
タグにschema
属性を指定してみる -
enableSubPackages
プロパティの要/不要
-
実行
対象のプロジェクト右クリック⇒[実行構成]⇒[MyBatis Generator]の新規作成で、上で作ったxmlファイルを指定して実行する。
自動作成ファイル
下記のような感じでファイルが出力されました。
また、作成されたMapperインターフェースのメソッドは
- deleteByPrimaryKey
- insert
- insertSelective
- selectByPrimaryKey
- updateByPrimaryKeySelective
- updateByPrimaryKey
が作られてました。
Selective
のありなしがあるメソッドは、モデルに設定された値がNULL
の場合にNULL
で更新するか、更新対象外のカラムにするかの違いです。
MySQL
だとtext
型などのカラムがあるテーブルは、~WithBLOBs
というメソッドも作成される。ここら辺が、唐突に作られるとビビる。テーブルのカラム型のせいだってのを知っとくとビビらない。
個人的には、桁数はしっかりと決めて定義するほうなのでここら辺はあまり気にしない事にした。
動作確認
自動生成したファイルを、動かしてみる。
Mapper
インターフェースの読み込み設定
自動生成した@MapperScan
アノテーションで自動生成したmapper
インターフェースの出力先パッケージを設定する。
Mapper
インターフェース自体に@Mapper
アノテーションを付ける方法もあるが、これだと自動生成のたびに毎回やる必要があるのでNG
package com.example.demo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.example.demo.db.mapper")
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
applicationでのDB設定
application.properties
にDB
の接続設定など
pring.datasource.url=jdbc:mysql://localhost/hogedb
spring.datasource.username=hoge
spring.datasource.password=passw0rd
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.map-underscore-to-camel-case
は、DB側の命名規則snake_case
とJava側の命名規則camelCase
とのマッピング用です。
画面表示で確認
動確用なので、Controller
で直接実行させちゃいます
package com.example.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.demo.db.mapper.generated.TbSampleMapper;
import com.example.demo.db.model.generated.TbSample;
@Controller
public class SampleController {
@Autowired
TbSampleMapper mapper;
@GetMapping("/sample/select/{id}")
@ResponseBody
public String select(@PathVariable("id") Integer id) {
TbSample sample = mapper.selectByPrimaryKey(id);
return "product Name = " + sample.getProductName();
}
@GetMapping("/sample/update/{id}/{name}")
@ResponseBody
public String update(@PathVariable("id") Integer id, @PathVariable("name") String name) {
TbSample sample = new TbSample();
sample.setId(id);
sample.setProductName(name);
mapper.updateByPrimaryKeySelective(sample);
return "Update!";
}
@GetMapping("/sample/insert/{name}/{category}")
@ResponseBody
public String insert(@PathVariable("name") String name, @PathVariable("category") String category) {
TbSample sample = new TbSample();
sample.setProductName(name);
sample.setCategoryId(category);
mapper.insert(sample);
return "Insert!";
}
}
ちゃんと実行できているの確認
手動作成分の追加
mapper
ファイル作成
com.example.demo.db.mapper.custom
のパッケージに手動作成分のファイルを作っていきます。
作成対象としては、category_id
カラムの値でselect
です。自動生成でも作れますが、とりあえず単純なのということで…
<?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.example.demo.db.mapper.custom.TbSampleCustomMapper">
<select id="selectByCategory" parameterType="java.lang.String" resultMap="com.example.demo.db.mapper.generated.TbSampleMapper.BaseResultMap">
select
<include refid="com.example.demo.db.mapper.generated.TbSampleMapper.Base_Column_List" />
from tb_sample
where category_id = #{category,jdbcType=VARCHAR}
</select>
</mapper>
package com.example.demo.db.mapper.custom;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.example.demo.db.model.generated.TbSample;
@Mapper
public interface TbSampleCustomMapper {
public List<TbSample> selectByCategory(String category);
}
ポイントはxml
でcom.example.demo.db.mapper.generated.TbSampleMapper.Base_Column_List
で、自動生成の方で定義したものを利用していること。
同じmodel
クラスにマッピングしたくても、カラム数が多いテーブルだとそれだけで大変だし…これなら、テーブルにカラムが追加されても自動で反映してくれる。
実行確認
自動作成の実行確認の時のController
に下記を追加
@Controller
public class SampleController {
// 省略
@Autowired
TbSampleCustomMapper mapperCustom;
// 省略
@GetMapping("/sample/category/{category}")
@ResponseBody
public String selectCategory(@PathVariable("category") String category) {
List<TbSample> samples = mapperCustom.selectByCategory(category);
StringBuilder s = new StringBuilder();
samples.forEach(sample -> {
s.append("[ID=" + sample.getId() + ", PRODUCT_NAME=" + sample.getProductName() + "]<br>");
});
return s.toString();
}
}
さいごに
自動生成したファイルを手で修正するのはNGってのは鉄則かなと思う。自動生成するたびに上書きされてしまうので。
出来る限り、自動で記述量を少なめにしてみたが…どうだったかな?
あまり本質と違う部分で虚無的プログラミングをしたくないので、まとめてみました。
Discussion