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