프로젝트/Ku:room

QueryDSL로 동적 쿼리 가져오기

개발하는 민우 2025. 2. 13. 01:10

현재 친구 기능을 개발중이다. 친구 기능을 사용하면서, QueryDSL을 이용하여 동적 쿼리를 개선하였다.

친구 도메인은 다음과 같이 저장된다. 

Friend

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Friend extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "fromUser_id", nullable = false)
    private User fromUser;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "toUser_id", nullable = false)
    private User toUser;

    private FriendStatus status;

    public void setStatus(FriendStatus status) {
        this.status = status;
    }

    @Builder
    private Friend(User fromUser, User toUser, FriendStatus status) {
        this.fromUser = fromUser;
        this.toUser = toUser;
        this.status = status;
    }

    public static Friend of(User fromUser, User toUser, FriendStatus status) {
        return Friend.builder()
                .fromUser(fromUser)
                .toUser(toUser)
                .status(status)
                .build();
    }
}

 

FriendStatus

public enum FriendStatus {
    ACCEPT, PENDING, REJECT
}

 

한 유저(index=1)이 다른 유저(index=2)에게 친구 요청을 보내면, Friend 테이블에는 fromUser, toUser, status(1, 2, PENDING) 형태의 데이터로 저장된다. 친구 승인이 된다면 fromUser, toUser, status(1, 2, ACCEPT)로 수정이 된다.

따라서 유저 1의 친구를 모두 불러와야 할 때, fromUser 혹은 toUser가 1이고, status가 ACCEPT인 데이터를 모두 불러와야 한다.

따라서 이러한 동적 쿼리는 QueryDSL을 사용하면 좋다고 생각하였다.

 

 

FriendRepositoryCustom

interface를 이용하여, QueryDSL에 사용할 메서드를 정의한다.

public interface FriendRepositoryCustom {
    List<Friend> findFriends(FriendStatus status, Long userId);
    Boolean existFriends(FriendStatus status, Long fromUser, Long toUser);
    List<Friend> findOriginFriends(FriendStatus status, Long fromUser, Long toUser);
}

 

 

 

FriendRepositoryCustomImpl

interface를 구현한다. 

 

findFriends는 userId의 친구를 모두 불러오는 쿼리이다.

existFriends는 fromUser, toUser 관계인 친구가 DB 상에 존재하는지 유무를 가리는 쿼리이다.

@Repository
public class FriendRepositoryCustomImpl implements FriendRepositoryCustom {
    private final JPAQueryFactory queryFactory;

    @Autowired
    public FriendRepositoryCustomImpl(EntityManager em) {
        this.queryFactory = new JPAQueryFactory(em);
    }

    @Override
    public List<Friend> findFriends(FriendStatus status, Long userId) {
        QFriend friend = QFriend.friend;

        return queryFactory.selectFrom(friend)
                .where(friend.status.eq(status)
                        .and(friend.fromUser.id.eq(userId).or(friend.toUser.id.eq(userId))))
                .fetch();
    }

    @Override
    public Boolean existFriends(FriendStatus status, Long fromUser, Long toUser) {
        Integer fetchOne = queryFactory
                .selectOne()
                .from(friend)
                .where(friend.status.eq(status).and(
                        friend.fromUser.id.eq(fromUser).and((friend.toUser.id.eq(toUser)))))
                .fetchFirst();

        Integer fetchTwo = queryFactory
                .selectOne()
                .from(friend)
                .where(friend.status.eq(status).and(
                        friend.fromUser.id.eq(toUser).and((friend.toUser.id.eq(fromUser)))))
                .fetchFirst();

        return fetchOne != null || fetchTwo != null;
    }

    @Override
    public List<Friend> findOriginFriends(FriendStatus status, Long fromUser, Long toUser) {
        QFriend friend = QFriend.friend;

        List<Friend> fetch = queryFactory.selectFrom(friend)
                .where(friend.status.eq(status)
                        .and(friend.fromUser.id.eq(fromUser).or(friend.toUser.id.eq(toUser))))
                .fetch();

        List<Friend> fetch2 = queryFactory.selectFrom(friend)
                .where(friend.status.eq(status)
                        .and(friend.fromUser.id.eq(toUser).or(friend.toUser.id.eq(fromUser))))
                .fetch();

        fetch.addAll(fetch2);
        return fetch;
    }

}