현재 친구 기능을 개발중이다. 친구 기능을 사용하면서, 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;
}
}
'프로젝트 > Ku:room' 카테고리의 다른 글
개발자 겸 디자이너 겸 PM 도전기 (0) | 2025.02.14 |
---|---|
nGrinder 스크립트 검증 도중 Connection refused (1) | 2025.02.13 |
SecurityConfig 내 이용한 허용 URI Enum 클래스로 관리하기 (0) | 2025.02.13 |
각 상황에 맞는 @ControllerAdvice로 예외 메시지 분리하기 (0) | 2025.02.13 |
이메일 인증 기능 @Async 비동기 처리 (0) | 2025.02.08 |