我正在尝试为数据库表正确实现搜索功能。我尝试过这种方法:
控制器:
@GetMapping
public Page<TransactionDTO> find(TransactionFilterDTO filter, Pageable page) {
return searchRepository
.findTransactionsByFilter(mapper.toFilter(filter), page)
.map(mapper::toDTO);
}
Filer DTO:
public class TransactionFilterDTO {
private String name;
private Integer id;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime from;
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private LocalDateTime to;
... // getters and setter
}
搜索实现:
@Repository
public class TransactionSearchRepositoryImpl implements TransactionSearchRepository {
@Autowired
private TransactionRepository transactionRepository;
@Autowired
private TransactionSpecification specification;
@Override
public Page<Transaction> findTransactionsByFilter(TransactionFilter filter, @Nullable Pageable page) {
List<Transaction> transactions = transactionRepository
.findAll(specification.getFilter(filter));
int totalCount = transactions.size();
if(page != null) {
transactions = transactions
.stream()
.skip(page.getOffset())
.limit(page.getPageSize())
.collect(Collectors.toList());
}
return new PageImpl<>(transactions, page, totalCount);
}
}
存储库:
public interface TransactionSearchRepository {
Page<Transaction> findTransactionsByFilter(TransactionFilter filter, Pageable page);
}
是否有更好的方法来实现搜索功能?我认为这种解决方案非常难看。
答案 0 :(得分:4)
我可以推荐几种解决此任务的方法:
1)简单但不灵活:只需根据您的过滤器属性(name
,from
,to
)在控制器中使用请求参数,并在存储库中准备相应的查询,例如:
控制器:
@GetMapping("/q")
public List<ResponseDto> getAllByQuery(
@RequestParam(value = "name", required = false) String name,
@RequestParam(value = "from", required = false) @DateTimeFormat(iso = ISO.DATE) LocalDate from,
@RequestParam(value = "to", required = false) @DateTimeFormat(iso = ISO.DATE) LocalDate to,
Pageable pageable
) {
return service.getByQuery(name, from, to, pageable);
}
服务:
public Page<ResponseDto> getByQuery(String name, LocalDate from, LocalDate to, Pageable pageable) {
return repo.getByQuery(name, from, to, pageable).map(mapper::toResponseDto);
}
存储库:
@Query("select m from MyEntity m where " +
"(?1 is null or upper(m.name) like concat('%', upper(?1), '%')) " +
"and (?2 is null or m.createdAt >= ?2) " +
"and (?3 is null or m.createdAt <= ?3)")
Page<MyEntity> getByQuery(String name, final LocalDate from, final LocalDate to, final Pageable pageable);
然后执行请求:
GET http://localhost:8080/q?name=john&from=2019-04-19&to=2019-04-19
2)使用 QueryDsl 。您应该将其添加到项目中(可以找到详细信息here),从QuerydslPredicateExecutor
和QuerydslBinderCustomizer
扩展存储库,并向其中添加一些“调整”:
public interface MyEntityRepo extends JpaRepository<MyEntity, Integer>, QuerydslPredicateExecutor<MyEntity>, QuerydslBinderCustomizer<QMyEntity> {
@Override
default void customize(@NonNull QuerydslBindings bindings, @NonNull QMyEntity entity) {
// Make case-insensitive 'like' filter for all string properties
bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
// Add 'between' and 'greater or equal' filter date property
bindings.bind(entity.createdAt).all((path, value) -> {
Iterator<? extends LocalDate> it = value.iterator();
LocalDate from = it.next();
if (value.size() >= 2) {
LocalDate to = it.next();
return Optional.of(path.between(from, to)); // between
} else {
return Optional.of(path.goe(from)); // greater than or equal
}
});
}
添加服务方法:
public Page<ResponseDto> getAllByQueryDsl(Predicate predicate, Pageable pageable) {
return repo.findAll(predicate, pageable).map(mapper::toResponseDto);
}
添加控制器方法:
@GetMapping("/query-dsl")
public Page<ResponseDto> getAllByQueryDsl(
@QuerydslPredicate(root = MyEntity.class, bindings = MyEntityRepo.class) Predicate predicate,
Pageable pageable
) {
return service.getAllByQueryDsl(predicate, pageable);
}
并在实体的“日期”属性中添加@DateTimeFormat
注释:
@Entity
public class MyEntity {
// ...
@DateTimeFormat(iso = ISO.DATE) private LocalDate createdAt;
}
然后您可以执行这样的请求:
GET http://localhost:8080/query-dsl?name=john&createdAt=2019-04-15&createdAt=2019-04-19
第一个日期是“ from”参数,第二个日期是“ to”参数。如果您仅使用一个日期-它将是'from'参数(大于或等于)。
3)使用 specification-arg-resolver 库。将其添加到您的项目中(请参阅说明:1和2),然后从JpaSpecificationExecutor
扩展存储库:
public interface MyEntityRepo extends JpaRepository<MyEntity, Integer>, JpaSpecificationExecutor<MyEntity> {}
将这样的方法添加到您的控制器中:
@GetMapping("/specification")
public Page<ResponseDto> getAllBySpecification(
@And({
@Spec(path = "name", spec = LikeIgnoreCase.class),
@Spec(path = "createdAt", params = "from", spec = GreaterThanOrEqual.class),
@Spec(path = "createdAt", params = "to", spec = LessThanOrEqual.class)
}) Specification<MyEntity> specification,
Pageable pageable
) {
return service.getAllBySpecification(specification, pageable);
}
更新您的服务:
public Page<ResponseDto> getAllBySpecification(final Specification<MyEntity> specification, final Pageable pageable) {
return repo.findAll(specification, pageable).map(mapper::toResponseDto);
}
然后请求您的数据:
GET http://localhost:8080/specification?name=john&from=2019-04-10&to=2019-04-19
4)手动建立规范:
创建过滤器类:
@Data
public class MyFilter implements Specification<MyEntity> {
private String name;
@DateTimeFormat(iso = ISO.DATE) private LocalDate from;
@DateTimeFormat(iso = ISO.DATE) private LocalDate to;
@Override
public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
List<Predicate> predicates = new ArrayList<>();
if (name != null) predicates.add(builder.like(builder.upper(root.get("name")), "%" + name.toUpperCase() + "%"));
if (from != null) predicates.add(builder.greaterThanOrEqualTo(root.get("createdAt"), from));
if (to != null) predicates.add(builder.lessThanOrEqualTo(root.get("createdAt"), to));
return builder.and(predicates.toArray(new Predicate[0]));
}
}
创建控制器方法:
@GetMapping("/filter")
public Page<ResponseDto> getAllByMyFilter(MyFilter filter, Pageable pageable) {
return service.getAllBySpecification(filter, pageable);
}
然后运行请求:
GET http://localhost:8080/filter?name=john&from=2019-04-10&to=2019-04-19
答案 1 :(得分:2)
看看query by example。您只需使用
就可以摆脱一些代码transactionRepository.findAll(Example.of(transaction));
它还支持分页和更高级的API检查ExampleMatcher
类。
答案 2 :(得分:0)
有几种方法可以解决此问题。您也可以实现以下解决方案: 例如 在您的存储库中:
@Query(value="FROM Students AS student "
+ " LEFT JOIN FETCH student.school AS school"
.......................
...................
+ " WHERE LOWER(student.name) LIKE LOWER(CONCAT('%',:criteria, '%')) OR"
+ " LOWER(student.code) LIKE LOWER(CONCAT('%',:criteria, '%')) OR"
.........................
.........................
.........................
+ " LOWER(school.name) LIKE LOWER(CONCAT('%',:criteria, '%')) "
+ " ORDER BY student.name ASC",
countQuery=" SELECT COUNT(student) from Students AS student ")
public Page<Students> getAllStudentsBySearchCriteria(@Param("criteria")String search, Pageable pageable);
为您服务:
public Page<Students> getAll(Page pagable) {
return studentRepository.getAllStudentsBySearchCriteria(searchString, pageable);
}