DevBoi

[JPA] Nested Response List 처리 본문

Develop/[JPA]

[JPA] Nested Response List 처리

HiSmith 2023. 9. 30. 12:55
반응형

상속 적으로 Response가 필요한 경우가 있다.

부모 DTO가 있고 해당 디티오는 하위의 디티오 리스트를 가지고 있다.

 

해당 케이스는 아래와 같이 처리한다.

package com.boiler.core.backend.normaladmin.reservation.dto;

import com.boiler.core.backend.entity.Gym;
import com.boiler.core.backend.entity.Member;
import com.boiler.core.backend.entity.Reservation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.List;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class NormalReservationListResponse {
  Member trainer;
  List<NormalReservationDetailResponse> info;
}
package com.boiler.core.backend.normaladmin.reservation.dto;

import com.boiler.core.backend.entity.Member;
import com.boiler.core.backend.entity.Reservation;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class NormalReservationDetailResponse {
  Reservation reservation;
  Member member;
}

 

물론 엔티티를 내리는게 정상적인 방법은 아니지만, 단순 조회용이나 Response를 내리는것들은

영속성 컨텍스트가 어차피 끊어지기 때문에 딱히 위험하지 않다.

 

public List<?> getGymReservationList(NormalReservationDto normalReservationDto){
     QMember trainer = new QMember("trainer");
     return queryFactory
       .from(reservation)
       .leftJoin(trainer).on(trainer.id.eq(reservation.trainerId))
       .leftJoin(member).on(member.id.eq(reservation.memberId))
       .where(
         reservation.gymId.eq(normalReservationDto.gymId()),
         reservation.useDate.between(
           normalReservationDto.date(),
           normalReservationDto.date().plusDays(1))
       )
        .transform(groupBy(trainer.id)
          .list(Projections.constructor(NormalReservationListResponse.class,
              trainer.as("trainer"),
            list(Projections.fields(NormalReservationDetailResponse.class,
                member.as("member"),
                reservation.as("reservation")
              )
              )
         ))
       )
       .stream().toList();
   }

 

해당 과 같이 하면 아래와 같은 결과를 얻는다.

