いろいろと思うところがあり、Spring JDBC ベースのコード生成ツールを自作しました。
なぜ今更このようなものを作ったのか、個人的な好みと、カラムデフォルト値問題、更新日時・作成日時問題(次回)から記載します。
個人的な好み
Java からDB アクセスを行う仕組みは歴史がある枯れた領域です。
無料で使えるライブラリが多数あるのですが、個人的にはどれも一長一短だと感じています。
私が使ったことのあるライブラリについて独断と偏見を列挙します。
多分私はマイノリティなので、反論は当然あると思います。
JOOQ
始めは好印象だった。Java でSQL の全てを書くことができる。ORマッパーとしては最強だと今も思っている。PostgreSQL、MySQL は無償版でも使えるが、Oracle は有償版でないと使えない点は気になる。今後料金体系が変わったりしたら一大事かも。ORマッパーなのでJava のコードからSQL を推測するスキルが必要。私は自分で書いたクエリーを時間が経ってから読んだら意味が分からなかった。
JPA
こちらも始めは使いやすいと思っていた。しかし、一次キャッシュにより更新されない問題を踏んだ時に、二度と使わないと思えるほど嫌になった。私の頭では一次キャッシュを使いこなせない。ライブラリの容量が大きいので起動が遅くなる、Boot jar のサイズが大きくなる、といった問題もある。
MyBatis
XML にSQL を書くのが嫌。CDATA がたくさん出てきて読みづらくなる。if foreach OGNL を覚えるのが面倒。Java ならすんなり書けるのに使い方を調べながら書くのがストレス。実質業界標準なので業務で使うことは多い。
Spring JDBC
機能が薄くてシンプル。SQL をJava で書くのでMyBatis のように独自の文法を覚える必要がない。欠点は簡単なCRUD を書くだけでも、Entity とRepository を手で作成する必要がありそこそこ手間がかかる。Java の文字列結合でSQL を書くとセキュリティ上の問題が起きると信じている人がいる気がする。
DBFlute
SQL を書いたらEntity を作ってくれるという、他にはない思想が良い。開発速度向上につながる。Generator が吐き出すjava コードをEclipse で開くと、警告が沢山出たことを当時(10年以上前)気にしていた。後述の問題は解消できない。
Exposed (kotlin)
Kotlin 良さと相まって、ものすごく少ない行数でDB アクセスができる点が素晴らしい。後述の問題にも対応できる。だが、ORマッパーの苦しさはJOOQ と同じ。あと、少し前にメジャーバージョンに変わった辺りでIF が変わり書き直しが必要になったのが辛かった。
よくある、ORマッパー派か生SQL派で言うと、私は生SQL派になります。生SQL を効率よく書きつつ、後述の問題を回避したいというのが私の願いです。
次に、多くのライブラリでカラムのデフォルト値が使えないという問題があると思うので触れてみます。
カラムデフォルト値問題
こちらは業務で影響が出ることはほとんどない重箱の隅のような話です。多くのライブラリは抽象化を優先している(そうあるべき)ため、この問題を孕んでいると思います。
例えばこのように、create_at カラムにnot null 制約と初期値としてnow() がついているとします。
create table account (
name text not null,
created_at timestamp not null default now()
updated_at timestamp not null default now()
)ORマッパーでInsert を行います。
var account = new Account();
account.setName("グリーン");
account.setCreatedAt(null);
account.setUpdatedAt(null);
repository.insert(account);このコードでは not null 制約に抵触してエラーになると思います。
発行されるSQLがこのようになるためです。
insert into account (name, created_at, updated_at) values ('グリーン', null, null);
--> ERROR: リレーション"account"の列"created_at"のNULL値が非NULL制約に違反していますDB でデフォルト値を持っているので、エラーにならなくてもよい気がします。
この問題が業務に影響することはほとんどありません。
こうするだけです。
var now = LocalDateTime.now();
account.setCreatedAt(now);一応、これに対する反論として、Java のサーバとDB サーバのマシン時刻が合っていない可能性があるというものがあります。
ですが、NTP を使えばずれはその差はほぼありません。僅かな差が許されない要件であることは稀でしょう。
さらに、初期値をカラムの定義に委ねるのはよろしくない、アプリで初期値を保つべきだという思想もあると思います。
思想に反する上に業務上困ることはないので気にしなければ良いのですが、私はDB が持つデフォルト値という機能をJava から使えなくなるという点にどうしても違和感を感じます。データはシステムの中心であり、データを管理するデータベースの機能がミドルウェアにより制限されるのは違うと思います。
ツールで解消
私が作成したツールは、DBのカラム定義がnot null で、デフォルト値を持っている場合に、Java から指定された値がnull の場合、Insert やUpdate の対象から外すようにしました。
そのため、先ほどのサンプルのSQL はこの形で実行されます。
insert into account (name) values ('グリーン');ツールはこちら。
このツールは、万人向けではありません。ただ、生SQL派で、DBのデフォルト値を大事にしたい人にはそれなりに刺さるかもしれません。
次回は、このツールを作るもうひとつのきっかけになった「作成日時・更新日時問題」について書きます。
コメントを残す