본문 바로가기

Playlist/Content

ContentRepository, Custom, Impl

*/domain/content/repository

 

ContentRepository.java

import com.codeit.playlist.domain.content.entity.Content;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.UUID;

public interface ContentRepository extends JpaRepository<Content, UUID>, ContentRepositoryCustom {
    boolean existsByApiId(Long apiId);
    boolean existsByTypeAndApiId(String type, Long apiId);
}

 

 


 

ContentRepositoryCustom.java

import com.codeit.playlist.domain.content.dto.request.ContentCursorRequest;
import com.codeit.playlist.domain.content.entity.Content;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface ContentRepositoryCustom {
    List<Content> searchContents(ContentCursorRequest request, boolean ascending, int limit, String sortBy);

    long countContents(ContentCursorRequest request);
}

 


커서기반 페이지네이션

ContentRepositoryImpl.java

import com.codeit.playlist.domain.content.dto.request.ContentCursorRequest;
import com.codeit.playlist.domain.content.entity.Content;
import com.codeit.playlist.domain.content.entity.QContent;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Order;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.Instant;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;

@Slf4j
@Repository
@RequiredArgsConstructor
public class ContentRepositoryImpl implements ContentRepositoryCustom {

    private final JPAQueryFactory query;

    @Override
    public List<Content> searchContents(ContentCursorRequest request, boolean ascending, int limit, String sortBy) {
        QContent qContent = QContent.content;
        BooleanBuilder builder = new BooleanBuilder(); // BooleanBuilder
        log.info("[콘텐츠 데이터 관리] 콘텐츠 검색: ascending={}, sortBy={}, cursor={}, idAfter={}",
                ascending, request.sortBy(), request.cursor(), request.idAfter());
        // 검색 조건
        if (request.typeEqual() != null) {
            builder.and(qContent.type.eq(request.typeEqual()));
        }

        if (request.keywordLike() != null) {
            builder.and(qContent.title.containsIgnoreCase(request.keywordLike()));
        }

        // 정렬 기준, 정렬 방향
//        String sortBy = request.sortBy() == null ? "createdAt" : request.sortBy();
        Order order = ascending ? Order.ASC : Order.DESC;

        // 커서를 여기에서 씀
        String cursor = request.cursor();
        String after = request.idAfter();

        if (cursor != null && after != null) {
            UUID cursorId;
            try {
                cursorId = UUID.fromString(after);
            } catch(IllegalArgumentException e) {
                throw new IllegalArgumentException("[콘텐츠 데이터 관리] cursorId was something wrong" + after);
            }

            switch (sortBy) {
                case "createdAt":
                    Instant cursorDt = Instant.parse(cursor);

                    if (ascending) {
                        builder.and(qContent.createdAt.gt(cursorDt)
                                .or(qContent.createdAt.eq(cursorDt).and(qContent.id.gt(cursorId))));
                    } else {
                        builder.and(qContent.createdAt.lt(cursorDt)
                                .or(qContent.createdAt.eq(cursorDt).and(qContent.id.lt(cursorId))));
                    }
                    break;

                case "watcherCount":
                    long cursorWatch = Long.parseLong(cursor);

                    if (ascending) {
                        builder.and(qContent.watcherCount.gt(cursorWatch)
                                .or(qContent.watcherCount.eq(cursorWatch).and(qContent.id.gt(cursorId))));
                    } else {
                        builder.and(qContent.watcherCount.lt(cursorWatch)
                                .or(qContent.watcherCount.eq(cursorWatch).and(qContent.id.lt(cursorId))));
                    }
                    break;

                case "rate":
                    Double cursorRate = Double.valueOf(cursor);

                    if (ascending) {
                        builder.and(qContent.averageRating.gt(cursorRate)
                                .or(qContent.averageRating.eq(cursorRate).and(qContent.id.gt(cursorId))));
                    } else {
                        builder.and(qContent.averageRating.lt(cursorRate)
                                .or(qContent.averageRating.eq(cursorRate).and(qContent.id.lt(cursorId))));
                    }
                    break;

                default:
                    throw new IllegalArgumentException("[콘텐츠 데이터 관리] sortBy was something wrong" + sortBy);
            }
        }

        // 정렬 + limit+1
        return query.selectFrom(qContent)
                .where(builder)
                .orderBy(getOrderSpecifier(sortBy, order),
                        new OrderSpecifier<>(order, qContent.id))
                .limit(limit + 1)
                .fetch();
    }

    private OrderSpecifier<?> getOrderSpecifier(String sortBy, Order order) {
        QContent c = QContent.content;

        switch (sortBy) {
            case "createdAt":
                return new OrderSpecifier<>(order, c.createdAt);

            case "watcherCount":
                return new OrderSpecifier<>(order, c.watcherCount);

            case "rate":
                return new OrderSpecifier<>(order, c.averageRating);

            default:
                throw new IllegalArgumentException("[콘텐츠 데이터 관리] SortBy was something wrong =" + sortBy);
        }
    }

    @Override
    public long countContents(ContentCursorRequest request) {
        QContent qContent = QContent.content;
        BooleanBuilder builder = new BooleanBuilder();

        // 검색 조건만 적용
        if (request.typeEqual() != null) {
            builder.and(qContent.type.eq(request.typeEqual()));
        }

        if (request.keywordLike() != null) {
            builder.and(qContent.title.containsIgnoreCase(request.keywordLike()));
        }

        long result = query.select(qContent.count()).from(qContent).where(builder).fetchOne();

        return result;
    }
}

'Playlist > Content' 카테고리의 다른 글

TagService  (0) 2025.12.17
TagRepository  (0) 2025.12.17
ContentMapper  (0) 2025.12.17
ContentException, ErrorCode  (0) 2025.12.17
Content, DTO  (0) 2025.12.17