[
  {
    "trainer": {
      "createDate": "2023-09-29T23:56:52.909215",
      "modifiedDate": "2023-09-29T23:56:52.909215",
      "id": 2,
      "userId": "asdfadsf",
      "userPw": "dfjeisn!",
      "imgUrl": "string",
      "nickname": "string",
      "name": "string",
      "gender": "string",
      "introduce": "string",
      "birth": "string",
      "phone": "string",
      "address": "string",
      "email": "string",
      "authCode": "10",
      "snsCode": "string",
      "status": "10",
      "memo": "string"
    },
    "info": [
      {
        "reservation": {
          "createDate": "2023-09-26T22:41:58.260999",
          "modifiedDate": "2023-09-26T22:41:58.260999",
          "id": 1,
          "memberId": 1,
          "trainerId": 2,
          "status": "10",
          "gymId": 1,
          "useDate": "2023-09-26T13:41:51.966",
          "memo": "string",
          "cancleCaus": "string",
          "ticketIssueId": 1,
          "reservationName": "string",
          "reservationPhone": 0
        },
        "member": {
          "createDate": "2023-09-27T21:14:55.854566",
          "modifiedDate": "2023-09-27T21:14:55.854566",
          "id": 1,
          "userId": "smith123",
          "userPw": "dfjeisn!",
          "imgUrl": "string",
          "nickname": "string",
          "name": "string",
          "gender": "string",
          "introduce": "string",
          "birth": "string",
          "phone": "string",
          "address": "string",
          "email": "string",
          "authCode": "string",
          "snsCode": "string",
          "status": "10",
          "memo": "string"
        }
      }
    ]
  },
  {
    "trainer": {
      "createDate": "2023-09-29T23:56:57.495662",
      "modifiedDate": "2023-09-29T23:56:57.495662",
      "id": 3,
      "userId": "vzxcvzxv",
      "userPw": "dfjeisn!",
      "imgUrl": "string",
      "nickname": "string",
      "name": "string",
      "gender": "string",
      "introduce": "string",
      "birth": "string",
      "phone": "string",
      "address": "string",
      "email": "string",
      "authCode": "20",
      "snsCode": "string",
      "status": "10",
      "memo": "string"
    },
    "info": [
      {
        "reservation": {
          "createDate": "2023-09-26T22:42:01.285291",
          "modifiedDate": "2023-09-26T22:42:01.285291",
          "id": 4,
          "memberId": 1,
          "trainerId": 3,
          "status": "10",
          "gymId": 1,
          "useDate": "2023-09-26T13:41:51.966",
          "memo": "string",
          "cancleCaus": "string",
          "ticketIssueId": 1,
          "reservationName": "string",
          "reservationPhone": 0
        },
        "member": {
          "createDate": "2023-09-27T21:14:55.854566",
          "modifiedDate": "2023-09-27T21:14:55.854566",
          "id": 1,
          "userId": "smith123",
          "userPw": "dfjeisn!",
          "imgUrl": "string",
          "nickname": "string",
          "name": "string",
          "gender": "string",
          "introduce": "string",
          "birth": "string",
          "phone": "string",
          "address": "string",
          "email": "string",
          "authCode": "string",
          "snsCode": "string",
          "status": "10",
          "memo": "string"
        }
      }
    ]
  },
  {
    "trainer": {
      "createDate": "2023-09-29T23:56:52.909215",
      "modifiedDate": "2023-09-29T23:56:52.909215",
      "id": 2,
      "userId": "asdfadsf",
      "userPw": "dfjeisn!",
      "imgUrl": "string",
      "nickname": "string",
      "name": "string",
      "gender": "string",
      "introduce": "string",
      "birth": "string",
      "phone": "string",
      "address": "string",
      "email": "string",
      "authCode": "10",
      "snsCode": "string",
      "status": "10",
      "memo": "string"
    },
    "info": [
      {
        "reservation": {
          "createDate": "2023-09-26T22:42:01.431401",
          "modifiedDate": "2023-09-26T22:42:01.431401",
          "id": 5,
          "memberId": 1,
          "trainerId": 2,
          "status": "10",
          "gymId": 1,
          "useDate": "2023-09-26T13:41:51.966",
          "memo": "string",
          "cancleCaus": "string",
          "ticketIssueId": 1,
          "reservationName": "string",
          "reservationPhone": 0
        },
        "member": {
          "createDate": "2023-09-27T21:14:55.854566",
          "modifiedDate": "2023-09-27T21:14:55.854566",
          "id": 1,
          "userId": "smith123",
          "userPw": "dfjeisn!",
          "imgUrl": "string",
          "nickname": "string",
          "name": "string",
          "gender": "string",
          "introduce": "string",
          "birth": "string",
          "phone": "string",
          "address": "string",
          "email": "string",
          "authCode": "string",
          "snsCode": "string",
          "status": "10",
          "memo": "string"
        }
      },
      {
        "reservation": {
          "createDate": "2023-09-26T22:43:46.185942",
          "modifiedDate": "2023-09-26T22:43:46.185942",
          "id": 6,
          "memberId": 1,
          "trainerId": 2,
          "status": "10",
          "gymId": 1,
          "useDate": "2023-09-26T13:41:51.966",
          "memo": "string",
          "cancleCaus": "string",
          "ticketIssueId": 1,
          "reservationName": "string",
          "reservationPhone": 0
        },
        "member": {
          "createDate": "2023-09-27T21:14:55.854566",
          "modifiedDate": "2023-09-27T21:14:55.854566",
          "id": 1,
          "userId": "smith123",
          "userPw": "dfjeisn!",
          "imgUrl": "string",
          "nickname": "string",
          "name": "string",
          "gender": "string",
          "introduce": "string",
          "birth": "string",
          "phone": "string",
          "address": "string",
          "email": "string",
          "authCode": "string",
          "snsCode": "string",
          "status": "10",
          "memo": "string"
        }
      },
      {
        "reservation": {
          "createDate": "2023-09-26T22:43:46.688126",
          "modifiedDate": "2023-09-26T22:43:46.688126",
          "id": 7,
          "memberId": 1,
          "trainerId": 2,
          "status": "10",
          "gymId": 1,
          "useDate": "2023-09-26T13:41:51.966",
          "memo": "string",
          "cancleCaus": "string",
          "ticketIssueId": 1,
          "reservationName": "string",
          "reservationPhone": 0
        },
        "member": {
          "createDate": "2023-09-27T21:14:55.854566",
          "modifiedDate": "2023-09-27T21:14:55.854566",
          "id": 1,
          "userId": "smith123",
          "userPw": "dfjeisn!",
          "imgUrl": "string",
          "nickname": "string",
          "name": "string",
          "gender": "string",
          "introduce": "string",
          "birth": "string",
          "phone": "string",
          "address": "string",
          "email": "string",
          "authCode": "string",
          "snsCode": "string",
          "status": "10",
          "memo": "string"
        }
      }
    ]
  }
]

 

트레이너 기준으로 하위 member,resevation 정보를얻는것이다.

 

이렇게 하면 간단하게 구현가능하다.

JPA를 사용하다 보면 Entity를 내리는 것에 대해 부정적인 개발자들이 많다.

내 개인적인 생각으로는 정보 요청용은 DTO로 받고 이를 변환시켜서 엔티티로 넣고

그외 조회는 엔티티를 그대로 내려도 무방하다고 생각한다.

 

엔티티를 조심하는건 Persist context를 통해 의도치 않은 디비 변경때문이라고 생각하는데

조회에서 해당 값을 변환하거나 수정하는 경우는 거의 없다.

 

무작정 Entity를 레이어간 분리하자! 는 좀 ;; 억까스러운 개발자 매너리즘으로 보인다.

반응형