[英]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 語句。 您可以使用and
和or
語句輕松組合多個條件字段。
我過去如何處理類似的事情是使用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.