More Related Content

Slideshows for you(20)

Similar to Java ORマッパー選定のポイント #jsug(20)

Recently uploaded(20)

Java ORマッパー選定のポイント #jsug

  1. (C) CASAREAL, Inc. All rights reserved. #jsug Java ORマッパー選定の ポイント (株)カサレアル 多⽥真敏 2019年1⽉31⽇ JSUG勉強会 1
  2. (C) CASAREAL, Inc. All rights reserved. #jsug このセッションについて ▸ 何故ORマッパーというものが必要か、
 どんな種類があるのか、どう選べばいいのかを
 解説します ▸ 各ORマッパーの詳細な設定⽅法などは触れません ▸ 初級〜中級者向け ▸ Javaで何らかのRDBアクセスプログラムを書いたことが ある⽅が対象です 2
  3. (C) CASAREAL, Inc. All rights reserved. #jsug ⾃⼰紹介 ▸ 多⽥真敏(@suke_masa) ▸ 研修トレーナー@カサレアル ▸ Spring / Java EE / Microservices
 / Cloud Foundry ▸ Pivotal認定講師 ▸ ⽇本Springユーザ会スタッフ ▸ ⽇本GlassFishユーザー会運営メンバー 3 仕事柄、 「ORマッパーは何がいいですか」 と聞かれることは多いです
  4. (C) CASAREAL, Inc. All rights reserved. #jsug ⽬次 ① ORマッパーとその分類 ② Type 1. JDBCラッパー型 ③ Type 2. SQLマッパー型 ④ Type 3. クエリビルダー型 ⑤ Type 4. ORマッパー型 ⑥ Extra. Spring Data ⑦ まとめ 4
  5. (C) CASAREAL, Inc. All rights reserved. #jsug ⽬次 ① ORマッパーとその分類 ② Type 1. JDBCラッパー型 ③ Type 2. SQLマッパー型 ④ Type 3. クエリビルダー型 ⑤ Type 4. ORマッパー型 ⑥ Extra. Spring Data ⑦ まとめ 5
  6. (C) CASAREAL, Inc. All rights reserved. #jsug JavaでDBアクセスと⾔えばJDBC 6 public class JdbcEmployeeRepository { private final DataSource dataSource; // 検索するSELECT⽂ private static final String SQL_SELECT_ALL = "SELECT e.id, e.name, e.salary, e.joined_date," + " e.department_id, d.name AS department_name" + " FROM employee e JOIN department d" + " ON e.department_id = d.id" + " ORDER BY e.id"; // 検索メソッド public List<Employee> findAll() { try (Connection con = dataSource.getConnection(); PreparedStatement ps = con.prepareStatement(SQL_SELECT_ALL); ResultSet rs = ps.executeQuery()) { // 次ページへ
  7. (C) CASAREAL, Inc. All rights reserved. #jsug JavaでDBアクセスと⾔えばJDBC 7 List<Employee> employeeList = new ArrayList<>(); while (rs.next()) { // ResultSetからEmployeeクラスに詰め替え int id = rs.getInt("id"); String name = rs.getString("name"); BigDecimal salary = rs.getBigDecimal("salary"); LocalDate joinedDate = rs.getDate("joined_date").toLocalDate(); int departmentId = rs.getInt("department_id"); String departmentName = rs.getString("department_name"); Employee employee = new Employee(id, name, salary, joinedDate, departmentId, departmentName); // Listに追加する employeeList.add(employee); } return employeeList; } catch (SQLException e) { throw new RuntimeException(e); } } }
  8. (C) CASAREAL, Inc. All rights reserved. #jsug JDBCの問題点 ① SQLを⽂字列でプログラム内にベタ書きする ▸ 後から修正するのは⼤変、"+"で連結するのも⾯倒、
 実⾏しないとスペルミスに気づけないことも… ② 毎回ほぼ同じことを書かなければならない※ ▸ Connectionを取得して、PreparedStatementを作って、
 rs.next()で繰り返して… ③ ResultSetをエンティティに詰め替えるのが⾯倒 ▸ (rs.getXxx()で列の値を取得→setterで代⼊) × 列数 8※「ボイラープレートコード」と呼ばれる 😩
  9. (C) CASAREAL, Inc. All rights reserved. #jsug そこでORマッパーの登場! ▸ Object Relational Mapping ▸ RDBのレコードをJavaのオブジェクトに変換する ▸ JDBCをラップして、冗⻑さを開発者に意識させ ない ▸ SQLを⾃動発⾏したり、外部ファイルに記述し たりできる 9 😆
  10. (C) CASAREAL, Inc. All rights reserved. #jsug ORマッパーは世の中にたくさんある 10 Spring JDBC JPA
  11. (C) CASAREAL, Inc. All rights reserved. #jsug ORマッパーは4種類 11 (広義の)ORマッパー 1. JDBCラッパー型: JDBCを薄くラッピングしただけ 2. SQLマッパー型: SQLとクラスの詰め替えに特化 3. クエリビルダー型: クラスやメソッドでSQLを記述する 4. (狭義の)ORマッパー型: リレーション重視、SQL⾃動発⾏
  12. (C) CASAREAL, Inc. All rights reserved. #jsug ⽬次 ① ORマッパーとその分類 ② Type 1. JDBCラッパー型 ③ Type 2. SQLマッパー型 ④ Type 3. クエリビルダー型 ⑤ Type 4. ORマッパー型 ⑥ Extra. Spring Data ⑦ まとめ 12
  13. (C) CASAREAL, Inc. All rights reserved. #jsug Type 1. JDBCラッパー型 ▸ JDBCを薄くラッピングしただけ ▸ 記述の冗⻑さは、JDBCより少しマシになる程度 ▸ 使いやすさと学習コストの低さが特徴 ▸ 例 ▸ Spring JDBC ←今回はコレを紹介 ▸ Apache Commons DbUtils、sql2o 13
  14. (C) CASAREAL, Inc. All rights reserved. #jsug Spring JDBC ▸ Spring Framework内で開発されている
 JDBCラッパー ▸ Connection・PreparedStatementは
 開発者に触らせない ▸ ResultSetからの詰め替えのみ開発者が記述する ▸ SpringのDIコンテナが無くても動く ▸ Java EEなど他の環境でも使える → 例 14
  15. (C) CASAREAL, Inc. All rights reserved. #jsug サンプルコード 15 List<Employee> employeeList = jdbcTemplate.query( "SELECT ... FROM ...", (rs, rowNum) -> { int id = rs.getInt("id"); String name = rs.getString("name"); BigDecimal salary = rs.getBigDecimal("salary"); LocalDate joinedDate = rs.getDate("joined_date").toLocalDate(); int departmentId = rs.getInt("department_id"); String departmentName = rs.getString("department_name"); return new Employee(id, name, salary, joinedDate, departmentId, departmentName); } ); SQL RowMapper
  16. (C) CASAREAL, Inc. All rights reserved. #jsug NamedParameterJdbcTemplateクラスの主なメソッド ▸ List<T> query(String, RowMapper<T>) ▸ 複数件検索 ▸ List<T> query(String, Map<String,?>, RowMapper<T>) ▸ 複数件検索(パラメーター指定付き) ▸ T queryForObject(String, Map<String,?>, RowMapper<T>) ▸ 単一検索 ▸ int update(String, Map<String,?>) ▸ INSERT・UPDATE・DELETEの実行 16
  17. (C) CASAREAL, Inc. All rights reserved. #jsug RowMapper<T>インタフェース ▸ T mapRow(ResultSet rs, int rowNum)のみを持つ
 関数型インタフェース ▸ ResultSetからエンティティに変換する処理を記述する 17 List<Employee> employeeList = jdbcTemplate.query( "SELECT ... FROM ...", (rs, rowNum) -> { int id = rs.getInt("id"); String name = rs.getString("name"); ... return new Employee(...); } );
  18. (C) CASAREAL, Inc. All rights reserved. #jsug query()におけるRowMapperのイメージ ▸ 各⾏ごとにmapRow()が実⾏される 18 id name salary 101 Alice $3000 102 Bob $3500 103 Chris $2500 mapRow() mapRow() mapRow() Employee Employee Employee
  19. (C) CASAREAL, Inc. All rights reserved. #jsug BeanPropertyRowMapperクラス ▸ 列名とプロパティ名が同じものは⾃動詰め替えしてくれる ▸ スネークケース(department_id)
 ↔ キャメルケース(departmentId) の変換対応 ▸ Date and Time APIも対応 19 List<Employee> employeeList = jdbcTemplate.query(SQL_SELECT_ALL, BeanPropertyRowMapper.newInstance(Employee.class));
  20. (C) CASAREAL, Inc. All rights reserved. #jsug JDBCができることは何でもできる ▸ 集計 ▸ long count = getForObject(
 "SELECT COUNT(*) ...", Long.class) ▸ バッチ更新 ▸ batchUpdate() ▸ 1:Nリレーションを持つクラスへの変換 ▸ ResultSetExtractor 20
  21. (C) CASAREAL, Inc. All rights reserved. #jsug 派⽣種:Bootiful SQL Template ▸ @cero_tさん作 (Java Champon!) ▸ Spring JDBCに、SQLマッパー型のような外部SQLファイル機能を追加 21https://github.com/cero-t/sqltemplate
  22. (C) CASAREAL, Inc. All rights reserved. #jsug まとめ:Spring JDBC ▸ メリット ▸ ボイラープレートコードを削減できる ▸ 使い⽅が簡単 ▸ BeanPropertyRowMapperなど意外と便利 ▸ デメリット ▸ SQLは変わらずベタ書きする ▸ RowMapperの記述は⼿間がかかる 22
  23. (C) CASAREAL, Inc. All rights reserved. #jsug ⽬次 ① ORマッパーとその分類 ② Type 1. JDBCラッパー型 ③ Type 2. SQLマッパー型 ④ Type 3. クエリビルダー型 ⑤ Type 4. ORマッパー型 ⑥ Extra. Spring Data ⑦ まとめ 23
  24. (C) CASAREAL, Inc. All rights reserved. #jsug Type 2. SQLマッパー型 ▸ SQLとクラスの詰替えに特化 ▸ SQLは別途テキストファイルに書くことが多い ▸ 例 ▸ MyBatis ←今回は主にコレを紹介 ▸ Doma ←コレも少し紹介 ▸ Mirage SQL、Jdbi、 24
  25. (C) CASAREAL, Inc. All rights reserved. #jsug MyBatis ▸ SQLをXMLに記述する ▸ 公式ドキュメントが多⾔語化されている ▸ 英語・⽇本語・スペイン語・ハングル語・中国語 ▸ Apacheプロジェクトの頃は「iBatis」だった ▸ Apacheからの独⽴時に「MyBatis」になった ▸ iBatis時代からの経験者も多い? 25
  26. (C) CASAREAL, Inc. All rights reserved. #jsug SQLを書くMapper XML 26 <mapper namespace="com.example.mapper.EmployeeMapper"> <select id="findById" resultType="Employee"> SELECT e.id AS id, e.name AS name, e.salary AS salary, e.joined_date AS joined_date, d.id AS department_id, d.name AS department_name FROM employee e JOIN department d ON e.department_id = d.id WHERE e.id = #{id} </select> </mapper>
  27. (C) CASAREAL, Inc. All rights reserved. #jsug 検索の実⾏ 27 public interface EmployeeMapper { public Employee findById(Integer id); } EmployeeMapper employeeMapper = ...; Employee employee = employeeMapper.findById(1); Mapper XMLと1対1の インタフェース
  28. (C) CASAREAL, Inc. All rights reserved. #jsug 条件分岐 28 <update id="update"> UPDATE employee e SET e.name = #{name} <if test="salary != null"> , e.salary = #{salary} </if> WHERE e.id = #{id} </update> public interface EmployeeMapper { public void update(String name, BigDecimal salary, Integer id); }
  29. (C) CASAREAL, Inc. All rights reserved. #jsug 不等号対策① ▸ CDATAセクションにする 29 <select id="findBySalaryUnder" resultType="Employee"> <![CDATA[ SELECT e.id AS id, e.name AS name, e.joined_date AS joined_date, e.salary AS salary, d.id AS department_id, d.name AS department_name FROM employee e JOIN department d ON e.department_id = d.id WHERE e.salary < #{salary} ORDER BY e.id ]]> </select> CDATAセクションにする
  30. (C) CASAREAL, Inc. All rights reserved. #jsug 不等号対策② ▸ CDATAセクションにする 30 <select id="findBySalaryUnder" resultType="Employee"> SELECT e.id AS id, e.name AS name, e.joined_date AS joined_date, e.salary AS salary, d.id AS department_id, d.name AS department_name FROM employee e JOIN department d ON e.department_id = d.id WHERE e.salary &lt; #{salary} ORDER BY e.id </select> エスケープする
  31. (C) CASAREAL, Inc. All rights reserved. #jsug <if>タグと不等号が混じったSQLを書くと ▸ <if>とCDATAの舞い踊り😱 31
  32. (C) CASAREAL, Inc. All rights reserved. #jsug まとめ:MyBatis ▸ メリット ▸ SQLが⾃由に記述できる ▸ デメリット ▸ XML内なので不等号の扱いが⾯倒 32
  33. (C) CASAREAL, Inc. All rights reserved. #jsug Doma ▸ 2-way SQLが書ける ▸ SQL単体でも実⾏可能(後述) ▸ ドキュメントやメッセージが⽇本語
 → 英語化が進⾏中 ▸ ⽇本語版ドキュメントは過去バージョンへ 33 ※URLに「/en」と⼊っていますが、2.20.0までは全て⽇本語です。
  2.21.0以降は段階的に英語化されています。
  34. (C) CASAREAL, Inc. All rights reserved. #jsug 2-way SQL 34 SELECT e.id AS id, e.name AS name, e.salary AS salary, e.joined_date AS joined_date, e.department_id AS department_id, d.name AS department_name FROM employee e JOIN department d ON e.department_id = d.id WHERE e.id = /*id*/101 パラメーターや条件分岐は コメントで書く コメント直後の値は Domaに無視される ▸ SQL単体でも、プログラムからでも実⾏可能
  35. (C) CASAREAL, Inc. All rights reserved. #jsug まとめ:Doma ▸ メリット ▸ 2-way SQL! ▸ デメリット ▸ アノテーションプロセッサーが他ライブラリのものと
 競合する可能性 ▸ ほぼ個⼈のプロジェクト 35 ぜひ開発に 参加しましょう!
  36. (C) CASAREAL, Inc. All rights reserved. #jsug ⽬次 ① ORマッパーとその分類 ② Type 1. JDBCラッパー型 ③ Type 2. SQLマッパー型 ④ Type 3. クエリビルダー型 ⑤ Type 4. ORマッパー型 ⑥ Extra. Spring Data ⑦ まとめ 36
  37. (C) CASAREAL, Inc. All rights reserved. #jsug Type 3. クエリビルダー型 ▸ SQLをクラスやメソッドで記述する ▸ 記述を間違えるとコンパイルエラーになる ▸ これらのクラスは⾃動⽣成することが多い ▸ 例 ▸ jOOQ ← 今回はコレを主に紹介 ▸ DBFlute ←コレも少し紹介 ▸ その他にもいろいろ(Reladomo、Querydsl、Ebean、Speedment、 Jinq、requery、Cayenne、Torque) 37
  38. (C) CASAREAL, Inc. All rights reserved. #jsug 先にお詫び ▸ このタイプは個⼈的に利⽤経験があまりなく、
 内容薄めです ▸ 不正確な部分があればお知らせください 38 🙇
  39. (C) CASAREAL, Inc. All rights reserved. #jsug jOOQ ▸ スイスのData Geekery GmbH社が開発 ▸ 無償版・有償版などプランがいろいろある 39
  40. (C) CASAREAL, Inc. All rights reserved. #jsug 検索の例 40 create.select(EMPLOYEE.ID, EMPLOYEE.NAME) .from(EMPLOYEE) .orderBy(EMPLOYEE.ID.asc()) .fetch();
  41. (C) CASAREAL, Inc. All rights reserved. #jsug まとめ:jOOQ ▸ メリット ▸ SQLタイプミスの⼼配がない! ▸ デメリット ▸ 複雑なSQLを完全に再現できるのか? ▸ いざとなったら⽂字列でSQLを書けるクエリビルダー型ORマッパー もあるっぽい ▸ やや記述は冗⻑な気がする 41
  42. (C) CASAREAL, Inc. All rights reserved. #jsug 変わり種:DBFlute ▸ @jfluteさん作 ▸ タイプセーフ記述 + 2-waySQL ▸ ドキュメント⽣成機能が充実 ▸ DBスキーマからDB定義書(HTML)を⽣成 ▸ 旧DBスキーマとの差分からDB変更履歴書(HTML)を ⽣成 42 http://dbflute.seasar.org
  43. (C) CASAREAL, Inc. All rights reserved. #jsug ⽬次 ① ORマッパーとその分類 ② Type 1. JDBCラッパー型 ③ Type 2. SQLマッパー型 ④ Type 3. クエリビルダー型 ⑤ Type 4. ORマッパー型 ⑥ Extra. Spring Data ⑦ まとめ 43
  44. (C) CASAREAL, Inc. All rights reserved. #jsug JPA ▸ Java EEで標準化されたデータアクセスの仕様 ▸ もともとはHibernateをモデルとしている ▸ JPA仕様に準拠したORマッパーの例 ▸ EclipseLink ▸ Hibernate 44
  45. (C) CASAREAL, Inc. All rights reserved. #jsug 基本的なCRUD処理 45 // 主キー検索。SELECT⽂は⾃動発⾏される Employee employee = entityManager.find(Employee.class, 1); // 追加。INSERT⽂は⾃動発⾏される entityManager.persist(new Employee(...)); // 更新。コミット時にUPDATE⽂が⾃動発⾏される Employee employee = entityManager.find(Employee.class, 1); employee.setName("Other Name"); // 削除。DELETE⽂が⾃動発⾏される Employee employee = entityManager.find(Employee.class, 1); entityManager.remove(employee);
  46. (C) CASAREAL, Inc. All rights reserved. #jsug JPQLによる検索 ▸ SQLに似ているが微妙に⽂法は違う ▸ 集合演算が出来ない、副問合せに制限あり、
 3つ以上のテーブルの結合に難あり、… 46 // 主キー検索 String jpql = "SELECT e FROM Employee e" + " WHERE e.name LIKE :name"; List<Employee> employeeList = entityManager.createQuery(jpql, Employee.class) .setParameter("name", "%a%") .getResultList();
  47. (C) CASAREAL, Inc. All rights reserved. #jsug [超重要] エンティティの状態 47 永続化コンテキスト MANAGED 状態 REMOVED 状態 NEW 状態 DETACHED 状態 persist() detach()
 clear() merge() remove() flush() flush() refresh() find()
 JPQL detach()
 clear()
  48. (C) CASAREAL, Inc. All rights reserved. #jsug 実装依存な挙動 ▸ EclipseLinkとHibernateでは、
 同じコードでも挙動が違う部分が多々 ▸ Hibernateの情報は、EclipseLinkで役に⽴たない
 ことがある😱 48
  49. (C) CASAREAL, Inc. All rights reserved. #jsug N+1問題 ▸ 注⽂:明細などの1:Nリレーションが
 ある場合に発⽣しうるパフォーマンス問題 ▸ 注⽂の検索(1回)+ 各注⽂に紐づく明細(N回)
 のSELECT⽂が発⾏される ▸ 対策はあるが(JOIN FETCH⽂)、
 3つ以上のテーブル結合にはテクニックが必要 49 遅延読み込みが ⼤きな原因の1つ
  50. (C) CASAREAL, Inc. All rights reserved. #jsug ネイティブSQLの扱いにも難あり ▸ 複数の⽅法があるが、制限がある or 記述が⾯倒 50 @Entity @SqlResultSetMapping( name = "product_id_name", classes = { @ConstructorResult(targetClass = ProductDto.class, columns = { @ColumnResult(name = "id"), @ColumnResult(name = "name") } ) } ) public class Product { … } アノテーションの中に アノテーションの中に アノテーション
  51. (C) CASAREAL, Inc. All rights reserved. #jsug 学習コストは⾼い ▸ JPA仕様書はA4で600ページ以上 ▸ もちろん全て英語 ▸ 書籍「パーフェクトJava EE」でも
 相当のページ数を割いている ▸ 多⽥の過去スライドは50分枠で120枚 ▸ 削る前は160枚(それでも控えめにしてた) 51
  52. (C) CASAREAL, Inc. All rights reserved. #jsug [アンケート] なぜJPAを選びましたか? 52 その他 10% ベンダーサポート があるから 40% 標準技術だから 50% ※個⼈の感覚値です きちんとした技術検証なしに 選ばれているように感じる (個⼈の意⾒)
  53. (C) CASAREAL, Inc. All rights reserved. #jsug JPAを使ってもいい条件 53 ① DBを新規に設計できる ② 集合演算やFROM句での副問合せなど、
 複雑なSQLは要件的に少ない ③ 「パーフェクトJava EE」を読破した⼈が
 プロジェクトに1⼈以上いる 1つでも当てはまらない項⽬があれば、 他のORマッパーを使った⽅がいいかも😅 ※「向いている」とは⾔ってない
  54. (C) CASAREAL, Inc. All rights reserved. #jsug まとめ:JPA ▸ 難易度が⾼いため、なるべく避けたほうが懸命 ▸ 使うなら必死に勉強を 54
  55. (C) CASAREAL, Inc. All rights reserved. #jsug ⽬次 ① ORマッパーとその分類 ② Type 1. JDBCラッパー型 ③ Type 2. SQLマッパー型 ④ Type 3. クエリビルダー型 ⑤ Type 4. ORマッパー型 ⑥ Extra. Spring Data ⑦ まとめ 55
  56. (C) CASAREAL, Inc. All rights reserved. #jsug Spring Data ▸ 多くのデータアクセス技術を抽象化して、
 共通のインタフェースを提供 56 Spring Data Spring Data JPA Spring Data Redis Spring Data MongoDB ・・・
  57. (C) CASAREAL, Inc. All rights reserved. #jsug CrudRepositoryインタフェース ▸ 基本的なCRUDメソッドを持つ ▸ これを継承したインタフェースを作成するだけ ▸ 実装クラスやそのインスタンスはSpringが実⾏時に作成 57 public interface EmployeeRepository extends CrudRepository<Employee, Integer> { // 空でOK。メソッドの追加も可能 }
  58. (C) CASAREAL, Inc. All rights reserved. #jsug エンティティクラス ▸ それぞれ付加するアノテーションが異なるが、
 それ以外はほぼ同じ 58 // JPA @Entity public class Employee { ... } // Spring Data Redis @RedisHash("employee") public class Employee { ... } データストアが違っても同じように アクセスできるのは本当にありがたい!
  59. (C) CASAREAL, Inc. All rights reserved. #jsug RDB向けはSpring Data JPAのみ ▸ 「Spring Dataを使いたいから」というだけで
 Spring Data JPAを使っている事も多いのでは? ▸ しかしJPAであることには変わりない ▸ JPAの複雑さを分かった上で採⽤してます? 59
  60. (C) CASAREAL, Inc. All rights reserved. #jsug [告知] このあと! ▸ しんどーさんによる
 Spring Data JDBCのセッションがあります ▸ Spring JDBCのSpring Data! ▸ JPAの複雑さなしでSpring Dataを使えるので期待 60
  61. (C) CASAREAL, Inc. All rights reserved. #jsug ⽬次 ① ORマッパーとその分類 ② Type 1. JDBCラッパー型 ③ Type 2. SQLマッパー型 ④ Type 3. クエリビルダー型 ⑤ Type 4. ORマッパー型 ⑥ Extra. Spring Data ⑦ まとめ 61
  62. (C) CASAREAL, Inc. All rights reserved. #jsug 正直、どれも⼀⻑⼀短あり。 62 ⭕ 2-way SQLが書ける ❌ アノテーションプロセッサー ⭕ SQLが書ける ❌ XMLにSQLを書く ⭕ 簡単 ❌ やや冗⻑、SQLベタ書き ⭕ タイプセーフ ❌ やや冗⻑ ⭕ ⾼機能 ❌ 複雑、SQLの扱いに難 享受するメリット・許容できるデメリットと、 プロジェクトの状況を鑑みて選定するしかない
  63. (C) CASAREAL, Inc. All rights reserved. #jsug ORマッパー選定フロー ※個⼈の意⾒です 63 どうしても
 JPAを使わなければならない
 事情がある ORマッパー型 タイプセーフに
 SQLを記述したい クエリ ビルダー型 SQL マッパー型 JDBC ラッパー型 SQLを外部ファイルに
 書きたい Yes No No No Yes Yes
  64. (C) CASAREAL, Inc. All rights reserved. #jsug 最後に伝えたいこと(建前) ▸ 「標準だから」だけの理由でJPAを選ばない🙅 ▸ もしJPAを使うなら必死で勉強してください ▸ 選択肢が多くあることを知り、
 プロジェクトに合わせて適切に選びましょう! ▸ ORマッパー名は挙げたので、
 気になったものをググってください 64
  65. (C) CASAREAL, Inc. All rights reserved. #jsug 最後に伝えたいこと(※個⼈の(ry) ▸ とにかく安牌ならSpring JDBC or MyBatis ▸ Doma、DBFlute、クエリビルダー型は、
 それらの知識がある⼈がいれば、良い選択と思います ▸ JPAを「積極的に採⽤したい」シチュエーションは、 特に思い浮かびません ▸ 個⼈プロジェクトが⼼配なら、
 ⾃社からコミッターを出しましょう 65
  66. (C) CASAREAL, Inc. All rights reserved. #jsug Enjoy Data Access!! ▸ ご清聴ありがとうございました! 66