publicIngredientfindById(String id) {returnjdbc.queryForObject("select id, name, type from Ingredient where id=?",this::mapRowToIngredient, id);}@OverridepublicIterable<Ingredient>findAll() {returnjdbc.query("select id, name, type from Ingredient",this::mapRowToIngredient);}privateIngredientmapRowToIngredient(ResultSet rs,int rowNum) throws SQLException {returnnewIngredient(rs.getString("id"),rs.getString("name"),Ingredient.Type.valueOf(rs.getString("type")));}
해당 코드에는 명령문이나 데이터베이스 연결 객체를 생성하는 코드가 아예 없다. 그리고 메서드의 실행이 끝난 후 그런 객체들을 클린업하는 코드 또한 없다. 또한, catch 블록에서 올바르게 처리할 수 없는 예외를 처리하는 어떤 코드도 없다.
findAll()과 findById() 모두의 두 번째 인자로는 스프링 RowMapper 인터페이스를 구현한 mapRowToIngredient() 메서드의 참조가 전달된다. 이처럼 메서드 인자로 다른 메서드의 참조를 전달할 수 있는 것은 자바 8에서 메서드 참조와 람다(lambda)가 추가되었기 때문이며, JdbcTemplate을 사용할 때 매우 편리하다. 그러나 종전처럼 RowMapper 인터페이스의 mapRow(0 메서드를 구현하는 방법을 사용할 수도 있다.
스키마 정의하고 데이터 추가하기
schema.sql이라는 이름의 파일이 애플리케이션 classpath의 루트 경로에 있으면 애플리케이션이 시작될 때 schema.sql 파일의 SQL이 사용 중인 데이터베이스에서 자동 실행된다. 따라서 schema.sql이라는 이름의 파일로 src/main/resources 폴더에 저장하면 된다.
create table if not exists Ingredient ( id varchar(4) not null, name varchar(25) not null, type varchar(10) not null);create table if not exists Taco ( id identity, name varchar(50) not null, createdAt timestamp not null);create table if not exists Taco_Ingredients ( taco bigint not null, ingredient varchar(4) not null);alter table Taco_Ingredients add foreign key (taco) references Taco(id);alter table Taco_Ingredients add foreign key (ingredient) references Ingredient(id);
@SessionAttributes("order")를 사용하는 이유는 Order 인스턴스를 세션에 담아두기 위함이다. 하나의 세션에서 생성되는 Taco 객체와 다르게 주문은 다수의 HTTP 요청에 걸쳐 존재해야 한다. 다수의 타코를 생성하고 그것들을 하나의 주문으로 추가할 수 있게 하기 위해서다. 그러면 세션에서 계속 보존되면서 다수의 요청에 걸쳐 사용될 수 있다.
주문 객체가 데이터베이스에 저장된 후에는 더 이상 세션에 보존할 필요가 없다. 그러나 만일 제거하지 않으면 이전 주문 및 이것과 연관된 타코가 세션에 남아 있게 되어 다음 주문은 이전 주문에 포함되었던 타코 객체들을 가지고 시작하게 될 것이다.
JDBC 버전의 리퍼지터리에서는 리퍼지터리가 제공하는 메서드를 우리가 명시적으로 선언하였다. 그러나 스프링 데이터에서는 그 대신 CrudRepository 인터페이스를 확장(extends)할 수 있다. CrudRepository 인터페이스에는 데이터베이스의 CRUD 연산을 위한 많은 메서드가 선언되어 있다. CrudRepository는 매개변수화 타입이다.
JPA 리퍼지터리 커스터마이징하기
지금까지 작성한 로직은 비교적 간단한 쿼리에서는 유용할 수 있다. 그러나 더 복잡한 쿼리의 경우는 메서드 이름만으로는 감당하기 어렵다. 따라서 이때는 어떤 이름이든 우리가 원하는 것을 지정한 후 해당 메서드가 호출될 때 수행되는 쿼리에 @Query 애노테이션을 지정하자.
@Query("Order o where o.deliveryCity = 'Seattle'")List<Order>readOrdersDeliveredInSeattle();
이름 규칙을 준수하여 쿼리를 수행하는 것이 어렵거나 불가능할 때에도 @Query를 사용할 수 있다.