Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
やっぱり SQL を 
書きたい派に送る jOOQ 
第九回 #渋谷java on 2014-12-13 
Yusuke Ikeda(@yukung)
$ whoami 
• 池田 裕介 
• @yukung : http://yukung.hatenablog.com/ 
• 道玄坂の方の緑の会社でジャバとか Node.js とか 
Cassandra とか 
• 会社では golang 流...
本題
jOOQ ご存じですか
ちょっとした案件にて 
• とあるマスタ情報を返す Web API 
• マスタメンテナンスのバッチ 
• 期間1ヶ月程度であんまり時間ない 
• 自分1人だけ 
• 作ったら引き継ぎする予定で、性質的に頻繁にメンテ 
されるものではない
弊社 Java プロジェクトの鉄板構成 
• Spring Framework 
• Tomcat 
• MyBatis 3 
• MySQL
(´ヘ`;)ウーム…
まぁ引き継ぎ考えたら 
堅いけどね…
でもぶっちゃけ
(゚⊿゚)ツマンネ 
モチベーション大事ですよね…?
というわけで
軸はあまり変えずに今っぽく 
• Spring Framework 
• Tomcat 
• MyBatis 3 
• MySQL
軸はあまり変えずに今っぽく 
• Spring Framework 
• Tomcat 
• MyBatis 3 
• MySQL 
• Spring Boot 
• Dropwizard と迷ったけど、ちょっと停滞気味? 
• (Embedd...
みなさんなら???の 
とこ何選びますか?
• そのまま MyBatis でええやん派
• そのまま MyBatis でええやん派 
• 後述
• そのまま MyBatis でええやん派 
• 後述 
• JPA だろ標準だし jk 派
• そのまま MyBatis でええやん派 
• 後述 
• JPA だろ標準だし jk 派 
• JPA/Hibernate はハマった話の方がよく聞く
• そのまま MyBatis でええやん派 
• 後述 
• JPA だろ標準だし jk 派 
• JPA/Hibernate はハマった話の方がよく聞く 
• ここはビズリーチだぞ DBFlute だろ空気読めよ
• Doma 一択派
• Doma 一択派 
• Doma 良かったんだけど、S2Dao 経験してる人 
居なくて決め手に欠けるのと、Lombok と相性悪 
くて諦めた(APT 絡みで)
• Doma 一択派 
• Doma 良かったんだけど、S2Dao 経験してる人 
居なくて決め手に欠けるのと、Lombok と相性悪 
くて諦めた(APT 絡みで) 
• 同じ理由で QueryDSL も諦めた
知見 
https://twitter.com/gakuzzzz/status/443924561961037824 
https://twitter.com/gakuzzzz/status/443925068486164480
知見 
https://twitter.com/gakuzzzz/status/443382960935284736
先日 @cero_t さんがこんな Tweet を
http://d.hatena.ne.jp/cero-t/20141212/1418339302
なんという昨日の俺
やっぱり SQL 
書きたい派
あと私 MyBatis が 
好きじゃない
MyBatis で 
実際に見たコード 
@Select({ 
"SELECT ", 
"isbn,", 
"author_id,", 
"title,", 
"publish_date,", 
"create_date,", 
"update...
MyBatis で 
実際に見たコード 
@Select({ 
"SELECT ", 
"isbn,", 
"author_id,", 
"title,", 
"publish_date,", 
"create_date,", 
"update...
クエリビルダーあるよ 
public String selectPersonLike(final String id, final String firstName, final String 
lastName) { 
return new ...
クエリビルダーあるよ 
public String selectPersonLike(final String id, final String firstName, final String 
lastName) { 
return new ...
MyBatis Generator が 
あるじゃない!
MyBatis Generator で 
生成した Criteria 使うと 
public List<ItemAchievement> findAllByApplicationId(long UserId, long 
Application...
MyBatis Generator で 
生成した Criteria 使うと 
public List<ItemAchievement> findAllByApplicationId(long UserId, long 
Application...
うーむ。悩ましい。
そこで jOOQ
http://www.jooq.org/
特徴 
• Database First 
• 既存の DB スキーマを重視します(レガシーなシステムのスキーマにも対応) 
• Typesafe SQL / Fluent API 
• コードを見ると一目瞭然。とことんタイプセーフにしようとす...
サンプル 
-- Select all books by authors born after 1920, 
-- named "Paulo" from a catalogue: 
SELECT * 
FROM author a 
JOIN b...
サンプル 
titleResult<Record> result = 
create.select() 
.from(AUTHOR.as("a")) 
.join(BOOK.as("b")).on(a.ID.equal(b.AUTHOR_ID)...
初見の感想 
• 日本ではほぼ聞かないけど、海外では割と使われている印象 
• ドキュメントがかなりしっかりしている(英語) 
• jOOQ ユーザーの Screen Cast やブログも結構ある 
• Screen Cast 一度見れば使い方...
初見の感想 
• GitHub にも連携ライブラリとのサンプルなど結構ある 
• 開発も割と活発(ただしやっぱり Lukas Eder さんだ 
け頑張ってる感) 
• 有料サポートもあるみたい 
• Lukas Eder さんは他にも jOO...
Demo 
https://github.com/yukung/jooq-sample
使ってリリースした後の知見 
! 
• リリースした後しばらく経ってから Repository 層(Dao 層)のコード見ても、 
SQL 読めれば大体何やってるかはすぐ分かる 
• POJO とのマッピングは .fetchInto(POJO....
必須じゃないけど便利 
// Fetch books and format them as CSV 
String csv = create.selectFrom(BOOK).fetch().formatCSV(); 
! 
ID,AUTHOR...
未知数なところ 
• 扱った案件が小規模だったので、パフォーマンスとか気になるところ 
• たくさん Join しなきゃいけないようなクエリ投げるプロジェクトで使うとやっ 
ぱりカオスる気がする 
• これは多分何かで解決するより、テーブルを見...
Conclusion 
• Java の O/R マッパー色々あるけど、jOOQ もなか 
なかいいよ! 
• とにかく SQL をタイプセーフに書きたいんだ、 
IDE の補完使ってガシガシ書きたいんだって人に 
オススメ 
• カスタマイズ...
jOOQ も選択肢に入れ 
てみては?
ありがとう 
ございました
やっぱり SQL を書きたい派に送る jOOQ
やっぱり SQL を書きたい派に送る jOOQ
Upcoming SlideShare
Loading in …5
×

やっぱり SQL を書きたい派に送る jOOQ

18,516 views

Published on

第九回 #渋谷java にてLTした jOOQ に関するスライドです。

Published in: Software
  • Be the first to comment

やっぱり SQL を書きたい派に送る jOOQ

  1. 1. やっぱり SQL を 書きたい派に送る jOOQ 第九回 #渋谷java on 2014-12-13 Yusuke Ikeda(@yukung)
  2. 2. $ whoami • 池田 裕介 • @yukung : http://yukung.hatenablog.com/ • 道玄坂の方の緑の会社でジャバとか Node.js とか Cassandra とか • 会社では golang 流行ってて乗り遅れ… • Groovy とか Gradle がすきです
  3. 3. 本題
  4. 4. jOOQ ご存じですか
  5. 5. ちょっとした案件にて • とあるマスタ情報を返す Web API • マスタメンテナンスのバッチ • 期間1ヶ月程度であんまり時間ない • 自分1人だけ • 作ったら引き継ぎする予定で、性質的に頻繁にメンテ されるものではない
  6. 6. 弊社 Java プロジェクトの鉄板構成 • Spring Framework • Tomcat • MyBatis 3 • MySQL
  7. 7. (´ヘ`;)ウーム…
  8. 8. まぁ引き継ぎ考えたら 堅いけどね…
  9. 9. でもぶっちゃけ
  10. 10. (゚⊿゚)ツマンネ モチベーション大事ですよね…?
  11. 11. というわけで
  12. 12. 軸はあまり変えずに今っぽく • Spring Framework • Tomcat • MyBatis 3 • MySQL
  13. 13. 軸はあまり変えずに今っぽく • Spring Framework • Tomcat • MyBatis 3 • MySQL • Spring Boot • Dropwizard と迷ったけど、ちょっと停滞気味? • (Embedded Tomcat/Jetty) • ??? • MySQL
  14. 14. みなさんなら???の とこ何選びますか?
  15. 15. • そのまま MyBatis でええやん派
  16. 16. • そのまま MyBatis でええやん派 • 後述
  17. 17. • そのまま MyBatis でええやん派 • 後述 • JPA だろ標準だし jk 派
  18. 18. • そのまま MyBatis でええやん派 • 後述 • JPA だろ標準だし jk 派 • JPA/Hibernate はハマった話の方がよく聞く
  19. 19. • そのまま MyBatis でええやん派 • 後述 • JPA だろ標準だし jk 派 • JPA/Hibernate はハマった話の方がよく聞く • ここはビズリーチだぞ DBFlute だろ空気読めよ
  20. 20. • Doma 一択派
  21. 21. • Doma 一択派 • Doma 良かったんだけど、S2Dao 経験してる人 居なくて決め手に欠けるのと、Lombok と相性悪 くて諦めた(APT 絡みで)
  22. 22. • Doma 一択派 • Doma 良かったんだけど、S2Dao 経験してる人 居なくて決め手に欠けるのと、Lombok と相性悪 くて諦めた(APT 絡みで) • 同じ理由で QueryDSL も諦めた
  23. 23. 知見 https://twitter.com/gakuzzzz/status/443924561961037824 https://twitter.com/gakuzzzz/status/443925068486164480
  24. 24. 知見 https://twitter.com/gakuzzzz/status/443382960935284736
  25. 25. 先日 @cero_t さんがこんな Tweet を
  26. 26. http://d.hatena.ne.jp/cero-t/20141212/1418339302
  27. 27. なんという昨日の俺
  28. 28. やっぱり SQL 書きたい派
  29. 29. あと私 MyBatis が 好きじゃない
  30. 30. MyBatis で 実際に見たコード @Select({ "SELECT ", "isbn,", "author_id,", "title,", "publish_date,", "create_date,", "update_date ", "FROM ", "book ", "WHERE ", "author_id = #{authorId} ", "AND ", "#{publishDate} = publish_date" }) Book select(@Param("authorId") long authorId, @Param("publishDate") Date publishDate);
  31. 31. MyBatis で 実際に見たコード @Select({ "SELECT ", "isbn,", "author_id,", "title,", "publish_date,", "create_date,", "update_date ", "FROM ", "book ", "WHERE ", • 保存したら崩れたんだけど…フォーマッタどこや • 修正入れてテスト実行した時に SQLException とかよく見る • 調査するのにクエリコピペするのめんどくさすぎる "author_id = #{authorId} ", "AND ", "#{publishDate} = publish_date" }) Book select(@Param("authorId") long authorId, @Param("publishDate") Date publishDate);
  32. 32. クエリビルダーあるよ public String selectPersonLike(final String id, final String firstName, final String lastName) { return new SQL() {{ SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME"); FROM("PERSON P"); if (id != null) { WHERE("P.ID like ${id}"); } if (firstName != null) { WHERE("P.FIRST_NAME like ${firstName}"); } if (lastName != null) { WHERE("P.LAST_NAME like ${lastName}"); } ORDER_BY("P.LAST_NAME"); }}.toString();
  33. 33. クエリビルダーあるよ public String selectPersonLike(final String id, final String firstName, final String lastName) { return new SQL() {{ SELECT("P.ID, P.USERNAME, P.PASSWORD, P.FIRST_NAME, P.LAST_NAME"); FROM("PERSON P"); if (id != null) { WHERE("P.ID like ${id}"); } if (firstName != null) { WHERE("P.FIRST_NAME like ${firstName}"); } if (lastName != null) { WHERE("P.LAST_NAME like ${lastName}"); } ORDER_BY("P.LAST_NAME"); }}.toString(); • 多少マシになったけど相変わらずカラム指定は文字列 • ぶっちゃけ StringBuilder とあんまり違わない…?
  34. 34. MyBatis Generator が あるじゃない!
  35. 35. MyBatis Generator で 生成した Criteria 使うと public List<ItemAchievement> findAllByApplicationId(long UserId, long ApplicationId){ ItemAchievementExample ItemAchievementExample = new ItemAchievementExample(); ItemAchievementExample.Criteria criteria = ItemAchievementExample.createCriteria(); criteria.andApplicationIdEqualTo(ApplicationId).andUserIdEqualTo(UserId); String tableSuffix = RoutingPartitionManager.getPartition(UserId).getSuffix(); return ItemAchievementMapper.selectByExample(tableSuffix, ItemAchievementExample); }
  36. 36. MyBatis Generator で 生成した Criteria 使うと public List<ItemAchievement> findAllByApplicationId(long UserId, long ApplicationId){ ItemAchievementExample ItemAchievementExample = new ItemAchievementExample(); ItemAchievementExample.Criteria criteria = ItemAchievementExample.createCriteria(); criteria.andApplicationIdEqualTo(ApplicationId).andUserIdEqualTo(UserId); String tableSuffix = RoutingPartitionManager.getPartition(UserId).getSuffix(); return ItemAchievementMapper.selectByExample(tableSuffix, ItemAchievementExample); } • Example って何や…サンプルっぽいな( ゚д゚)ポカーン • ぱっと見なにしてるか分かりづらい • メソッド名がウソついてたりしてたら…ウッ
  37. 37. うーむ。悩ましい。
  38. 38. そこで jOOQ
  39. 39. http://www.jooq.org/
  40. 40. 特徴 • Database First • 既存の DB スキーマを重視します(レガシーなシステムのスキーマにも対応) • Typesafe SQL / Fluent API • コードを見ると一目瞭然。とことんタイプセーフにしようとする意思を感じます。 • Code Generation • APT ではなく生成ツールを Maven / Gradle などから使います • Active Records • POJO や JPA ベースの Entity、イミュータブルな POJO や DAO も設定により自動生成 • and more…
  41. 41. サンプル -- Select all books by authors born after 1920, -- named "Paulo" from a catalogue: SELECT * FROM author a JOIN book b ON a.id = b.author_id WHERE a.year_of_birth > 1920 AND a.first_name = 'Paulo' ORDER BY b.title http://www.jooq.org/doc/3.5/manual/sql-building/sql-statements/dsl-and-non-dsl/
  42. 42. サンプル titleResult<Record> result = create.select() .from(AUTHOR.as("a")) .join(BOOK.as("b")).on(a.ID.equal(b.AUTHOR_ID)) .where(a.YEAR_OF_BIRTH.greaterThan(1920) .and(a.FIRST_NAME.equal("Paulo"))) .orderBy(b.TITLE) .fetch(); ! http://www.jooq.org/doc/3.5/manual/sql-building/sql-statements/dsl-and-non-dsl/
  43. 43. 初見の感想 • 日本ではほぼ聞かないけど、海外では割と使われている印象 • ドキュメントがかなりしっかりしている(英語) • jOOQ ユーザーの Screen Cast やブログも結構ある • Screen Cast 一度見れば使い方はほぼ分かるので、後はドキュメント 見つつ • Stack Overflow にも良QAがたくさん • ただし答えてるのが作者の Lukas Eder さんばっかりですごい頑張って る感
  44. 44. 初見の感想 • GitHub にも連携ライブラリとのサンプルなど結構ある • 開発も割と活発(ただしやっぱり Lukas Eder さんだ け頑張ってる感) • 有料サポートもあるみたい • Lukas Eder さんは他にも jOOL とか jOOX とか作って て DSL 厨っぽい
  45. 45. Demo https://github.com/yukung/jooq-sample
  46. 46. 使ってリリースした後の知見 ! • リリースした後しばらく経ってから Repository 層(Dao 層)のコード見ても、 SQL 読めれば大体何やってるかはすぐ分かる • POJO とのマッピングは .fetchInto(POJO.class) でわりかし空気読んでマッピング してくれる • カラム名とフィールド名が対応してない POJO や Join を考慮した POJO にマッピ ングしたかったら ModelMapper というマッピングライブラリと連携できるので それを使う • Flyway とか Liquibase のようなマイグレーションツールと組合せてコード生成し ながら使うのがベストプラクティスっぽい
  47. 47. 必須じゃないけど便利 // Fetch books and format them as CSV String csv = create.selectFrom(BOOK).fetch().formatCSV(); ! ID,AUTHOR_ID,TITLE 1,1,1984 2,1,Animal Farm ! // Fetch books and format them as JSON String json = create.selectFrom(BOOK).fetch().formatJSON(); ! {"fields":[{"name":"field-1","type":"type-1"}, {"name":"field-2","type":"type-2"}, ..., {"name":"field-n","type":"type-n"}], "records":[[value-1-1,value-1-2,...,value-1-n], [value-2-1,value-2-2,...,value-2-n]]}
  48. 48. 未知数なところ • 扱った案件が小規模だったので、パフォーマンスとか気になるところ • たくさん Join しなきゃいけないようなクエリ投げるプロジェクトで使うとやっ ぱりカオスる気がする • これは多分何かで解決するより、テーブルを見直しましょうだと思う • マッピングや結果セットを Handler や Listener 使ってコールバック内に処理 書けたりするけど、やりすぎるとカオスになる気が • Generation Tool も大分カスタマイズできるけど、やり過ぎない方が良さそう • Lukas さんがコミットできなくなったらちとアレかも(一応企業としてやって るみたいだけども)
  49. 49. Conclusion • Java の O/R マッパー色々あるけど、jOOQ もなか なかいいよ! • とにかく SQL をタイプセーフに書きたいんだ、 IDE の補完使ってガシガシ書きたいんだって人に オススメ • カスタマイズもかなりできるけど、やり過ぎない 方がいいと思います
  50. 50. jOOQ も選択肢に入れ てみては?
  51. 51. ありがとう ございました

×