Contenu connexe Similaire à ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ (20) ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ3. #ccc_a1
Who?
● 渡辺 祐
● (株)ビズリーチ
● SREグループ
○ Site Reliability Engeneering
● twitter: @nabedge
● nabedge@gmail.com
3
4. #ccc_a1
Software Design 2019 / 1月号に寄稿しました
第2特集
リリースモデルの変更にどう対処する?
Javaのバージョン問題に前向きに
取り組む方法
第3章
Javaをバージョンアップしやすくする
アイデア
進化に臆さず,そのメリットを
享受するために
4
15. #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
17. #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
19. #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
26. #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のマイグレーション機能
33. #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に記録されていれば、何も起きな
い(べき等性)
37. #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を同時に書き続ける
41. #ccc_a1
ローカルDB方式 = Docker時代のデファクト
41
$ docker run mysql:5.7
$ ./gradlew flywayMigrate
● 不要なカラムを削除したい
● 不適切な名前のカラムを
RENAMEしたい
● 新しい機能のために新しい
テーブルを追加したい
● 並行して作業できる
● ただしFlywayの場合はsqlファイルのバージョン番号
だけは衝突しないように話し合う
$ docker run mysql:5.7
$ ./gradlew flywayMigrate
43. #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
51. #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クラスの内部クラス)
56. #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文よりは楽。日付の相対指定も可能。
72. #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
74. #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 でググる。