[step6] mybatis -> JPA 리팩토링

maxxyoung·2022년 10월 24일
0

OOP-PJT

목록 보기
6/7

객체 관계 정리

cmm_file, member -> 1 : 1 관계
member, message -> 1 : N 관계
member, message_request -> 1 : N 관계
member, friend_relation -> 1 : N 관계

도메인 엔티티 변경

Mybatis로 구현되어 있을 때는 RDB 그대로 복합키 만듦
JPA로 리팩토링 할 때는 대리키를 만들어 각각의 엔티티를 정의하고 관계를 매핑함
PK로 매핑하지 않았을 경우 나타난 문제점

@Entity
public class UploadFile {

    @Id @GeneratedValue
    @Column(name = "file_id")
    private Long id;
    private String targetId;
    private String localFileName;
    private String filePath;
    private String realFileName;
    public UploadFile(String realFileName, String localFileName, String filePath) {
        this.realFileName = realFileName;
        this.localFileName = localFileName;
        this.filePath = filePath;
    }

    public void setTargetId(String targetId){
        this.targetId = targetId;
    }
}
@Entity
public class Member implements Serializable {

    @Id
    private String memberId;
    private String id;
    private String password;
    private String nickname;
    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "file_id")
    private UploadFile uploadFile;

    public Member(String id, String password) {
        this.id = id;
        this.password = password;
    }


    public Member(String memberId, String id, String nickname, String password) {
        this.memberId = memberId;
        this.id = id;
        this.nickname = nickname;
        this.password = password;
    }

    public void setUploadFile(UploadFile uploadFile) {
        this.uploadFile = uploadFile;
    }

    public void setId(String id){
        this.id = id;
    }
}
@Entity
public class FriendRelation implements Serializable {

    @Id @GeneratedValue
    Long id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "my_id", referencedColumnName = "id")
    private Member me;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="friend_Id", referencedColumnName = "id")
    private Member friend;
    private String status;
    
    public FriendRelation(Member me, Member friend, String status) {
        this.me = me;
        this.friend = friend;
        this.status = status;
    }
}
@Entity
public class Message {

    @Id @GeneratedValue
    @Column(name = "message_id")
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "sender_id", referencedColumnName = "id")
    private Member sender;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "receiver_id", referencedColumnName = "id")
    private Member receiver;
    private LocalDateTime regDate;
    private String body;
    private String title;
    
    public Message(Member sender, Member receiver, SendMessageRequest messageRequest) {
        this.sender = sender;
        this.receiver = receiver;
        this.title = messageRequest.getTitle();
        this.body = messageRequest.getBody();
    }
}
@Entity
public class MessageRequest {

    @Id @GeneratedValue
    @Column(name = "message_request_id")
    private Long id;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "my_id", referencedColumnName = "id")
    private Member me;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "friend_id", referencedColumnName = "id")
    private Member friend;
    private String status;
    private LocalDateTime requestDate;
    
    public MessageRequest(Member me, Member friend, String status) {
        this.me = me;
        this.friend = friend;
        this.status = status;
    }
}

N+1 문제 해결

내 소스 상에서 N+1이 나탈 날 수 있는 지점은 다음과 같다

  • 친구 관계를 조회할 때, 멤버 정보
  • 메시지 요청 정보를 조회할 때, 멤버정보
  • 메시지 정보를 조회할 때, 멤버 정보
  • 멤버를 조회할 때, 프로필 파일 정보

매핑된 정보의 fetch를 FetchType.LAZY로 우선 다 설정하였고
멤버 정보가 필요한 곳은 join fetch을 사용하여 해결하였음

public List<Message> findAllReceivedList(String myId, int offset, int limit) {
        return em.createQuery(
                "select ms " +
                        "from Message ms " +
                        "join fetch ms.sender " +
                        "join fetch ms.receiver " +
                        "where ms.receiver.id = :myId", Message.class)
                .setParameter("myId", myId)
                .setFirstResult(offset)
                .setMaxResults(limit)
                .getResultList();
    }

테스트 코드 작성

Repository 테스트 코드 작성
Service 테스트 코드 작성
Controller 테스트 코드 작성

JPA를 활용한 API 성능 최적화

성능 최적화 과정

  • 엔티티 직접 노출하지 않기(DTO를 사용하여 Entity에 가는 영향 최소화)
 /**
     * 보낸 메시지 목록
     * @param offset
     * @param limit
     * @param request
     * @return
     */
    @GetMapping
    @RequestMapping("sent-list")
    public Result sentList(@RequestParam(defaultValue = "1") int offset,
                           @RequestParam(defaultValue = "100") int limit, HttpServletRequest request) {
        String myId = (String)request.getSession().getAttribute(SessionConst.ID);

        Result result = new Result();
        try{
            List<MessageDto>messageDtos = messageService.findAllSentList(myId, offset, limit);
            result.setData(messageDtos);
            result.setResultCode(CustomRespond.FAIL.getShortStatus());
            return result;
        } catch (Exception e){
            result.setResultCode(CustomRespond.FAIL.getShortStatus());
            return  result;
        }
    }
  • 응답값으로 array나 list일 경우 한 번 감싸서 응답값 주기
@Data
public class Result<T> {
    private T data;
    private String resultCode;
}
  • N+1은 fetch join으로 해결
profile
오직 나만을 위한 글. 틀린 부분 말씀해 주시면 감사드립니다.

0개의 댓글