Mit Spring Daten jpa und Spezifikationen arbeiten, muss ich eine Filter/Suche-Funktion in Spring mvc implementieren. Das Backend empfängt ein Objekt (ReportTemplateBean), bei dem es sich im Wesentlichen um eine Bean mit einigen Feldern handelt, die die Filter im Frontend darstellen.Dynamische Abfragen in Spring Data JPA. Refactoring
public class ReportTemplateBean implements Serializable {
private static final long serialVersionUID = -3915391620260021813L;
private Long id;
private String property;
private String city;
private String state;
private String zipCode;
private String propertyStatus;
private String realEstateRep;
//more code
Wir haben den Controller
@RequestMapping(value = "/search", method = RequestMethod.GET)
@ResponseBody
public ReportBean search(@AuthenticationPrincipal ActiveUser activeUser,
@ModelAttribute("templateForm") ReportTemplateBean template,
Pageable pageable) throws GenericException {
LOGGER.info("Pulling report requested");
ReportBean report = reportService.searchProperties(template,
pageable.getPageNumber(), pageable.getPageSize());
return report;
}
Der Service
@Override
@Transactional(readOnly = true, timeout = 20)
public ReportBean searchProperties(ReportTemplateBean template,
Integer pageNumber, Integer pageSize) throws GenericException,
TransactionTimedOutException {
LOGGER.info("searchProperties({})", template);
try {
// pageNumber = (pageNumber == null ? 0 : pageNumber);
// pageSize = (pageSize == null ? 10 : pageSize);
ReportTemplate t = reportTemplateMapper.beanToEntity(template);
List<PropertyBean> beans = new ArrayList<PropertyBean>();
PropertySpecification spec = new PropertySpecification(t);
Page<Property> properties = propertyRepository.findAll(spec,
new PageRequest(pageNumber, pageSize, Sort.Direction.ASC,
"name"));
Und dann baut er die Abfrage dynamisch, aber eine lange IF-Kette mit, dass ich es nicht mögen. Dies ist die Spezifikation.
@SuppressWarnings("unchecked")
@Override
public Predicate toPredicate(Root<Property> root, CriteriaQuery<?> query,
CriteriaBuilder cb) {
Path<String> propertyName = root.get(Property_.name);
Path<String> city = root.get(Property_.city);
Path<String> state = root.get(Property_.state);
Path<String> zipCode = root.get(Property_.zipCode);
final List<Predicate> orPredicates = new ArrayList<Predicate>();
final List<Predicate> andPredicates = new ArrayList<Predicate>();
if (template.getProperty() != null
&& template.getProperty().length() > 0) {
andPredicates.add(cb.equal(propertyName, template.getProperty()));
}
if (template.getCity() != null && template.getCity().length() > 0) {
andPredicates.add(cb.equal(city, template.getCity()));
}
if (template.getState() != null && template.getState().length() > 0) {
andPredicates.add(cb.equal(state, template.getState()));
}
if (template.getZipCode() != null && template.getZipCode().length() > 0) {
andPredicates.add(cb.equal(zipCode, template.getZipCode()));
}
if (template.getRealEstateRep() != null) {
Join<Property, User> pu = null;
if (query.getResultType().getName().equals("java.lang.Long")) {
pu = (Join<Property, User>) root.fetch(Property_.createdBy);
} else {
pu = root.join(Property_.createdBy);
}
Path<Long> userId = pu.get(User_.id);
andPredicates.add(cb.equal(userId, template.getRealEstateRep()));
}
if (template.getProjectType() != null
&& template.getProjectType().length() > 0) {
Join<Property, Project> pp = null;
if (query.getResultType().getName().equals("java.lang.Long")) {
pp = root.join(Property_.projects);
} else {
pp = (Join<Property, Project>) root.fetch(Property_.projects);
}
Path<String> projectType = pp.get(Project_.projectName);
andPredicates.add(cb.equal(projectType, template.getProjectType()));
}
//more IF's
return query.getRestriction();
}
Wie Sie die Spezifikation feststellen kann, scheint hässlich und neben diesem SONAR beschwert sich über die zyklomatische Komplexität dieser Methode (was gut ist).
Also Frage ist, wie kann ich die Spezifikation (IFs) zu mehr OO-Code? Refaktor. Vielen Dank im Voraus. UPDATE - Ich möchte so etwas wie die neue Funktion in Spring Data JPA (Query by Example) Es scheint, verwenden/implementieren, wenn Sie eine Bohne die ExampleMatcher Klasse übergeben wird den Nullwert in den Bohnenfelder ignorieren, die ist fast, was Ich suche nach. Ignoriere Null- und Leerwerte.
Möchten Sie eine Lösung nur mit Spezifikation? Ich hatte ein ähnliches Problem, aber ich löste es Schreiben der Abfrage mit @ Query Annotation mit optionalen Parametern in meinem Repository – amicoderozer
Wäre schön mit Spezifikation, aber nicht zwingend, kann ich einen Blick auf Ihre Lösung? – TheProgrammer