Spring Data JPA多个可选搜索参数和多个联接的查询

3

我目前正在使用Spring Boot和Spring Data JPA连接Oracle数据库。对于一个参数,我只需使用Spring Repository findById(Long id);,它非常好用。然而,对于搜索功能,我觉得更加复杂。在我们的情况下,用户提供了一个嵌套的JSON对象,其中包含多个可选的搜索参数(此时我无法更改他们发送数据的方式,必须通过嵌套的JSON)。以下是JSON输入对象的样式:

{
  "agent_filter": {
    "first_name": "string",
    "last_name": "string",
    "agentNumber": "string",
    "agentCode": "string"
  },
  "account": "string",
  "status": "string",
  "paid": "string",
  "amount": "string",
  "person_filter": {
    "date_of_birth": "string",
    "first_name": "string",
    "last_name": "string",
    "tax_id": "string"
  }
}

所有的搜索条件都是可选的(至少需要1个参数)

在后端,我们有以下实体:

@Entity
Account{
@OneToMany
List<PersonRole> role;

@OneToMany
List<AgentRole> role;
}

@Entity
PersonRole{
String role;

Person person;
}

@Entity
AgentRole{
String role;

Agent agent;
}

@Entity
Person{...}

@Entity
Agent{...}

为了提供搜索功能,我可以进行多个连接。我开始使用带有@Query注释的JPQL,但是必须对每个参数进行"is null or"检查,这很混乱。我开始研究其他选项,看到了关于QueryDSL、criteria和specification的内容,但不确定应该专注学习哪一个。不幸的是,我对这个主题并不了解太多,希望有人能指点我实现这个搜索的正确方向。谢谢!
3个回答

2

QueryDSL很棒!

让我给你举个例子,当我在代码中遇到一个和你非常相似的问题时,我有一堆东西需要过滤,其中很多可能为空...

顺便说一下,如果你需要高级连接,那么你可能需要直接使用QueryDSL。这些示例是针对QueryDSL 3的,所以你可能需要为QueryDSL 4进行更改。因为你提到了如何“提供搜索功能,我可以做多个连接”,所以你可能需要直接使用QueryDSL。

首先,创建一个BooleanBuilder,然后像这样操作:

BooleanBuilder builder = new BooleanBuilder();
QContent content = QContent.content;
if (contentFilter.headlineFilter == null || contentFilter.headlineFilter.trim().length() == 0) {
        // no filtering on headline as headline filter = null or blank
    } else if (contentFilter.headlineFilter.equals(Filter.NULL_STRING)) {
        // special case when you want to filter for specific null headline
        builder.and(content.label.isNull());
    } else {
        try {
            long parseLong = Long.parseLong(contentFilter.headlineFilter);
            builder.and(content.id.eq(parseLong));
        } catch (NumberFormatException e) {
            builder.and(content.label.contains(contentFilter.headlineFilter));
        }
    }
    if (contentFilter.toDate != null) {
        builder.and(content.modifiedDate.loe(contentFilter.toDate));
    }
    if (contentFilter.fromDate != null) {
        builder.and(content.modifiedDate.goe(contentFilter.fromDate));
    }

根据您是否拥有每个字段,您可以将它添加到过滤器中。

要使此功能正常工作,您需要生成查询DSL元数据 - 这是使用com.mysema.query.apt.jpa.JPAAnnotationProcessor注释处理器完成的。它会生成上面的QContent.content内容。

BooleanBuilder是Predicate的子类。


谢谢你的回答,我很快会去尝试一下。我会告诉你结果的。谢谢! - ejgreenwald
查询DSL是否适用于使用@OneToMany列表进行过滤?看着我的例子,我能否按代理商名字进行选择?即使代理商是列表中的一个? - ejgreenwald
你可能需要使用join,在构建查询时在父表和子表之间进行连接,然后在代理上添加过滤器。如果需要这样做,则无法使用JpaRepository - 必须直接使用QueryDSL。 - Michael Wiles
谢谢。当您说直接使用querydsl时,是指使用实体管理器并构建JPA查询吗?您能否举个例子呢?我非常感激您的帮助。非常感谢! - ejgreenwald
我已经发布了一个新问题,也许你可以回答一下。链接 - ejgreenwald

2

然而,使用查询DSL、条件查询和规范是一个不错的方法,但需要学习它们。

您的问题可以仅通过使用JpaRepository解决。您的AccountRepository可能正在扩展JpaRepository,而JpaRepository又扩展了QueryByExampleExecutor

QueryByExampleExecutor提供了一些方法,如findOne(Example<S> example)findAll(Example<S> example),这将根据您传递的Example对象返回结果。

创建Example很简单。

Person person = new Person();                         
person.setFirstname("Dave");                          

Example<Person> example = Example.of(person); 

这将匹配所有firstName = Dave的Person。

了解更多信息,请查看Spring Data Query by Example


感谢您的回答。Querybyexample似乎无法与列表一起使用。例如:New Person person; person.setFirstName("Bob"); New PersonRole personRole; personRole.add(person); New ArrayList<PersonRole> () list; list.add(PersonRole) 因此,使用此代码时它不会搜索人的名字。 - ejgreenwald

1

您需要使用自定义查询来创建自己的搜索查询。

@Query("select u from User u where u.firstname = :#{#customer.firstname}")
List<User> findUsersByCustomersFirstname(@Param("customer") Customer customer);

现在您可以添加任意数量的参数。

谢谢你的回答。那绝对是快速而简单的方法,也是我目前的实现方式,但由于有很多可选参数,它变得非常丑陋。正如我所提到的,我必须开始为每个参数放置或为空的语句。 - ejgreenwald

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接