@PostMapping("/api/v2/members")
public CreateMemberResponse saveMemberV2(@RequestBody @Valid CreateMemberRequest request)
{
Member member = new Member();
member.setName(request.getName());
Long id = memberService.join(member);
return new CreateMemberResponse(id);
}
@Data
static class CreateMemberRequest
{
private String name;
}
REST API spec에 맞는 DTO를 따로 만들어 사용해야한다. Entity를 그대로 사용하게되면 유지보수도 힘들고 api spec이 Entity에 따라 수시로 변하는 일이 발생하게된다. Parameter로 받을떄도 마찬가지이다.
철저히 쿼리와 커맨드를 분리해야한다. 또 영속성이 끊어진 객체를 반환해야하는 문제점이 있다.
Validation 같은 검증로직은 DTO에 들어가 있는것이 좋다. 어떤 API에선 @NotEmpty가 필요할수도 있고 안필요할 수도 있기때문이다.
JSON 으로 리턴해줄경우 Array 일때는
@Data
@AllArgsConstructor
static class Result<T>
{
private T data;
}
{
"data" :[받아온 정보들]
}
항상 이런식으로 확장성 있게 return 해주어야 한다. 그냥 array를 return 해줘버리면 나중에
{
"count":"4",
"data":[]
}
이런식의 설계가 불가능 해져버린다.
@GetMapping("api/v2/members")
public Result memberV2()
{
List<Member> findMembers = memberService.findMembers();
List<MemberDto> collect = findMembers.stream()
.map(m -> new MemberDto(m.getName()))
.collect(Collectors.toList());
return new Result(collect,collect.size());
}
@Data
@AllArgsConstructor
static class Result<T>
{
private T data;
private int count;
}
@Data
@AllArgsConstructor
static class MemberDto
{
private String name;
}
{
"data": [
{
"name": "tony21"
},
{
"name": "12"
},
{
"name": "123"
}
],
"count": 3
}
이 member들을 다 불러오는 api는 기존 findMembers를 통하여 엔티티 방식으로 담아오고 후에 MemberDto 로 다 변환해준다.
그리고 최종적으로 위에서 언급한 api 확장성을 위해 Result 클래스에 담아주어 return 해준다.
양방향 연관관계
이런 상황일때 의 조회는 둘 중 한쪽을 꼭 @JsonIgnore 해주어야 무한참조루프 에러가 발생하지 않는다.
그리고Hibernate5Module 라이브러리를 추가해서 사용해야한다. 하지만 이런식으로 엔티티를 직접 API 스펙에 맞추는것은 위험한일이고 할 필요도 없는 일이다.
DTO 로 변환을 해서 반환을 하자
또한 1+N 문제가 발생하는데
- order 조회 1번으로
- order -> member 지연로딩 조회 N번
- order -> delivery 지연로딩 조회 N번
이떄 db에 직접 조회하는것이 아니라 영속성 켄텍스트를 조회하는 것이다.
이떄 지연로딩을 초기화하는 개념으로 fetch join으로 처음 쿼리를 날릴떄 관련된 객체까지 다 가져온다.
public List<OrderSimpleQueryDto> findOrderDtos()
{
return em.createQuery("select new jpabook.jpashop.repository.OrderSimpleQueryDto(o.id, m.name,o.orderDate , o.orderStatus, d.address)" +
" from Order o" +
" join o.member m" +
" join o.delivery d",OrderSimpleQueryDto.class)
.getResultList();
}
원하는 것만 가져오기위해서는 쿼리를 new를 통해 직접 짜주어야한다. 이떄도 DTO를 이용하여한다. fetch join의 경우 모든 select * 처럼 다 가져온다.
하지만 이렇게 짜면 재사용성이 많이 떨어진다. Repository는 엔티티의 순수성이 유지되면서 조회되는 것이 앵간하면 좋다.
저런 쿼리는 따로 OrderSimpleQueryRepository 처럼아예 따로 관리해주는것이 유지보수성 측면에서 괜찮다.
쿼리 방식 선택 권장 순서
- 우선 엔티티를 DTO로 변환하는 법을 선택한다.
- 필요하면 fetch join으로 성능을 최적화 한다. -> 대부분의 성능이슈가 해결될것이다.
- 그래도 안되면 DTO로 직접 조회하는 방법을 사용한다.
- 최후의 방법은 JPA가 제공하는 네이티브 SQL이나 스프링 JDBC Template을 사용해서 SQL을 직접 사용한다.
'WEB > JPA' 카테고리의 다른 글
실전! 스프링부트와 JPA와 활용2 (OSIV와 성능 최적화) (0) | 2021.04.06 |
---|---|
실전! 스프링부트와 JPA와 활용2 (컬렉션 조회 최적화) (0) | 2021.04.06 |
김영한 (ORM 표준 JPA 프로그래밍 11) 객체지향 쿼리 언어 소개2 - 중급문법 (0) | 2021.03.20 |
김영한 (ORM 표준 JPA 프로그래밍 10) 객체지향 쿼리 언어 소개 (0) | 2021.03.18 |
김영한 (ORM 표준 JPA 프로그래밍 9) 값 타입 (0) | 2021.03.16 |