daangn-daangn / daangn-server

🥕당근 서버 리포지토리🥕
4 stars 2 forks source link

물품 API 개발 #29

Closed cotchan closed 2 years ago

cotchan commented 2 years ago

물품 API 개발

목차

  1. 예상 기능과 관련된 이슈
    • 이슈1: 물품 리스트 조회를 위한 동적 쿼리
    • 이슈2: Entity와 QueryDto 분리
  2. 이슈 해결방법
    • 이슈1: 물품 리스트 조회를 위한 동적 쿼리
    • 이슈1: 물품 리스트 조회를 위한 동적 쿼리

예상 기능과 관련된 이슈

이슈1: 물품 리스트 조회를 위한 동적 쿼리

이슈2: Entity와 QueryDto 분리

이슈 해결방법

이슈1: 물품 리스트 조회를 위한 동적 쿼리

@RequiredArgsConstructor
@Repository
public class ProductQueryRepository {
    public List<ProductQueryDto> findAll(ProductSearchOption productSearchOption, String address, Pageable pageable) {

        return jpaQueryFactory
                .select(
                    Projections.constructor(ProductQueryDto.class,
                        product.id,
                        product.title,
                        product.location.address,
                        product.price,
                        product.thumbNailImage,
                        product.createdAt,
                        favoriteProduct.id.count()
                    )
                ).from(product)
                    .join(product.category, category)
                    .leftJoin(favoriteProduct).on(product.id.eq(favoriteProduct.product.id))
                                            .on(favoriteProduct.isValid.eq(true))
                .where(
                    categoriesEq(productSearchOption.getCategories()),
                    titleContains(productSearchOption.getTitle()),
                    addressContains(address),
                    priceCondition(productSearchOption.getMinPrice(), productSearchOption.getMaxPrice()),
                    product.productState.notIn(ProductState.HIDE, ProductState.DELETED)
                )
                .groupBy(product.id, product.title)
                .orderBy(product.updatedAt.desc())
                    .limit(pageable.getPageSize())
                    .offset(pageable.getOffset())
                .fetch();
    }

    private BooleanExpression categoriesEq(List<Long> categories) {
        if (isEmpty(categories)) {
            return null;
        }
        return product.category.id.in(categories);
    }

    private BooleanExpression titleContains(String title) {
        if (isBlank(title)) {
            return null;
        }
        return product.title.contains(title);
    }

    private BooleanExpression addressContains(String address) {
        if (isBlank(address)) {
            return null;
        }
        return product.location.address.contains(address);
    }

    private BooleanExpression priceCondition(long minPrice, long maxPrice) {
        final long UN_USED_VALUE = -1L;

        if (minPrice == UN_USED_VALUE && maxPrice == UN_USED_VALUE) {
            return null;
        } else if (minPrice == UN_USED_VALUE) {
            return product.price.loe(maxPrice);
        } else if (maxPrice == UN_USED_VALUE) {
            return product.price.goe(minPrice);
        }

        return product.price.between(minPrice, maxPrice);
    }
}

이슈2: Entity와 QueryDto 분리

ProductQueryDto를 선언하고 repository layer에 둠으로써 common 모듈과 api-server 모듈에서 모두 사용할 수 있도록 처리

@Getter
@AllArgsConstructor
public class ProductQueryDto {
    private Long id;
    private String title;
    private String location;
    private Long price;
    private String imageUrl;
    private LocalDateTime createdAt;
    private Long favoriteCount;
}
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "products")
public class Product extends AuditingCreateUpdateEntity {

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

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "seller_id")
    private User seller;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "buyer_id")
    private User buyer;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id")
    private Category category;

    @Column(nullable = false, length = 50)
    private String name;

    @Column(nullable = false)
    private long price;

    @Column(nullable = false, length = 100)
    private String title;

    @Column(nullable = false, length = 100)
    private String description;

    @Column(nullable = false)
    private Location location;

    @Column(nullable = false)
    private ProductState productState;

    @Column(length = 250)
    private String thumbNailImage;

    @Column(nullable = false)
    private int refreshCnt;

    @OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
    private List<ProductImage> productImages = new ArrayList<>();

    //...
}