Loading
  • LIGHT

  • DARK

ROUTE

ルートゼロの
アクティビティ

【保存版】JPA vs JDBC│Spring Bootでの実務的な使い分けと導入判断ガイド

3

JPAとJDBC、あなたは正しく使い分けできていますか?

「Spring Bootのプロジェクトで、データベース操作をどう実装すればいいのかわからない…」 「JPAを使ってはいるけど、内部で何をしているのかイマイチ理解できていない…」

そんな悩みを抱えていませんか?

Spring Bootでは、データベースアクセスにおいて「JPA(Java Persistence API)」と「JDBC(Java Database Connectivity)」の2つが主に使われます。しかし、それぞれの特徴や用途を把握せずに使ってしまうと、後から保守性やパフォーマンスで苦労することも。

この記事では、JPAとJDBCの違い、使いどころ、JOINの使い方、nativeQueryの判断基準までを、実務目線で丁寧に解説します。初心者〜中級者の方が、今後のプロジェクトで迷わず選べるようになることがゴールです。


JPAとJDBCの基本と構造の違いを理解しよう

JPAとは?

JPAは、Javaの標準的なORM(Object Relational Mapping)仕様です。オブジェクトとDBのテーブルを1対1でマッピングすることで、SQLを直接書かずにデータ操作が可能になります。

@Entity
public class User {
    @Id
    private Long id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "department_id")
    private Department department;
}
User user = new User();
user.setName("Taro");
userRepository.save(user);

➡ JPAの主な利点:

  • SQLを書かずに開発できる

  • メンテナンス性が高い

  • 複雑なリレーションを扱いやすい(JOINも定義で実現可能)

JPAでのJOINの使い方

@Query("SELECT u FROM User u JOIN u.department d WHERE d.name = :deptName")
List<User> findByDepartmentName(@Param("deptName") String deptName);

➡ テーブル結合もJPQL(Java Persistence Query Language)で直感的に記述可能。

また、JOINの結果として返されるListの中身の型に注意が必要です。例えば、複数エンティティを返すクエリではObject[]型になることがあります。

@Query("SELECT u.name, d.name FROM User u JOIN u.department d")
List<Object[]> findUserAndDepartmentNames();

➡ この場合、Object[0]にUser名、Object[1]にDepartment名が格納されていることを意識しましょう。

nativeQueryの活用

JPAでは複雑なSQLやパフォーマンスチューニングが必要な場面でnativeQueryを使えます。

@Query(value = "SELECT * FROM users u WHERE u.created_at >= :date", nativeQuery = true)
List<User> findUsersCreatedAfter(@Param("date") LocalDate date);

➡ 自由度の高いSQLを使いたい場合、nativeQueryは有効な選択肢になります。


JDBCとは?

JDBCは、Java標準の低レベルなDB接続APIです。SQLを明示的に書き、結果を手動でマッピングする必要があります。

Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setLong(1, 1);
ResultSet rs = ps.executeQuery();

while (rs.next()) {
    String name = rs.getString("name");
    // ...
}

➡ JDBCの主な利点:

  • パフォーマンスが高い

  • DB処理を細かく制御できる

  • シンプルな処理なら実装が早い

JDBCでBeanに詰める実装例

JDBCではSQLの実行結果をJavaBeanに手動でマッピングする必要があります。

// UserBean.java
public class UserBean {
    private Long id;
    private String name;
    private String email;
    // getter/setter
}
// JDBCを使ったBeanへのマッピング
List<UserBean> users = new ArrayList<>();
Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT id, name, email FROM users");
ResultSet rs = ps.executeQuery();

while (rs.next()) {
    UserBean user = new UserBean();
    user.setId(rs.getLong("id"));
    user.setName(rs.getString("name"));
    user.setEmail(rs.getString("email"));
    users.add(user);
}

return users;

➡ 項目が多いテーブルでも、明示的にマッピングすることでバグを防ぎやすく、テストもしやすくなります。


ありがちなつまずきと正しい選び方の判断基準

よくある誤解①「JPAは万能」

誤り:「JPAを使えばどんなシステムにも適している」

正解: JPAは便利ですが、大量データ処理や複雑なSQLではパフォーマンスが落ちる場合があります。

// Before(JPA)
List<User> users = userRepository.findAll(); // 全件取得(非効率)

// After(nativeQuery または JDBC)
@Query(value = "SELECT id, name FROM users LIMIT 100", nativeQuery = true)
List<Object[]> getLimitedUsers();

また、JOINを使った場合、N+1問題が発生することもあるため、fetch joinの活用が重要です。

N+1問題とは?

エンティティのリストを取得した際に、関連するエンティティを1件ずつ追加で取得することで、クエリ数が急増する問題です。

// N+1問題の例(自動で1件ずつ発行される)
List<User> users = userRepository.findAll();
for (User u : users) {
    System.out.println(u.getDepartment().getName());
}

➡ この場合、Userの数+1件のSQLが発行され、パフォーマンスが著しく悪化します。

// 解決策:fetch join を使って一括取得
@Query("SELECT u FROM User u JOIN FETCH u.department")
List<User> findAllWithDepartments();

よくある誤解②「JDBCは古い・非推奨」

誤り:「JDBCはもう時代遅れ」

正解: JDBCは現在も実務でよく使われており、JPAの裏側でも使われています。特にバッチ処理やレガシーシステムでは重宝されます。


実務での選び方:こんな時はどっち?

【JPAを使うべきケース】
┗ オブジェクト指向で開発したい
┗ 複数のエンティティが関連している
┗ メンテナンス性を重視したい
┗ JOINを使ったリレーションを活用したい

【JDBCを使うべきケース】
┗ SQLを細かく最適化したい
┗ 大量データのバルク処理を行う
┗ native SQLで特殊な結合・集計が必要
┗ 処理の高速性を優先したい

また、併用することも実務ではよくあります。

  • 通常の画面系処理はJPA

  • バッチや重たい処理はJDBC(JDBCTemplate)やnativeQuery


JPAとJDBCはどちらも現場で生きるスキル

JPAとJDBCは、どちらかが優れているという話ではありません。それぞれに強みと弱みがあり、プロジェクトの要件やパフォーマンス要件によって使い分けることが重要です。

今回のポイントまとめ:

  • JPAは保守性・生産性に優れるが、パフォーマンス課題がある

  • JDBCは高速・自由度が高いが、開発効率や保守性は劣る

  • JOINやnativeQueryはJPAでも柔軟に活用できる

  • JOIN結果のListでは型に注意(Object[]など)

  • N+1問題はfetch joinで回避可能

  • 実務では併用パターンも多い

GitHubでJPAとJDBCの実装比較コードを試せるリポジトリを探すと、理解が深まります。

【外部リンク】

Spring Boot公式サイト

Spring JPA ガイド

Oracle公式JDBCチュートリアル

【内部リンク】

【Spring Boot実践ガイド】List型・Map型の違いとBeanクラスの基礎&応用

Spring Bootアプリ開発で頻発するWhitelabel Error Page|Thymeleafのテンプレートエラーと対策まとめ

Javaのfor文をStreamに書き換えるには?Spring Bootでの実践例付きでわかる!

Spring Bootのよく使われるアノテーションとは?初心者必見の解説と活用法

Spring Boot バリデーション入門|MVC統合とカスタムアノテーション実践例

もっとルートゼロを知りたいなら

DISCOVER MORE