현재 예약 시스템은 화면에서 신청에 필요한 AccountId, 멤버십Id, 수업Id를 받아와 예약 히스토리에 해당 값을 insert하는 방식으로 진행한다. 이를 저장하기 위한 객체는 CourseReservation 객체였는데, 처음에는 AccountId만 PK로 지정했다가 수업Id와 AccountId를 같이 중복키로 등록해야 할 필요성이 생겼다.
이를 위해서 CourseReservationId라는 객체를 만들어주고 @Embeddable이라는 애노테이션을 붙여준다. 이렇게 PK만 따로 도메인을 만들어주면, 기존의 CourseReservation객체에서는 이를 주입받아서 사용하기만 하면 된다.
@Data
@Embeddable
@NoArgsConstructor
@AllArgsConstructor
public class CourseReservationId implements Serializable {
@Column(name="crs_id")
private Long crsId; // 수업아이디
@Column(name = "account_id", nullable = false)
private Integer accountId;
public CourseReservationId(Integer accountId, Long crsId) {
this.crsId = crsId;
this.accountId= accountId;
}
}
CourseReservation 객체에서는 중복키 객체를 등록하기 위해 @EmbeddedId 애노테이션을 붙여주면 된다.
@Data
@Entity
@Table(name = "crs_rsv")
public class CourseReservation {
@EmbeddedId
private CourseReservationId courseReservationId;
@Column(name = "mmbrshp_id", nullable = false)
private Integer mmbrshpId; // 멤버십 아이디
}
리포지토리 인터페이스도 아래와 같이 PK값에 Integer > CourseReservationId객체로 변환해주면 된다.
@Repository
public interface CourseReservationRepository extends JpaRepository<CourseReservation, CourseReservationId>
서비스 부분에서는 Repository의 매개변수를 생성할 때 CourseReservation과 CourseReservationId를 각각 셋팅하여 생성한다.
@Transactional
public CourseReservation saveCrsSrv(CourseReservationDto vo){
CourseReservation bean = new CourseReservation();
CourseReservationId id = new CourseReservationId(vo.getAccountId(), vo.getCrsId());
bean.setMmbrshpId(vo.getMmbrshpId());
bean.setCourseReservationId(id);
bean = courseReservationRepository.save(bean);
}
그리고 사용자가 예약한 수업리스트를 모두 가져오기 위해서 테스트 코드에 아래와 같은 로직을 추가해주었다.
List<CourseDTO> vo = courseReservationController.getReservedCourses(1);
System.out.println("reserved course: " + vo.toString());
테스트 결과는 ConverterNotFoundException 발생
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.core.euljiro.dto.CourseReservationDto]
CourseReservationDto는
@Data
@Builder
@NoArgsConstructor
public class CourseReservationDto {
@NotNull
private Long crsId; // 수업아이디
@NotNull
private Integer mmbrshpId; // 멤버십 아이디
@NotNull
private Integer accountId;
@Builder
public CourseReservationDto(Long crsId, Integer mmbrshpId , Integer accountId){
this.crsId = crsId;
this.mmbrshpId = mmbrshpId;
this.accountId = accountId;
}
}
Repository에서 JPQL이 이렇게 작성되어 있어 문제가 발생하였다.
@Repository
public interface CourseReservationRepository extends JpaRepository<CourseReservation, CourseReservationId> {
@Query(value="select p.crs_id as crsId, p.mmbrshp_id as mmbrshpId , p.account_id as accountId from crs_rsv p " +
"where p.account_id = :userId", nativeQuery = true)
public List<CourseReservationDto> findByUserId(@Param("userId") int userId);
}
native 쿼리 사용시 도메인으로 리턴을 받지 않고 Dto로 받아야 할 때 Converter를 작성해주어야 하는데 명시된 Converter가 없어서 나는 오류다(https://www.popit.kr/jpa-native-query-%EC%82%AC%EC%9A%A9-%EC%8B%9C-dto%EB%A1%9C-%EB%A7%A4%ED%95%91%ED%95%98%EA%B8%B0/)
그렇다... 리턴 타입에 Dto가 들어가있었다. 나는 도메인으로 리턴을 받을 생각이었는데....
그런데 알아보던 중, 중복키를 주입받았을 때 중복키 객체를 기준으로 쿼리 메소드를 작성하는 방법이 스프링 JPA 문서에 나와있었다.
https://www.baeldung.com/spring-jpa-embedded-method-parameters
// 1. Book 도메인
@Entity
public class Book {
@EmbeddedId
private BookId id; // id라는 이름으로 중복키 객체 생성
private String genre;
private Integer price;
//standard getters and setters
}
// 2. findById + 중복키 객체 내 키 name 으로 쿼리 메소드 생성
@Repository
public interface BookRepository extends JpaRepository<Book, BookId> {
List<Book> findByIdName(String name);
List<Book> findByIdAuthor(String author);
}
// 3. Jpa에서 자동으로 conversion 해줌
findByIdName -> directive "findBy" field "id.name"
findByIdAuthor -> directive "findBy" field "id.author"
나는 중복키 객체를
@EmbeddedId
private CourseReservationId courseReservationId;
이렇게 선언했으므로 쿼리메소드는 courseReservationIdAccountId로 작성하면 directive "findBy" field courseReservation.AccoutId 로 변환해주겠지! 아래와 같이 리포지토리 소스를 변경해주었다.
@Repository
public interface CourseReservationRepository extends JpaRepository<CourseReservation, CourseReservationId> {
// @Query(value="SELECT crs_id as crsId, mmbrshp_id as mmbrshpId , account_id as accountId from crs_rsv " +
// "where account_id = :userId", nativeQuery = true)
public List<CourseReservation> findByCourseReservationIdAccountId(@Param("userId") int userId);
}
하.. 뿌듯한 마음으로 회먹으러가야징
'개인 프로젝트 > 예약 시스템 개발하기' 카테고리의 다른 글
예약 시스템 개발하기 05. HttpMediaTypeNotSupportedException (0) | 2022.03.12 |
---|---|
예약 시스템 개발하기 04. JPA 쿼리 메소드와 JPQL , CharacterEncodingFilter (0) | 2022.03.06 |
예약 시스템 개발하기 03. axios 통신과 CORS 정책 (0) | 2022.02.02 |
예약 시스템 개발하기 02. Router (0) | 2022.01.31 |
예약 시스템 개발하기 01. Vuetify 설치 및 프로젝트 구조 (0) | 2022.01.31 |