Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
ツール比較しながら語るO/RマッパーとDBマイグレーション
Search
Yu Watanabe
December 18, 2018
Technology
180
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
ツール比較しながら語るO/RマッパーとDBマイグレーション
Yu Watanabe
December 18, 2018
More Decks by Yu Watanabe
See All by Yu Watanabe
JUnitテストをCI環境で並列で実行する方法とその速度, スケーラビリティ
nabedge
6
2.9k
クラウド時代だからSpring-Retryフレームワーク
nabedge
0
330
JavaでWebサービスを作り続けるための戦略と戦術
nabedge
0
110
サーバーサイドな人がフロントエンド技術と仲良くするはじめの一歩
nabedge
0
86
Selenium再入門
nabedge
0
79
Webエンジニアがスタートダッシュをキメるためのローカル開発環境の勘所
nabedge
0
96
テストゼロからイチに進むための戦略と戦術
nabedge
0
110
jOOQってなんて読むの?から始めるSpringBootとO/Rマッパーの世界
nabedge
0
170
あなたのプロジェクトが気軽にJavaをバージョンアップするために必要なこと
nabedge
0
74
Other Decks in Technology
See All in Technology
Deployment の 先にある AI Agent 基盤 - kagent vNext、Agent Substrate、Hermes から読み解く Agent Runtime の現在地 / k8s-matsuri-2-ai-agent-platform-amsy810
masayaaoyama
3
550
生成AIエージェントを用いた、 手動テスト手順書から自動テストへの 変換手法の検討
magicpod
0
150
Minecraft JavaのMODをSwiftで作る
1mash0
0
160
Gitは怖い?共有ワークスペースから始めるSnowflakeチーム開発
coco_se
0
220
アプリをもっと"iOSアプリっぽく"する小さな工夫 / Small Touches That Make Your App Feel More Like an iOS App
matsuji
1
890
Railsのように考える: See through the Master
snoozer05
PRO
3
770
登壇の自信を奪う3匹のオバケ / 3 Ghosts That Rob You of Your Confidence in Public Speaking
pauli
8
900
絵ではじめるKubernetesセキュリティ
aoi1
3
540
Deploying a Full-Stack Bun-Native Framework on Cloudflare Workers
7nohe
0
160
TinyGo 開発サイクルを高速化する:Go で作るエミュレータ入門
zozotech
PRO
1
730
開発投資の期待値を上げるプロダクトロードマップづくり ~プロダクトエンジニアが越境して事業を伸ばす~
kekekenta
0
130
映像変換サーバーなしで端末内でHLSを生成してライブ配信
hikarusato
0
100
Featured
See All Featured
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
280
The #1 spot is gone: here's how to win anyway
tamaranovitovic
3
1.2k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
720
Un-Boring Meetings
codingconduct
0
420
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2.1k
How to build a perfect <img>
jonoalderson
1
6k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2.2k
Writing Fast Ruby
sferik
630
63k
Avoiding the “Bad Training, Faster” Trap in the Age of AI
tmiket
0
240
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
240
HU Berlin: Industrial-Strength Natural Language Processing with spaCy and Prodigy
inesmontani
PRO
0
700
Thoughts on Productivity
jonyablonski
76
5.4k
Transcript
#ccc_a1 ツール比較しながら語る O/RマッパーとDBマイグレーション JJUG-CCC 2018 Fall 日本Javaユーザーズグループ クロスコミュニティカンファレンス ベルサール西新宿 2018-12-15
Y.Watanabe
#ccc_a1 (ストップウォッチ スタート確認)
#ccc_a1 Who? • 渡辺 祐 • (株)ビズリーチ • SREグループ ◦
Site Reliability Engeneering • twitter: @nabedge •
[email protected]
3
#ccc_a1 Software Design 2019 / 1月号に寄稿しました 第2特集 リリースモデルの変更にどう対処する? Javaのバージョン問題に前向きに 取り組む方法
第3章 Javaをバージョンアップしやすくする アイデア 進化に臆さず,そのメリットを 享受するために 4
#ccc_a1 今日、伝えたいこと • アプリケーションの寿命 < DBの寿命 • 流れの速さのギャップに、アプリケーションのテク ノロジーで、うまいこと付き合う方法ってなんだろ う?
5
#ccc_a1 さっそくですがアンケート • MyBatis(iBatis) • SpringのJdbcTemplate • Hibernate • QueryDSL
• jOOQ • Doma • DBFlute • S2JDBC • Flyway 過去1年であなたが実際に仕事で使ったものは? 6
#ccc_a1 ただし! • 銀の弾丸は無い • エンジニアが現場の状況にあわせて ツールをチョイスして運用するしかない 7
#ccc_a1 もくじ1 1. タイプセーフなO/Rマッパーの特徴 2. DBマイグレーションツールとは? 3. Flywayとは? 8
#ccc_a1 もくじ2 4. 開発のスタート地点は? CREATE/ALTER文? ER図? テーブル定義書.xls ? JPAのエンティティクラス.java @Table,
@Column 5. 開発用DBはどこにある? ローカルPC? 共有DBサーバ? 9
#ccc_a1 もくじ3 6. O/Rマッパーのソースコード自動生成を どのタイミングでやるか 7. 自動生成したコードをgitに入れるか 8. 自動生成したコードとドメインオブジェ クトのコードを分けるべきか
10
#ccc_a1 もくじ4 9. テストデータをどうやって投入するか 10. 実際に実行されるSQLを見たい 11. RDBMSの独自関数を使いたい 12. テーブル定義書をどう作るか
11
#ccc_a1 もくじ5 13. 複数のO/Rマッパーを同じプロジェクトで使う or乗り換えるためのヒント 12
#ccc_a1 (盛り込み過ぎ...) 13
#ccc_a1 1. モダンなO/Rマッパーの特徴 14
#ccc_a1 • Hibernate 2001〜 • Spring-JDBC(JdbcTemplate) 2001〜 • iBatis/MyBatis 2005〜
• S2JDBC 2008〜 • QueryDSL 2008〜 • DBFlute 2008〜 • jOOQ 2010〜 テーブル作成済みのDBサー バからメタデータを読み取っ てO/Rマッピング用Javaソー スを自動生成する方式 古い順に並べて超ざっくり分類 SQLを手で埋め込む方式 素人にはおすすめできない(*1,2) 15
#ccc_a1 SpringのJdbcTemplate List<Book> books = jdbcTemplate.query( “SELECT ISBN, TITLE FROM
BOOKS” + “ WHERE ISBN = ? ”, new Object[]{“hoge”}, // “?”のところに入れたい引数 new BeanPropertyRowMapper(Book.class) ); 16
#ccc_a1 MyBatis <!-- xmlファイル --> <select id="selectBook" parameterType=”String” resultType="Book"> <![CDATA[
SELECT ISBN, TITLE FROM BOOKS WHERE ISBN = #{isbn} ]]></select> // Javaコード List<Book> books = bookRepository.select(“hoge”); 17
#ccc_a1 いま紹介したのは旧来型O/Rマッパー • メリット ◦ とにかくSQLを手で書かないと気が済まない人 • デメリット ◦ タイプセーフではない
◦ BOOKをBOOKSと書いても実行するまで(バグるまで)ミ スに気づけない ※想定しているテーブル名はBOOKです。前のページは わざと間違いを書いています。 18
#ccc_a1 jOOQ(ジュークと読む) //テーブルのメタデータ情報クラス Book book = Tables.book; // SQLを組み立てて実行 List<BookVo>
books = dsl .select(book.isbn, book.title) .from(book) .where(book.isbn.eq(“hoge”)) .fetchInto(BookVo.class); // PoJoであれば手作りクラスでも可 タイプセーフ=間違えたらコンパイルエラーでわかる 赤字は自動されたJavaコードを 使っている箇所 19
#ccc_a1 DBFlute List<Book>books = bookBhv.selectEntity( condition -> { condition.query().setBookIsbn_equal("999"); }
); 赤字は自動されたJavaコードを 使っている箇所 20
#ccc_a1 うたぐり深い人へ、本当の話。 1. jOOQはもっと複雑なSQLを組み立てることも可能です。 a. 参考文献 https://docs.google.com/presentation/d/1MvsMo38Bt-2h4b_ZDSSXNSgq_UuweXx9P0HmlbO y8k8 2. 正直に言うと、DBFluteは
group by をサポートしていません。 a. そういうことは「外出しSQL」で書く方向。 b. 外出しSQLの結果マッピングや呼び出しコードの自動生成をサポート。 21 割愛
#ccc_a1 QueryDSL • jOOQと似てる(ので、サンプルコードは省略) • NoSQLも積極的(Lucene拡張、MongoDB拡張) • 2016年9月、QueryDSLのメインなコミッターが 「やりきったから、別の仕事やるわ。」 と事実上の開発停滞宣言。*3
• jOOQ陣営「QueryDSLおつかれ。俺たちはまだやるぜ」宣言。*4 • 2018年5月 約2年ぶりのバージョンアップ 22
#ccc_a1 いま風なO/Rマッパーの共通項 • タイプセーフなJavaコーディングでCRUDを書く ◦ ミスったらコンパイルエラー • DBにピッタリ合わせたJavaコードでCRUDを書く ◦ DB変更の影響範囲がコンパイルエラーでわかる
• 上記を実現するために、 ◦ テーブル作成済みのDBサーバから自動的にメタデータを 読み取って、Javaソースコードを自動生成 23
#ccc_a1 2. DBマイグレーションツールとは? 24
#ccc_a1 ここで言うDBマイグレーションとは DBに対する変更=DDL文の適用=を管理するツール • O/Rマッパー同梱型 ◦ Ruby on Rails ◦
DBFlute ◦ Hibernate(?) • 専用ツール型 ◦ LiquiBase ◦ MyBatis Migration ◦ Flyway 25
#ccc_a1 「いまの状態のDB -> 変更のDDLをあてる -> 次の状態のDBになる」 1. 「次の状態のDB」のフルDDL(CREATE文)を手で作っておく 2. 「今の状態から変更するためのDDL」も手で作っておく
3. DBFluteの”save-previous”コマンドで今の状態のDBの定義情報を保存 4. 3と1を使ってDBFluteの”alter-check”コマンドで下記を検証できる 今の状態 + 変更のDDL = 次の状態 5. 4の結果を見たDBAは安心して「変更のDDL」を本番DBで実行 6. 開発者はDBFluteの”replace-schema”で手元の開発DBを再構築 26 DBFluteのマイグレーション機能
#ccc_a1 LiquiBase • 却下。 • 巨大なXMLファイルを手でメンテし続ける前提だから 27 割愛
#ccc_a1 MyBatis Migrations • 時間が無いので割愛。 • 考え方はFlywayとよく似ている 28 割愛
#ccc_a1 3. Flywayとは (これが近年の本命) 29
#ccc_a1 基本的な考え方 DBマイグレーションツールが無い世界で DBA担当がDBに向かってやる基本動作は 究極、これだけ。 1. DDL文を FooBar-0001.sql ファイルに書いて保存。 2.
順に、一度だけ、実行する。 30
#ccc_a1 DB担当者の基本動作を そのままソース管理&実行管理する ツールが Flyway だと思えばいい 31
#ccc_a1 32 src/main/resources/db/migration/ V1.1__foo_init.sql <- 去年のサービス開始のとき V1.2__hoge_alter.sql <- 先月の機能追加のとき V1.3__add_foobar.sql
<- 来週のための機能追加 1. DBに対する変更を.sqlファイルで積み重ねてゆく 2. flywayを実行 $ ./gradlew flywayMigrate
#ccc_a1 33 3. 管理テーブルに無いsqlファイルだけが実行対象となる > SELECT ... FROM SCHEMA_VERSION version
| script | success ---------+-----------------------+--------- 0.1 | << Flyway Baseline >> | true 1.1 | V1.1__foo_init.sql | true 1.2 | V1.2__hoge_alter.sql | true 1.3 <- このレコードは未だ無いのでV1.3__add_foobar.sqlが対象 4. sqlファイルの追加や変更がない状態でもう一度 flywayMigrate して も、全て実行済みでSCHEMA_VERSIONに記録されていれば、何も起きな い(べき等性)
#ccc_a1 補足 • 運用中のDBに、途中から導入することも可能 ◦ Flyway ▪ “flyway baseline” でググる
◦ DBFlute ▪ 詳しくはマニュアルを ▪ O/Rマッパーとして使わずとも、他のマイグレーション 支援コマンド群だけ使うことが可能 34
#ccc_a1 ちょっと休憩 35 1. 水を飲む 2. 時間を確認 20分くらい?
#ccc_a1 4. 開発のスタート地点はどこ? 36
#ccc_a1 37 A. 手書きのDDL(を積んでゆくだけ) 最初にCREATE TABLE、 運用しながら ALTER, CREATE/DROP INDEX,
CREATE/DROP TABLE... B. ER図をまず書く。(そこからDDL文を自動生成) C. JPAのエンティティクラスを手書きし、Hibernate-JPAでDDL文 を自動生成 D. テーブル定義書.xlsと手書きのDDLを同時に書き続ける
#ccc_a1 38 A. 手書きのDDLをテキスト形式で積み上げる この方法以外はすべて、なんだかんだで... • ツールのセットアップと使い方が難しい • 引き継ぎが難しくなる •
ツールが有償かつツールにロックインされる • バージョン管理システムとの相性が...
#ccc_a1 5. 開発DBサーバはどこにあるべき? 39
#ccc_a1 40 A. 共有DB方式 チームのエンジニア全員のPCからサーバ室の1台のDBサー バに接続 B. ローカルDB方式 それぞれのエンジニアのPCに自分専用の開発DBを構築
#ccc_a1 ローカルDB方式 = Docker時代のデファクト 41 $ docker run mysql:5.7 $
./gradlew flywayMigrate • 不要なカラムを削除したい • 不適切な名前のカラムを RENAMEしたい • 新しい機能のために新しい テーブルを追加したい • 並行して作業できる • ただしFlywayの場合はsqlファイルのバージョン番号 だけは衝突しないように話し合う $ docker run mysql:5.7 $ ./gradlew flywayMigrate
#ccc_a1 6. O/Rマッパーのソースコード自動生成を どのタイミングでやるか 7. 自動生成したコードをgitに入れるか 42
#ccc_a1 ローカルDB方式 + 自動生成型O/R + Flyway の場合 1. エンジニアはそれぞれやりたいDB変更をDDLで書く 書いたら手元PCで
./gradlew flywayMigrate (手元のDBが変更される) 2. エンジニアはそれぞれ手元でO/RマッパのJavaコード生成を実行 自動生成したJavaコードはコミット対象外!(理由は後述) 3. 2.に合わせてアプリのJavaコードも書く 4. 手元のPCでアプリを起動 -> 動作確認 5. プルリクを作る -> masterブランチにマージ (続く) 43
#ccc_a1 ※以下はエンジニアのPCではなくCIサーバが実施 6. 全てのソースコードツリーをチェックアウト 7. CIサーバ内部でDockerでローカルDBを起動 8. ./gradlew flywayMigrate (ローカルDBの再構成)
9. ./gradlew [O/RマッパのJavaコード自動生成コマンド] 10. ./gradlew build ->全てのコードがjar/warファイル化される 11. アプリをデプロイする前に ./gradlew flywayMigrate -DdbHost=... ※今度はDBの向き先をデプロイ先環境内のDBにしておく 12. jar/warをデプロイ 44
#ccc_a1 前頁のポイント • DB変更とアプリケーションコードの変更を 同じブランチ/プルリクエストで作業できる ◦ FlywayのマイグレーションSQLがバッティングしないように、変更内容 と適用順序をエンジニア間で要調整 • O/Rマッパーの自動生成Javaコードはgitコミットしない
• そのかわりに開発者のPCと CIサーバそれぞれで 必要なタイミングで自動生成を実行 45
#ccc_a1 O/Rマッパーの自動生成コードもコミットしたい場合 • マイグレーションSQL文のコミットと、 O/RマッパーのJavaコード自動生成の 実行&コミットを、同時にやるべき。 • ということは、5頁前のような並行作業だとコンフリクトを起こし やすくなる。 ◦
特に自動生成したJavaコード部分のコンフリクト • ということは、直列にしか作業できない(かもしれない) 46 割愛
#ccc_a1 DBFlute = 自動生成コードをコミットする前提 • 例:他のカラムから導出、計算した結果を入れるプロパティを、 自動生成したエンティティクラスに追加 • 例:共通のWHERE句を組み立て易くするために検索条件生 成クラスに自作のメソッドを追加
(正確には加筆用の継承クラスがあらかじめ自動生成される) 47
#ccc_a1 8. O/Rマッパーが自動生成したJavaコードと ドメインオブジェクトのコードを 分けるべきか? 注:DDDのそれというよりはDTOに近いかも 48
#ccc_a1 がぜん、分けるべき。 49 RDB Repository Logic O/Rマッパー 自動生成したentity クラス ドメインクラス
/DTO ドメインクラス /DTO Controller ドメインクラス /DTO • setter/getterで地 道に詰め替え • MapStruct, Dozer, etc 長寿 長寿に なりがち コロコロ変 わる
#ccc_a1 ドメインクラスと自動生成クラスの名前衝突に注意 テーブル名 BOOK O/Rマッパが自動生成したエンティティクラスやメタデータクラス名 Book.java 丹念に手作りしたいDDD的なドメインクラスの名前 Book.java 50 名前衝突
#ccc_a1 // jOOQでのカスタム例 public class FooPrefixGeneratorStrategy extends DefaultGeneratorStrategy { @Override
public String getJavaClassName(final Definition definition, final Mode mode) { String name = super.getJavaClassName(definition, mode); switch (mode) { case POJO: return name + "Vo"; // エンティティクラスは BookVo.javaになる case DEFAULT: return 'Foo' + name; // メタデータクラスは FooBook.javaになる } return name; } 51 (正確にはTablesクラスの内部クラス)
#ccc_a1 ちょっと休憩 52 1. 水を飲む 2. 時間を確認 35分くらい?
#ccc_a1 9. テストデータの投入方法は? 53
#ccc_a1 テストデータは必須。しかし.... 54 • 空っぽのテーブルでアプリケーションの動作確認はできない • テストデータは固定ではない。特に日付。 ◦ 「発売前の本」のつもりのデータが常に 2018-12-15
だったら?
#ccc_a1 DBFluteの場合 55 ‘replace-schema’コマンドが 1. 全てのテーブル、インデックスを DROP -> CREATE 2.
xls, tsv, csvファイルがあればテストデータとしてINSERT csvファイル上の “$sysdate.addDay(7)” は コマンド実行時刻の7日後の値がDBカラムに入る
#ccc_a1 他の方法 56 A. RDBMSのcsv, tsvのバルクロード機能 a. 日付の相対指定が難しい B. INSERT文を用意して実行
a. 大量の手書きINSERT文が今後のDB変更に耐えられるか? C. 上記A,Bのハイブリッド a. csvで入れて相対日付カラムはUPDATE文 D. FlywayのJava-Based Migration a. DB定義変更用PJとは別PJとしてテスデータ用PJを作っておく b. SQL文ではなくJavaコードを作っておく c. INSERT文よりは楽。日付の相対指定も可能。
#ccc_a1 Flyway公式マニュアルによると 57 出典 *10 src/main/java/db/migration/V1_2__Another_user.java src/main/resources/db/migration/V1_3__HogeHoge.sql ./gradlew flywayMigrate でファイル名順に実行される
ループして値を変えながらINSERTすればいい
#ccc_a1 10. O/Rマッパが作るSQLを見たい 58 - 手書きのSQL以外は信用しないタイプのエンジニアのため に -
#ccc_a1 DBFluteの場合 • デフォルトでこんな感じ ◦ SqlLogHandlerでさらに細かい制御も可能 59 出典 *5 結果データも出てる
呼び出し元クラス
#ccc_a1 jOOQのデバッグログ 60 出典 *6
#ccc_a1 O/Rマッパーを問わない方法もある • JDBCドライバの中継器として稼働しつつ 実行しようとしているSQLをログ出力 (正確にはプリペアドステートメントだけのことがほとんど) ◦ p6spy ◦ log4jdbc
61
#ccc_a1 11. RDBMSの独自関数を使いたい 62
#ccc_a1 DBFluteの場合 • sql_calc_found_rowsくらいならデ フォルト対応 • 外出しSQLならなんでも書ける • フォーマットは2-Way-SQL •
呼び出し側コード (WHERE句の調整等)も 自動生成 63 出典 *7
#ccc_a1 jOOQの場合 64 出典 *8,9
#ccc_a1 12. テーブル定義書をどう作るか 65
#ccc_a1 自動生成 一択 66
#ccc_a1 DBFluteの場合 67 ‘doc’コマンド一発
#ccc_a1 SchemaSpyの場合 • jarを直接実行、あるいはdocker run (*11) • ER図も自動生成 68
#ccc_a1 13. 同じプロジェクトで 複数のO/Rマッパーを同時に使う or乗り換えるためのヒント 69
#ccc_a1 • 複数のO/Rマッパを好きに混ぜて使って、 いいとこどりできたら幸せ。 • 一つのWeb/DBプロジェクトの開発で、 2つ以上のO/Rマッパーを混ぜて使うことは 無理?、危険? • トランザクション管理ェェ...
70
#ccc_a1 ※ Spring Frameworkを使っているとして 71
#ccc_a1 72 @Autowired OrderBhv orderBhv; // DBFlute @Autowired DSLContext dsl;
// jOOQ @Transactional public void order(String isbn, Long memberId) { // 本を購入するメソッド Order order = new Order(); order.setIsbn(isbn); order.setMemberId(memberId); orderBhv.insert(order); Book book = Tables.Book; dsl.update(book) .set(book.STOCK, book.STOCK.minus(1)) .where(book.ISBN.eq(isbn)) .execute(); } • DBFluteでINSERT • jOOQでUPDATE • 一つのトランザクション (BIGIN〜 COMMIT) で実行されていればOK DBFlute jOOQ
#ccc_a1 2つのO/Rマッパが使用する javax.sql.DataSource オブジェクトが 確実に同じ(インスタンス)であれば 正しくトランザクション管理できる。 73
#ccc_a1 @Bean public javax.sql.DataSource dataSource() { // コネクションプール機構を使うとして(ここではHikariCP) HikariConfig config
= new HikariConfig(); config.setJdbcUrl(...); config.setUsername(...); config.setPassword(...); HikariDataSource ds = new HikariDataSource(config); // return ds; // ←こうじゃなくて↓こう return new TransactionAwareDataSourceProxy(ds); } 74 詳しくは TransactionAwareDataSourceProxy でググる。
#ccc_a1 まとめ 75
#ccc_a1 Java/DB開発の今どきの手法とツール • O/Rマッピングライブラリ ◦ ソースコード自動生成によるタイプセーフ方式 ◦ 外部SQLファイル実行方式 • 実行したSQLのロギング
• DBマイグレーションの自動化 • テストデータ投入の自動化 • テーブル定義書の自動作成 • トランザクションに気をつけて複数のO/Rマッパーを同時に使用 76 選択肢と使い方をよく吟味して、レッツ快適開発!
#ccc_a1 Thank you ! 77
#ccc_a1 参考文献 1. Hibernateはどのようにして私のキャリアを破滅寸前にしたか https://www.kaitoy.xyz/2017/02/23/how-hibernate-ruined-my-career/ 上記の原文 https://medium.com/@ggajos/how-hibernate-almost-ruined-16f31ba7d381 2. 我々はいかにして技術選択を間違えたのか? https://blog.cybozu.io/entry/2016/12/28/101500
3. https://groups.google.com/forum/#!msg/querydsl/fNFXliG8P-k/7dy2aAotVQ0J 4. https://blog.jooq.org/2014/05/29/querydsl-vs-jooq-feature-completeness-vs-now-more-than-ever/ 5. http://dbflute.seasar.org/ja/manual/function/genbafit/implfit/debuglog/index.html 6. https://www.jooq.org/doc/3.11/manual/sql-execution/logging/ 7. http://dbflute.seasar.org/ja/manual/function/ormapper/outsidesql/howto.html 8. https://www.jooq.org/doc/3.11/manual/sql-execution/query-vs-resultquery/ 9. https://www.jooq.org/doc/3.11/manual/sql-building/plain-sql 10. https://flywaydb.org/documentation/migrations#java-based-migrations 11. https://hub.docker.com/r/schemaspy/schemaspy/ 12. 78