BooleanExpression은 QueryDSL에서 논리 조건을 표현하는 타입 클래스이다.
where 절에서 사용가능하고 동적 조건조합을 지원
조건을 동적으로 제어가 가능하기때문에 특정 조건으로 서치를 할때사용한다.
여러 조건이 사용가능하다 and, or, not 등등..
https://developer-backend.tistory.com/105
2024-11-13 동적쿼리 OrderSpecifier
심화 프로젝트를 진행하면서 동적쿼리에 대한 정리와 동적쿼리 중 정렬(orderBy)에 사용되는 OrderSpecifier을 사용하여 공통 코드를 작성해보려고 한다. 동적쿼리란 사용자의 입력에 따라 조건이나
developer-backend.tistory.com
정렬 조건 코드에서 조금수정해서 테스트 환경 구성
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Entity
@Table(
name = "p_store"
)
public class Store extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "id")
private UUID id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@Column(name = "name", nullable = false)
private String name;
@OneToMany(mappedBy = "store",fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<StoreCategory> storeCategory = new ArrayList<>();
@Column(name = "address", nullable = false)
private String address;
@Column(name = "call_number", nullable = false)
private String callNumber;
@Column(name = "store_grade", nullable = false)
private double storeGrade;
@Column(name = "store_grade_reviews", nullable = false)
private int storeGradeReviews;
}
@Getter
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseTimeEntity {
@CreatedDate
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;
@Setter
@Column(name = "deleted_at")
private LocalDateTime deletedAt;
@Setter
@CreatedBy
@Column(name = "created_by", updatable = false)
private UUID createdBy;
@LastModifiedBy
@Column(name = "updated_by")
private UUID updatedBy;
@Setter
@Column(name = "deleted_by")
private UUID deletedBy;
}
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v1")
@Tag(name = "매장 관련 API")
public class StoreController {
private final StoreService storeService;
@GetMapping("/stores/test")
public ResponseEntity<CommonResponse<Slice<TestResponse>>> testStoreGradeQuery(
@RequestParam(value = "storeName", required = false) String storeName,
Pageable pageable
){
return CommonResponse.success(SuccessCode.SUCCESS,
storeService.testStoreGradeQuery(pageable, storeName));
}
}
@Getter
@AllArgsConstructor
public class TestResponse {
private final UUID storeId;
private final String storeName;
private final double storeGrade;
}
@Service
@RequiredArgsConstructor
public class StoreService {
private final StoreJpaRepository storeJpaRepository;
private final StoreQueryRepository storeQueryRepository;
@Transactional(readOnly = true)
public Slice<TestResponse> testStoreGradeQuery(Pageable pageable, String storeName){
return storeQueryRepository.testStoreQuery(pageable, storeName);
}
}
@Repository
@RequiredArgsConstructor
public class StoreQueryRepository {
private final JPAQueryFactory jpaQueryFactory;
QStore qStore = QStore.store;
public Slice<TestResponse> testStoreQuery(Pageable pageable, String storeName){
List<TestResponse> testResponseList = jpaQueryFactory.query()
.select(
Projections.constructor(
TestResponse.class,
qStore.id,
qStore.name,
qStore.storeGrade
)
).from(qStore)
.where(storeNameEqual(storeName))
.orderBy(getOrderSpecifierStore(pageable))
.limit(pageable.getPageSize() + 1)
.fetch();
boolean hasNext = testResponseList.size() > pageable.getPageSize();
if (hasNext) {
testResponseList.remove(testResponseList.size() - 1);
}
return new SliceImpl<>(testResponseList, pageable, hasNext);
}
private BooleanExpression storeNameEqual(String storeName){
return storeName != null ? QStore.store.name.eq(storeName) : null;
}
private OrderSpecifier<?>[] getOrderSpecifierStore(Pageable pageable) {
return pageable
.getSort()
.stream()
.map(order -> {
PathBuilder pathBuilder = new PathBuilder<>(QStore.store.getType(), QStore.store.getMetadata());
return new OrderSpecifier(
order.isAscending() ? Order.ASC : Order.DESC,
pathBuilder.get(order.getProperty()));
}).toArray(OrderSpecifier[]::new);
}
}
매장 평점이 1, 2, 3 인 매장이 있다고 가정하고 평점으로 정렬 테스트를 진행
피자 1호점과 2호점에 대한 서치 결과
만약 서치 조건이 필요하지않다면 아래 결과와 같이 전체가 나오게 된다.
특정 조건에 대해 서치를 하기위해서 BooleanExpression에 대해서 간단하게 테스트를 진행해봤을때 뭔가 공통메서드를 작성해볼려고 노력했으나 특정조건의 타입등의 고려사항이 많기 때문에 제네릭 타입별로 코드를 작성하면 조건마다 메서드를 사용하지 않고 사용할 수 있을것 같단 생각이 들어 다음에 한번 구현을 도전해 보려고 한다.
'심화 캠프 정리' 카테고리의 다른 글
ModelAttribute (0) | 2024.11.20 |
---|---|
PageableArgumentResolver (0) | 2024.11.20 |
동적쿼리 OrderSpecifier (0) | 2024.11.20 |
PreAuthorize (2) | 2024.11.19 |
생성자 수정자 자동으로 생성하기 (0) | 2024.11.19 |