はじめに
最近Springについてチームで勉強会をしていて、Spring JDBCについて以下の記事で基本をまとめた。
ただ、この時書いたRowMapperについていまいち理解が曖昧だったためもう少し調べてみた。
この記事で書かれていること
- Spring JDBCが提供している取得結果を変換するインターフェースは何があるか
- RowMapperとResultSetExtractorの使い方
Sprig JDBCで取得結果を独自にエンティティに変換するには?
Spring JDBCでは取得結果を変換するインターフェースが3つ用意されている。
- RowMapper
JDBCのResultSetを参照して特定のPOJOにマッピングするためのインターフェース。
ResultSetの1行を特定の1インスタンスに変換することができる。
ResultSetExtractorと異なり、ResultSetの1行から1インスタンスへ変換されることが保証される。
カーソル処理等のResultSetに対する処理をSpringにゆだねることができる。
- ResultSetExtractor
JDBCのResultSetを操作して特定のPOJOにマッピングするためのインターフェース。
RowMapperとResultSetExtractorの違い
RowMapper | ResultSetExtractor | |
---|---|---|
ResultSetの操作 | 許されていない | 許されている |
POJOの1インスタンス生成 | ResultSetの1行からのみ変換 | ResultSetの複数行から変換が可能 |
- RowCallbackHandler
JDBCのResultSetを参照して何らかの処理を行うためのインターフェース。
戻り値を返さない。取得結果のファイル出力やデータのチェック等を行う場合に使用。
JOINで取得した結果を複数のEntityオブジェクトへマッピングしたい場合は?
以下のテーブルを例に、1行のみレコードを変換する場合と複数レコードを変換する場合の両方確認してみる。
1行レコードのみ変換する場合
RowMapperインタフェースを実装する
■テーブル例
customerテーブル
customer_id(PK) | customer_no | first_name | last_name |
---|
reservationテーブル
reservation_id(PK) | customer_no | room_no |
---|
■JOIN SQL例
SELECT
c.customer_id,
c.customer_no AS c_customer_no,
c.first_name,
c.last_name,
r.reservation_id,
r.customer_no AS r_customer_no,
r.room_no
FROM
customer c
LEFT OUTER JOIN reservation r
ON c.customer_no = r.customer_no
WHERE
r.reservation_id = 1001
取得結果例:
customer_id | c_customer_no | first_name | last_name | reservation_id | r_customer_no | room_no |
---|---|---|---|---|---|---|
23 | guest23 | Mike | Johonson | 1001 | guest23 | 503 |
Customerエンティティ
public class Customer {
private int customerId;
private String customerNo;
private String firstName;
private String lastName;
private Reservation reservation;
...Getter, Setterメソッド
}
Reservationエンティティ
public class Reservation{
private int reservationId;
private String customerNo;
private int RoomNo;
...Getter, Setterメソッド
}
Customerリポジトリ
public Customer selectById(int id) {
String sql = "SELECT..." // 上に記載したJOIN SQL
return jdbcTemplate.queryForObject(sql, new CustomerRowMapper(), id);
}
CustomerRowMapperクラス
static class CustomerRowMapper implements RowMapper<Customer> {
@Override
public Customer mapRow(ResultSet rs, int rowNum) throws SQLException {
Customer customer = new Customer();
customer.setCustomerId(rs.getInt("customer_id"));
customer.setCustomerNo(rs.getString("c_customer_no"));
customer.setFirstName(rs.getString("first_name"));
customer.setLastName(rs.getString("last_name"));
Reservation reservation = new Reservation();
reservation.setReservationId(rs.getInt("reservation_id"));
reservation.setCustomerNo(rs.getString("r_customer_no"));
reservation.setRoomNo(rs.getInt("room_no"));
customer.setReservation(reservation);
return customer;
}
}
※以下の記事では1つのEntityオブジェクトへの変換にDataClassRowMapperを利用したが、上記のような複数オブジェクトへの変換はできないため利用できない。
https://qiita.com/atjjnnn/items/c0ea187cc5d8cc5519bb#レコードをentityオブジェクトに変換して取得する場合
(応用)ラムダ式を利用した場合の書き方
Customerリポジトリ
public List<Customer> selectById(int id) {
String sql = "SELECT..." // 上に記載したSQL
return jdbcTemplate.query(sql, (rs, rowNum -> {
Customer customer = new Customer();
customer.setCustomerId(rs.getInt("customer_id"));
customer.setCustomerNo(rs.getString("c_customer_no"));
customer.setFirstName(rs.getString("first_name"));
customer.setLastName(rs.getString("last_name"));
Reservation reservation = new Reservation();
reservation.setReservationId(rs.getInt("reservation_id"));
reservation.setCustomerNo(rs.getString("r_customer_no"));
reservation.setRoomNo(rs.getInt("room_no"));
customer.setReservation(reservation);
return customer;
});
}
ラムダ式を利用すると、RowMapperで行う処理をDAOクラスに直接記述するため、RowMapperインターフェースの実装クラスが不要となる。
複数レコードを変換する場合
ResultSetExtractorインタフェースを実装する
■テーブル例
customerテーブル
customer_id(PK) | customer_no | first_name | last_name |
---|
reservationテーブル
reservation_id(PK) | customer_no | room_no |
---|
■JOIN SQL例
SELECT
c.customer_id,
c.customer_no AS c_customer_no,
c.first_name,
c.last_name,
r.reservation_id,
r.customer_no AS r_customer_no,
r.room_no
FROM
customer c
LEFT OUTER JOIN reservation r
ON c.customer_no = r.customer_no
WHERE
c.customer_id = 23
取得結果例:
customer_id | c_customer_no | first_name | last_name | reservation_id | r_customer_no | room_no |
---|---|---|---|---|---|---|
23 | guest23 | Mike | Johonson | 1001 | guest23 | 503 |
23 | guest24 | Mike | Johonson | 1002 | guest24 | 504 |
23 | guest25 | Mike | Johonson | 1003 | guest25 | 505 |
※同じゲストが複数部屋を予約している想定
Customerエンティティ
public class Customer {
private int customerId;
private String customerNo;
private String firstName;
private String lastName;
private Reservation reservation;
private List<Reservation> reservations; // 複数のReservationオブジェクトのリスト
...Getter, Setterメソッド
}
Customerリポジトリ
public Customer selectById(int id) {
String sql = "SELECT..." // 上に記載したJOIN SQL
return jdbcTemplate.query(sql, new CustomerResultSetExtractor(), id);
}
CustomerResultSetExtractorクラス
static class CustomerResultSetExtractor implements ResultSetExtractor<Customer> {
@Override
public Customer extractData(ResultSet rs) throws SQLException, DataAccessException {
Customer customer = null;
while(rs.next()) {
if (customer == null) { // 1対多の1に該当するCustomerオブジェクトは1つ生成
Customer customer = new Customer();
customer.setCustomerId(rs.getInt("customer_id"));
customer.setCustomerNo(rs.getString("c_customer_no"));
customer.setFirstName(rs.getString("first_name"));
customer.setLastName(rs.getString("last_name"));
}
// 1対多の多に該当するReservationオブジェクトはレコードの数だけ生成
Reservation reservation = new Reservation();
reservation.setReservationId(rs.getInt("reservation_id"));
reservation.setCustomerNo(rs.getString("r_customer_no"));
reservation.setRoomNo(rs.getInt("room_no"));
customer.getReservations().add(reservation); // 取得したレコードの数だけ追加
}
return customer;
}
}
おわりに
何か間違えてたら教えてください。
参考