O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ

7.444 visualizações

Publicada em

JJUG-CCC 日本Javaユーザーズグループクロスコミュニティカンファレンス 2018/12 登壇資料です。

Publicada em: Tecnologia
  • DOWNLOAD THIS BOOKS INTO AVAILABLE FORMAT (Unlimited) ......................................................................................................................... ......................................................................................................................... Download Full PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... Download Full EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... ACCESS WEBSITE for All Ebooks ......................................................................................................................... Download Full PDF EBOOK here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... Download EPUB Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... Download doc Ebook here { https://tinyurl.com/y6a5rkg5 } ......................................................................................................................... ......................................................................................................................... ......................................................................................................................... .............. Browse by Genre Available eBooks ......................................................................................................................... Art, Biography, Business, Chick Lit, Children's, Christian, Classics, Comics, Contemporary, Cookbooks, Crime, Ebooks, Fantasy, Fiction, Graphic Novels, Historical Fiction, History, Horror, Humor And Comedy, Manga, Memoir, Music, Mystery, Non Fiction, Paranormal, Philosophy, Poetry, Psychology, Religion, Romance, Science, Science Fiction, Self Help, Suspense, Spirituality, Sports, Thriller, Travel, Young Adult,
       Responder 
    Tem certeza que deseja  Sim  Não
    Insira sua mensagem aqui

ツール比較しながら語る O/RマッパーとDBマイグレーションの実際のところ

  1. 1. #ccc_a1 ツール比較しながら語る O/RマッパーとDBマイグレーション JJUG-CCC 2018 Fall 日本Javaユーザーズグループ クロスコミュニティカンファレンス ベルサール西新宿 2018-12-15 Y.Watanabe
  2. 2. #ccc_a1 (ストップウォッチ スタート確認)
  3. 3. #ccc_a1 Who? ● 渡辺 祐 ● (株)ビズリーチ ● SREグループ ○ Site Reliability Engeneering ● twitter: @nabedge ● nabedge@gmail.com 3
  4. 4. #ccc_a1 Software Design 2019 / 1月号に寄稿しました 第2特集 リリースモデルの変更にどう対処する? Javaのバージョン問題に前向きに 取り組む方法 第3章 Javaをバージョンアップしやすくする アイデア 進化に臆さず,そのメリットを 享受するために 4
  5. 5. #ccc_a1 今日、伝えたいこと ● アプリケーションの寿命 < DBの寿命 ● 流れの速さのギャップに、アプリケーションのテク ノロジーで、うまいこと付き合う方法ってなんだろ う? 5
  6. 6. #ccc_a1 さっそくですがアンケート ● MyBatis(iBatis) ● SpringのJdbcTemplate ● Hibernate ● QueryDSL ● jOOQ ● Doma ● DBFlute ● S2JDBC ● Flyway 過去1年であなたが実際に仕事で使ったものは? 6
  7. 7. #ccc_a1 ただし! ● 銀の弾丸は無い ● エンジニアが現場の状況にあわせて ツールをチョイスして運用するしかない 7
  8. 8. #ccc_a1 もくじ1 1. タイプセーフなO/Rマッパーの特徴 2. DBマイグレーションツールとは? 3. Flywayとは? 8
  9. 9. #ccc_a1 もくじ2 4. 開発のスタート地点は? CREATE/ALTER文? ER図? テーブル定義書.xls ? JPAのエンティティクラス.java @Table, @Column 5. 開発用DBはどこにある? ローカルPC? 共有DBサーバ? 9
  10. 10. #ccc_a1 もくじ3 6. O/Rマッパーのソースコード自動生成を どのタイミングでやるか 7. 自動生成したコードをgitに入れるか 8. 自動生成したコードとドメインオブジェ クトのコードを分けるべきか 10
  11. 11. #ccc_a1 もくじ4 9. テストデータをどうやって投入するか 10. 実際に実行されるSQLを見たい 11. RDBMSの独自関数を使いたい 12. テーブル定義書をどう作るか 11
  12. 12. #ccc_a1 もくじ5 13. 複数のO/Rマッパーを同じプロジェクトで使う or乗り換えるためのヒント 12
  13. 13. #ccc_a1 (盛り込み過ぎ...) 13
  14. 14. #ccc_a1 1. モダンなO/Rマッパーの特徴 14
  15. 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
  16. 16. #ccc_a1 SpringのJdbcTemplate List<Book> books = jdbcTemplate.query( “SELECT ISBN, TITLE FROM BOOKS” + “ WHERE ISBN = ? ”, new Object[]{“hoge”}, // “?”のところに入れたい引数 new BeanPropertyRowMapper(Book.class) ); 16
  17. 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
  18. 18. #ccc_a1 いま紹介したのは旧来型O/Rマッパー ● メリット ○ とにかくSQLを手で書かないと気が済まない人 ● デメリット ○ タイプセーフではない ○ BOOKをBOOKSと書いても実行するまで(バグるまで)ミ スに気づけない ※想定しているテーブル名はBOOKです。前のページは わざと間違いを書いています。 18
  19. 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
  20. 20. #ccc_a1 DBFlute List<Book>books = bookBhv.selectEntity( condition -> { condition.query().setBookIsbn_equal("999"); } ); 赤字は自動されたJavaコードを 使っている箇所 20
  21. 21. #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 割愛
  22. 22. #ccc_a1 QueryDSL ● jOOQと似てる(ので、サンプルコードは省略) ● NoSQLも積極的(Lucene拡張、MongoDB拡張) ● 2016年9月、QueryDSLのメインなコミッターが 「やりきったから、別の仕事やるわ。」 と事実上の開発停滞宣言。*3 ● jOOQ陣営「QueryDSLおつかれ。俺たちはまだやるぜ」宣言。*4 ● 2018年5月 約2年ぶりのバージョンアップ 22
  23. 23. #ccc_a1 いま風なO/Rマッパーの共通項 ● タイプセーフなJavaコーディングでCRUDを書く ○ ミスったらコンパイルエラー ● DBにピッタリ合わせたJavaコードでCRUDを書く ○ DB変更の影響範囲がコンパイルエラーでわかる ● 上記を実現するために、 ○ テーブル作成済みのDBサーバから自動的にメタデータを 読み取って、Javaソースコードを自動生成 23
  24. 24. #ccc_a1 2. DBマイグレーションツールとは? 24
  25. 25. #ccc_a1 ここで言うDBマイグレーションとは DBに対する変更=DDL文の適用=を管理するツール ● O/Rマッパー同梱型 ○ Ruby on Rails ○ DBFlute ○ Hibernate(?) ● 専用ツール型 ○ LiquiBase ○ MyBatis Migration ○ Flyway 25
  26. 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のマイグレーション機能
  27. 27. #ccc_a1 LiquiBase ● 却下。 ● 巨大なXMLファイルを手でメンテし続ける前提だから 27 割愛
  28. 28. #ccc_a1 MyBatis Migrations ● 時間が無いので割愛。 ● 考え方はFlywayとよく似ている 28 割愛
  29. 29. #ccc_a1 3. Flywayとは (これが近年の本命) 29
  30. 30. #ccc_a1 基本的な考え方 DBマイグレーションツールが無い世界で DBA担当がDBに向かってやる基本動作は 究極、これだけ。 1. DDL文を FooBar-0001.sql ファイルに書いて保存。 2. 順に、一度だけ、実行する。 30
  31. 31. #ccc_a1 DB担当者の基本動作を そのままソース管理&実行管理する ツールが Flyway だと思えばいい 31
  32. 32. #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
  33. 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に記録されていれば、何も起きな い(べき等性)
  34. 34. #ccc_a1 補足 ● 運用中のDBに、途中から導入することも可能 ○ Flyway ■ “flyway baseline” でググる ○ DBFlute ■ 詳しくはマニュアルを ■ O/Rマッパーとして使わずとも、他のマイグレーション 支援コマンド群だけ使うことが可能 34
  35. 35. #ccc_a1 ちょっと休憩 35 1. 水を飲む 2. 時間を確認 20分くらい?
  36. 36. #ccc_a1 4. 開発のスタート地点はどこ? 36
  37. 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を同時に書き続ける
  38. 38. #ccc_a1 38 A. 手書きのDDLをテキスト形式で積み上げる この方法以外はすべて、なんだかんだで... ● ツールのセットアップと使い方が難しい ● 引き継ぎが難しくなる ● ツールが有償かつツールにロックインされる ● バージョン管理システムとの相性が...
  39. 39. #ccc_a1 5. 開発DBサーバはどこにあるべき? 39
  40. 40. #ccc_a1 40 A. 共有DB方式 チームのエンジニア全員のPCからサーバ室の1台のDBサー バに接続 B. ローカルDB方式 それぞれのエンジニアのPCに自分専用の開発DBを構築
  41. 41. #ccc_a1 ローカルDB方式 = Docker時代のデファクト 41 $ docker run mysql:5.7 $ ./gradlew flywayMigrate ● 不要なカラムを削除したい ● 不適切な名前のカラムを RENAMEしたい ● 新しい機能のために新しい テーブルを追加したい ● 並行して作業できる ● ただしFlywayの場合はsqlファイルのバージョン番号 だけは衝突しないように話し合う $ docker run mysql:5.7 $ ./gradlew flywayMigrate
  42. 42. #ccc_a1 6. O/Rマッパーのソースコード自動生成を どのタイミングでやるか 7. 自動生成したコードをgitに入れるか 42
  43. 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
  44. 44. #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
  45. 45. #ccc_a1 前頁のポイント ● DB変更とアプリケーションコードの変更を 同じブランチ/プルリクエストで作業できる ○ FlywayのマイグレーションSQLがバッティングしないように、変更内容 と適用順序をエンジニア間で要調整 ● O/Rマッパーの自動生成Javaコードはgitコミットしない ● そのかわりに開発者のPCと CIサーバそれぞれで 必要なタイミングで自動生成を実行 45
  46. 46. #ccc_a1 O/Rマッパーの自動生成コードもコミットしたい場合 ● マイグレーションSQL文のコミットと、 O/RマッパーのJavaコード自動生成の 実行&コミットを、同時にやるべき。 ● ということは、5頁前のような並行作業だとコンフリクトを起こし やすくなる。 ○ 特に自動生成したJavaコード部分のコンフリクト ● ということは、直列にしか作業できない(かもしれない) 46 割愛
  47. 47. #ccc_a1 DBFlute = 自動生成コードをコミットする前提 ● 例:他のカラムから導出、計算した結果を入れるプロパティを、 自動生成したエンティティクラスに追加 ● 例:共通のWHERE句を組み立て易くするために検索条件生 成クラスに自作のメソッドを追加 (正確には加筆用の継承クラスがあらかじめ自動生成される) 47
  48. 48. #ccc_a1 8. O/Rマッパーが自動生成したJavaコードと ドメインオブジェクトのコードを 分けるべきか? 注:DDDのそれというよりはDTOに近いかも 48
  49. 49. #ccc_a1 がぜん、分けるべき。 49 RDB Repository Logic O/Rマッパー 自動生成したentity クラス ドメインクラス /DTO ドメインクラス /DTO Controller ドメインクラス /DTO ● setter/getterで地 道に詰め替え ● MapStruct, Dozer, etc 長寿 長寿に なりがち コロコロ変 わる
  50. 50. #ccc_a1 ドメインクラスと自動生成クラスの名前衝突に注意 テーブル名 BOOK O/Rマッパが自動生成したエンティティクラスやメタデータクラス名 Book.java 丹念に手作りしたいDDD的なドメインクラスの名前 Book.java 50 名前衝突
  51. 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クラスの内部クラス)
  52. 52. #ccc_a1 ちょっと休憩 52 1. 水を飲む 2. 時間を確認 35分くらい?
  53. 53. #ccc_a1 9. テストデータの投入方法は? 53
  54. 54. #ccc_a1 テストデータは必須。しかし.... 54 ● 空っぽのテーブルでアプリケーションの動作確認はできない ● テストデータは固定ではない。特に日付。 ○ 「発売前の本」のつもりのデータが常に 2018-12-15 だったら?
  55. 55. #ccc_a1 DBFluteの場合 55 ‘replace-schema’コマンドが 1. 全てのテーブル、インデックスを DROP -> CREATE 2. xls, tsv, csvファイルがあればテストデータとしてINSERT csvファイル上の “$sysdate.addDay(7)” は コマンド実行時刻の7日後の値がDBカラムに入る
  56. 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文よりは楽。日付の相対指定も可能。
  57. 57. #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すればいい
  58. 58. #ccc_a1 10. O/Rマッパが作るSQLを見たい 58 - 手書きのSQL以外は信用しないタイプのエンジニアのため に -
  59. 59. #ccc_a1 DBFluteの場合 ● デフォルトでこんな感じ ○ SqlLogHandlerでさらに細かい制御も可能 59 出典 *5 結果データも出てる 呼び出し元クラス
  60. 60. #ccc_a1 jOOQのデバッグログ 60 出典 *6
  61. 61. #ccc_a1 O/Rマッパーを問わない方法もある ● JDBCドライバの中継器として稼働しつつ 実行しようとしているSQLをログ出力 (正確にはプリペアドステートメントだけのことがほとんど) ○ p6spy ○ log4jdbc 61
  62. 62. #ccc_a1 11. RDBMSの独自関数を使いたい 62
  63. 63. #ccc_a1 DBFluteの場合 ● sql_calc_found_rowsくらいならデ フォルト対応 ● 外出しSQLならなんでも書ける ● フォーマットは2-Way-SQL ● 呼び出し側コード (WHERE句の調整等)も 自動生成 63 出典 *7
  64. 64. #ccc_a1 jOOQの場合 64 出典 *8,9
  65. 65. #ccc_a1 12. テーブル定義書をどう作るか 65
  66. 66. #ccc_a1 自動生成 一択 66
  67. 67. #ccc_a1 DBFluteの場合 67 ‘doc’コマンド一発
  68. 68. #ccc_a1 SchemaSpyの場合 ● jarを直接実行、あるいはdocker run (*11) ● ER図も自動生成 68
  69. 69. #ccc_a1 13. 同じプロジェクトで 複数のO/Rマッパーを同時に使う or乗り換えるためのヒント 69
  70. 70. #ccc_a1 ● 複数のO/Rマッパを好きに混ぜて使って、 いいとこどりできたら幸せ。 ● 一つのWeb/DBプロジェクトの開発で、 2つ以上のO/Rマッパーを混ぜて使うことは 無理?、危険? ● トランザクション管理ェェ... 70
  71. 71. #ccc_a1 ※ Spring Frameworkを使っているとして 71
  72. 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
  73. 73. #ccc_a1 2つのO/Rマッパが使用する javax.sql.DataSource オブジェクトが 確実に同じ(インスタンス)であれば 正しくトランザクション管理できる。 73
  74. 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 でググる。
  75. 75. #ccc_a1 まとめ 75
  76. 76. #ccc_a1 Java/DB開発の今どきの手法とツール ● O/Rマッピングライブラリ ○ ソースコード自動生成によるタイプセーフ方式 ○ 外部SQLファイル実行方式 ● 実行したSQLのロギング ● DBマイグレーションの自動化 ● テストデータ投入の自動化 ● テーブル定義書の自動作成 ● トランザクションに気をつけて複数のO/Rマッパーを同時に使用 76 選択肢と使い方をよく吟味して、レッツ快適開発!
  77. 77. #ccc_a1 Thank you ! 77
  78. 78. #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

×