簡體   English   中英

如何在Java中使用Criteria API進行動態搜索?

[英]How to dynamic search with Criteria API in Java?

我想在 Java 中使用條件 API 進行動態搜索。

在我寫的代碼中,我們需要在JSON中寫url欄中的每個實體。我不想寫“plaka”。

URL: < localhost:8080/api/city/query?city=Ankara&plaka= > 我只想“城市”或“普拉卡”

在這里我們需要寫下每個實體,即使我們只搜索 1 個實體。 鍵入實體,它應該是空的。

我的代碼如下。 假設有多個實體,我想做的是使用它想要搜索的單個實體進行搜索。 正如您在照片中看到的,我不想編寫一個我不需要的實體。 你能幫我做什么嗎?

我在存儲庫中的代碼

public interface CityRepository extends JpaRepository<City, Integer> , JpaSpecificationExecutor<City> {

}

我的服務代碼

@Service
public class CityServiceImp implements CityService{


private static final String CITY = "city";
private static final String PLAKA = "plaka";

@Override
public List<City> findCityByNameAndPlaka(String cityName, int plaka) {
    GenericSpecification genericSpecification = new GenericSpecification<City>();
    if (!cityName.equals("_"))
        genericSpecification.add(new SearchCriteria(CITY,cityName, SearchOperation.EQUAL));
    if (plaka != -1)
        genericSpecification.add(new SearchCriteria(PLAKA,plaka, SearchOperation.EQUAL));

    return cityDao.findAll(genericSpecification);
}

@Autowired
CityRepository cityDao;

我的代碼在 Controller

@RestController
@RequestMapping("api/city")
public class CityController {

@Autowired
private final CityService cityService;

public CityController(CityService cityService) {
    this.cityService = cityService;


@GetMapping("/query")
public List<City> query(@RequestParam String city, @RequestParam String plaka){

    String c = city;
    int p;

    if (city.length() == 0)
        c = "_";

    if (plaka.length() == 0) {
        p = -1;
    }
    else
        p = Integer.parseInt(plaka);

    return cityService.findCityByNameAndPlaka(c,p);
}

我在 SearchCriteria 中的代碼

public class SearchCriteria {

private String key;
private Object value;
private SearchOperation operation;

public SearchCriteria(String key, Object value, SearchOperation operation) {
    this.key = key;
    this.value = value;
    this.operation = operation;
}

public String getKey() {
    return key;
}

public Object getValue() {
    return value;
}

public SearchOperation getOperation() {
    return operation;
}

我在 GenericSpecification 中的代碼

public class GenericSpecification<T> implements Specification<T> {

private List<SearchCriteria> list;

public GenericSpecification() {
    this.list = new ArrayList<>();
}

public void add(SearchCriteria criteria){
    list.add(criteria);
}

@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

    List<Predicate> predicates = new ArrayList<>();

    for (SearchCriteria criteria : list) {
        if (criteria.getOperation().equals(SearchOperation.GREATER_THAN)) {
            predicates.add(builder.greaterThan(
                    root.get(criteria.getKey()), criteria.getValue().toString()));
        } else if (criteria.getOperation().equals(SearchOperation.LESS_THAN)) {
            predicates.add(builder.lessThan(
                    root.get(criteria.getKey()), criteria.getValue().toString()));
        } else if (criteria.getOperation().equals(SearchOperation.GREATER_THAN_EQUAL)) {
            predicates.add(builder.greaterThanOrEqualTo(
                    root.get(criteria.getKey()), criteria.getValue().toString()));
        } else if (criteria.getOperation().equals(SearchOperation.LESS_THAN_EQUAL)) {
            predicates.add(builder.lessThanOrEqualTo(
                    root.get(criteria.getKey()), criteria.getValue().toString()));
        } else if (criteria.getOperation().equals(SearchOperation.NOT_EQUAL)) {
            predicates.add(builder.notEqual(
                    root.get(criteria.getKey()), criteria.getValue()));
        } else if (criteria.getOperation().equals(SearchOperation.EQUAL)) {
            predicates.add(builder.equal(
                    root.get(criteria.getKey()), criteria.getValue()));
        } else if (criteria.getOperation().equals(SearchOperation.MATCH)) {
            predicates.add(builder.like(
                    builder.lower(root.get(criteria.getKey())),
                    "%" + criteria.getValue().toString().toLowerCase() + "%"));
        } else if (criteria.getOperation().equals(SearchOperation.MATCH_END)) {
            predicates.add(builder.like(
                    builder.lower(root.get(criteria.getKey())),
                    criteria.getValue().toString().toLowerCase() + "%"));
        }
    }

    return builder.and(predicates.toArray(new Predicate[0]));
}

我在 SearchOperation 中的代碼

public enum SearchOperation {

GREATER_THAN,
LESS_THAN,
GREATER_THAN_EQUAL,
LESS_THAN_EQUAL,
NOT_EQUAL,
EQUAL,
MATCH,
MATCH_END,
}

Criteria API 的好處是您可以使用 CriteriaBuilder 根據您擁有的字段構建復雜的 SQL 語句。 您可以使用andor語句輕松組合多個條件字段。

我過去如何處理類似的事情是使用GenericDao class,它采用具有最常見操作(等於、qualsIgnoreCase、lessThan、greaterThan 等)構建器的Filter 我實際上在我開始的一個開源項目中有類似的東西: https://gitlab.com/pazvanti/logaritmical/-/blob/master/app/data/dao/GenericDao.java https://gitlab.com/pazvanti /logaritmical/-/blob/master/app/data/filter/JPAFilter.java

接下來,隱式 DAO class 擴展了這個GenericDao ,當我想做一個操作時(例如:用提供的用戶名找到一個用戶),我在那里創建了一個Filter

現在,魔法就在過濾器中。 這是創建Predicate的那個。 在您的請求中,您將收到類似這樣的信息:field1=something&field2=somethingElse 等等。 如果您想要更小或更大,並且使用這些值初始化過濾器,則可以在該值之前加上“<”或“>”。 如果您可以將參數檢索為 Map<String, String>,那就更好了。

現在,對於請求中的每個字段,您使用JPAFilter class 中的輔助方法創建一個謂詞,並返回結果Predicate 在下面的示例中,我假設您沒有將它作為 Map,而是作為單獨的字段(很容易為地圖調整代碼):

public class SearchFilter extends JPAFilter {
    private Optional<String> field1 = Optional.empty();
    private Optional<String> field2 = Optional.empty();

    @Override
    public Predicate getPredicate(CriteriaBuilder criteriaBuilder, Root root) {
        Predicate predicateField1 = field1.map(f -> equals(criteriaBuilder, root, "field1", f)).orElse(null);
        Predicate predicateField2 = field2.map(f -> equals(criteriaBuilder, root, "field2", f)).orElse(null);

        return andPredicateBuilder(criteriaBuilder, predicateField1, predicateField2);
    }
}

現在,我將字段作為可選字段,因為在這種情況下我假設您在請求映射中將它們作為可選字段(Spring 有這個)並且我知道將可選字段作為輸入參數有點爭議,但在這種情況下我認為這是可以接受的(這里有更多信息: https://petrepopescu.tech/2021/10/an-argument-for-using-optional-as-input-parameters/

andPredicateBuilder()的制作方式是,即使提供的謂詞之一是 null,它也能正常工作。此外,我制作了簡單的映射 function,調整以包括<>

現在,在您的 DAO class 中,只需提供適當的過濾器:

public class SearchDao extends GenericDAO {

    public List<MyEntity> search(Filter filter) {
       return get(filter);
    }

}

需要進行一些調整(這只是入門代碼),例如更簡單的方法來初始化過濾器(並在 DAO 中執行此操作)並確保過濾器只能應用於指定的實體(可能使用 generics, JPAFIlter<T>並讓SearchFilter extends JPAFilter<MyEntity> )。 此外,還可以添加一些錯誤處理。

一個缺點是字段必須與實體 class 中的變量名稱相匹配。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM