はじめに
Javaでデータベースにアクセスする方法として、JDBC、Spring JDBC、ORM(MyBatisやJPA/Hibernate を利用できる。
どれもDBアクセスのための仕組みであり、抽象度や役割に違いがある。
今まで役割の違いについて曖昧な状態で開発を行なっていたので、本記事では、理解を深めるために整理した。
1. JDBC(Java標準API)
- Java標準のDBアクセスAPI
- SQLを直接記述し、
ResultSet
からオブジェクトに変換する必要がある
// JDBCを直接利用する例
String sql = "SELECT id, name FROM products WHERE id = ?";
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection(); // コネクション取得
ps = conn.prepareStatement(sql); // SQL準備
ps.setString(1, "p01"); // パラメータ設定
rs = ps.executeQuery(); // SQL実行
if (rs.next()) {
Product p = new Product( // 結果をオブジェクトにマッピング
rs.getString("id"),
rs.getString("name")
);
}
} finally {
if (rs != null) rs.close(); // リソース開放
if (ps != null) ps.close();
if (conn != null) conn.close();
}
- 標準APIの利用
- SQLを完全に制御できる
- 記述の冗長さ
- 例外処理、リソース開放、マッピング処理の手間
2. Spring JDBC
- Springが提供するJDBCラッパー
- SQLは記述する必要があるが、例外処理やリソース開放を自動化できる
- さらに
RowMapper
によってSQLの実行結果をオブジェクトに簡潔に変換できる
String sql = "SELECT id, name FROM products WHERE id = ?";
// queryForObject を使って1件だけデータを取得
Product product = jdbcTemplate.queryForObject(
sql, // 実行するSQL
new Object[]{"p01"}, // プレースホルダ(?)に渡すパラメータ
(rs, rowNum) -> // RowMapper: ResultSetの1行をオブジェクトに変換する処理
new Product(
rs.getString("id"),
rs.getString("name")
)
);
JDBCラッパーとは
- JDBCそのものは低レベルAPIで、SQL実行・例外処理・リソース開放(close())などを全部自分で書く必要がある
- それを簡単に扱えるように「便利メソッド」や「共通処理」を提供するのが ラッパー
- つまり「JDBCを内部で呼び出しているけど、外からはもっとシンプルなコードで使えるようにした仕組み」
RowMapper の正体
- Spring JDBC が提供するインターフェース
public interface RowMapper<T> {
T mapRow(ResultSet rs, int rowNum) throws SQLException;
}
1行のデータをJavaオブジェクトに変換するために利用する。
クラスで実装する場合
// Product エンティティ
public class Product {
private String id;
private String name;
public Product(String id, String name) {
this.id = id;
this.name = name;
}
}
// RowMapper 実装
public class ProductRowMapper implements RowMapper<Product> {
@Override
public Product mapRow(ResultSet rs, int rowNum) throws SQLException {
// ResultSet からデータを取り出して Product に変換
return new Product(rs.getString("id"), rs.getString("name"));
}
}
// 利用例
String sql = "SELECT id, name FROM products WHERE id = ?";
Product product = jdbcTemplate.queryForObject(
sql, // 実行するSQL
new Object[]{"p01"}, // パラメータ
new ProductRowMapper() // RowMapper 実装を渡す
);
ラムダ式で書く場合(Java 8以降)
String sql = "SELECT id, name FROM products WHERE id = ?";
Product product = jdbcTemplate.queryForObject(
sql,
new Object[]{"p01"},
// RowMapper をラムダ式で記述
(rs, rowNum) -> new Product(
rs.getString("id"), // id 列を取得
rs.getString("name") // name 列を取得
)
);
👉 RowMapper は関数型インターフェースなので、クラスを定義せずに簡潔に記述できる。
3. ORM(MyBatis / JPA(Hibernate))
- ORMはオブジェクトとテーブルのマッピングを自動化する仕組み
- 内部的にはJDBCを利用
MyBatis
- SQLを明示的に記述するスタイル
- 結果セットとオブジェクトのマッピングを自動化できる
Mapper.xml
<select id="findProductById" resultType="Product">
SELECT id, name FROM products WHERE id = #{id}
</select>
resultType="Product"
を指定することで、MyBatis が SQLの実行結果(ResultSet) を
Product
クラスにマッピングしてくれている。
Product p = productMapper.findProductById("p01");
JPA / Hibernate
- オブジェクト操作によるDBアクセスを実現
- SQLをほとんど意識せずに利用できる
// Product エンティティを取得(裏では SELECT が実行される)
Product p = entityManager.find(Product.class, "p01");
4. 比較
特徴 | JDBC | Spring JDBC | MyBatis | JPA/Hibernate |
---|---|---|---|---|
SQL記述 | 必須 | 必須 | 必須 | 不要 |
マッピング | 手動 | RowMapper | 自動 | 自動 |
自由度 | 高 | 高 | 中 | 低 |
利用対象 | SQLを直接制御したい場合 | SQLを記述しつつ効率化したい場合 | SQL派で効率化を重視する場合 | SQLを隠蔽しオブジェクト中心で開発したい場合 |
5. まとめ
- JDBC … 基礎。SQLを完全に制御できるが冗長
- Spring JDBC … JDBCラッパー。例外処理やリソース開放を自動化でき、
RowMapper
でマッピングも簡潔にできる - MyBatis … SQLを記述したい派に最適。SQL制御と効率化の両立
- JPA/Hibernate … SQLを隠蔽し、オブジェクト操作中心の開発ができる