DB/JPA

JPA DTO Mapping

코딩딩코 2022. 12. 29.

JPA를 사용하면서 원하는 데이터를 받기 위해서 NativeQuery를 작성하고 싶었는데

객체가 연관관계가 포함이 되어있어서인지 필요 없는 컬럼들도 추가를 해줘야 하는 상황이 되어버려서

다른 방법을 찾는 도중에 DTO로 바로 Mapping 시킬 수 있는 방법을 찾았습니다.

 

 

 

@SqlResultSetMapping, @NamedNativeQuery 를 이용해서 DTO로 받기

 

 

@SqlResultSetMapping(
        name = "followingListMapping",
        classes = @ConstructorResult(
                targetClass = ReadFollowListResponseDto.class,
                columns = {
                        @ColumnResult(name = "followCode", type = Integer.class),
                        @ColumnResult(name = "fromUserCode", type = Integer.class),
                        @ColumnResult(name = "followingFlag", type = Integer.class),
                        @ColumnResult(name = "fromUserName", type = String.class),
                        @ColumnResult(name = "toUserName", type = String.class),
                        @ColumnResult(name = "toDetailCode", type = Integer.class),
                        @ColumnResult(name = "fromDetailCode", type = Integer.class),
                        @ColumnResult(name = "toUserId", type = String.class),
                        @ColumnResult(name = "fromUserId", type = String.class),
                        @ColumnResult(name = "toUserNickname", type = String.class),
                        @ColumnResult(name = "fromUserNickname", type = String.class)
                }
        )
)
@NamedNativeQuery(
        name = "findFollowingListByUserCode",
        query = "select" +
                "   f.follow_code as followCode," +
                "   f.from_user_code as fromUserCode," +
                "   um.detail_code as toDetailCode," +
                "   um.user_id as toUserId," +
                "   um.user_name as toUserName," +
                "   um.user_nickname as toUserNickname," +
                "   um2.detail_code as fromDetailCode," +
                "   um2.user_id as fromUserId," +
                "   um2.user_name as fromUserName," +
                "   um2.user_nickname as fromUserNickname," +
                "   if(following_table.to_user_code is not null, 1, 0) as followingFlag " +
                "from" +
                "   follow f" +
                "   left outer join (select" +
                "   to_user_code" +
                "   from" +
                "   follow" +
                "   where" +
                "   from_user_code = :toUserCode) following_table on(following_table.to_user_code = f.from_user_code)" +
                "inner join user_mst um on(um.user_code = f.to_user_code)" +
                "inner join user_mst um2 on(um2.user_code = f.from_user_code) "
                "where" +
                "   f.to_user_code = :toUserCode",
        resultSetMapping = "followingListMapping"
)

가지고 오고 싶은 컬럼들만 DTO로 가지고 오려고 만들었습니다.

@SqlResultSetMapping는 Mybatis의 ResultMap과 비슷하다고 생각을 하면 됩니다.

resultSetMapping으로 @SqlResultSetMapping의 name과 연결해 줍니다.

@NamedNativeQuery의 name 속성은 나중에 쿼리를 불러올 때 사용합니다.

 

주의

@Entity가 선언된 Class에서 선언을 해줘야 합니다.

 

package com.project.instagram.web.dto.friend;

import lombok.Data;

@Data
public class ReadFollowListResponseDto {
    private int followCode;
    private int fromUserCode;
    private boolean followingFlag;
    private String fromUserName;
    private String toUserName;
    private int toDetailCode;
    private int fromDetailCode;
    private String toUserId;
    private String fromUserId;
    private String toUserNickname;
    private String fromUserNickname;

    public ReadFollowListResponseDto(){}

    public ReadFollowListResponseDto(int followCode, int fromUserCode, int followingFlag, String fromUserName, String toUserName, int toDetailCode, int fromDetailCode, String fromUserId,
                                     String toUserId, String toUserNickname, String fromUserNickname) {
        this.followCode = followCode;
        this.fromUserCode = fromUserCode;
        this.followingFlag = followingFlag > 0;
        this.fromUserName = fromUserName;
        this.toUserName = toUserName;
        this.toDetailCode = toDetailCode;
        this.toUserId = toUserId;
        this.fromDetailCode = fromDetailCode;
        this.fromUserId = fromUserId;
        this.toUserNickname = toUserNickname;
        this.fromUserNickname = fromUserNickname;
    }
}

@NamedNativeQuery에서 DTO로 받기 위해서는 DTO에 생성자가 있어야 합니다. 

 

이유는 모르겠지만 @AllArgsConstructor로는 안 되고 직접 만들어 줘야 작동합니다.

 

 

@Transactional
@Override
public List<ReadFollowListResponseDto> loadFollowUser(int userCode) {
    Query query = entityManager.createNamedQuery("findFollowingListByUserCode").setParameter("toUserCode", userCode);

    List<ReadFollowListResponseDto> followList = query.getResultList();
    return followList;
}

entityManager.createNamedQuery("위에서 작성한 @NamedNativeQuery의 name의 값").setParameter("키", 값);

 

이렇게 사용한다면 원하는 데이터를 바로 DTO로 받을 수 있습니다.

 

 

 

처음에는 Entity로 받고 DTO로 따로 변경하는 방법을 계속해서 시도해 보았지만.. 실패하고

결국 이 방법으로 했습니다.

'DB > JPA' 카테고리의 다른 글

JPA 즉시 로딩, 지연 로딩  (0) 2023.01.20
@PrePersist...  (0) 2023.01.14
JPA Auditing  (0) 2023.01.07
JPA @Entity  (0) 2023.01.06
JPA 더티 체킹(Dirty Checking)  (0) 2022.12.25

댓글