6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Spring JDBC で取得結果をエンティティにマッピングする

Last updated at Posted at 2024-06-09

はじめに

最近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;
	}
}

おわりに

何か間違えてたら教えてください。

参考

プロになるためのSpring入門

Spring徹底入門

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?