繁体   English   中英

Spring Data JPA中的动态查询。 重构

[英]Dynamic queries in Spring Data JPA. Refactoring

使用Spring数据jpa和规范,我需要在spring mvc中实现过滤器/搜索功能。 后端接收一个对象(ReportTemplateBean),该对象基本上是具有某些字段的Bean,这些字段表示前端的过滤器。

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

我们有控制器

@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;
}

服务

@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"));

然后,它动态地构建查询,但是使用了我不喜欢的较长的IF链。 这是规范。

@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();
}

如您所见,该规范看起来很丑陋,此外SONAR还抱怨这种方法的循环复杂性(很好)。

所以问题是,如何将规范(IF)重构为更多的OO代码? 提前致谢。 更新 -我想使用/实现类似Spring Data JPA中的新功能( 通过示例查询 ),似乎如果您传递一个bean, ExampleMatcher类将忽略bean字段中的空值,这几乎是我在寻找的东西对于。 忽略空值和空值。

我写我的解决方案给您另一个选择,但是正如我在评论中所说,我没有使用规范,并且我很好奇是否有人知道在Spring JPA中进行动态查询的另一种方法。

你可以写与自己的查询@Query一个内部注释@Repository接口。 在您的情况下(假设ReportTemplateBean是您的Entity,并且其主键是Long类型),它将类似于:

@Repository
public interface ReportTemplateRepo extends JpaRepository<ReportTemplateBean, Long>{

    @Query("SELECT rb FROM ReportBeanTemplate rb JOIN ExampleTable et WHERE et.idTemplate = rb.id AND (:id is null OR :id = rb.id) AND (:city is null OR :city = rb.city) AND (:state is null OR :state = rb.state)")
    public List<ReportTemplateBean> findTemplates(@Param("id") Long id, @Param("city") String city, @Param("state") String state);
}

您可以添加所需的所有参数,并在调用该方法时将其传递为null。

方法调用示例(在您的服务类中):

@Autowire
ReportTemplateRepo templateRepo;

public void invocation(ReportTemplateBean template){
    List<ReportTemplateBean> templateRepo.findTemplates(
        template.getId(), template.getCity(), template.getState());
    }

这是我发现进行这种查询的唯一方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM