简体   繁体   中英

Spring Data JPA Multiple Optional Search Parameters With Multiple Joins

I'm currently using Spring Boot with Spring Data JPA to connect to an oracle database. With one parameter I just use the Spring Repository findById(Long id); and it works great. Search on the other hand is much more complicated for me. In our case, the user provides a nested JSON object with multiple optional search parameters (at this point I can't change the way the way they send their data it has to be through the nested JSON). Here is what the JSON input object looks like:

{
  "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"
  }
}

All the search criteria are optional (except at least 1 parameter)

On the back-end we have the following entities:

@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{...}

So to provide the search functionality I can do multiple joins. I started using JPQL with an @Query notation but I had to do is null or checks with each parameter and it's a big mess. I started looking into other options and I saw stuff about QueryDSL, criteria, specification but I wasn't sure which one I should focus on and learn about. Unfortunately I don't know a whole lot on this subject and I was hoping someone can point me in the right direction for a good implementation of this search. Thank you!

QueryDSL ftw!

Let me give you an example from my code when I had a very similar problem to you in that I had a bunch of stuff that I wanted to filter on and a lot of them could be null...

Btw, if you need fancy joins then you're probably going to use query dsl directly. These example are for QueryDSL 3 so you might have to change for QueryDSL 4. Because you've mentioned how you 'So to provide the search functionality I can do multiple joins' you're probably going to need to use QueryDSL directly.

First you create yourself and BooleanBuilder and then do something like this:

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

So based on whether or not you have each field you can add it to the filter.

To get this to work you're going to need to generate the Query DSL meta data - that is done with the com.mysema.query.apt.jpa.JPAAnnotationProcessor annotation processor. It generates the QContent.content stuff above.

That BooleanBuilder is a subclass of Predicate.

However going with query dsl, criteria, specification is good approach but it will require to learn them.

Your problem can just be solved using JpaRepository only. Your AccountRepository might be extending JpaRepository which again extends QueryByExampleExecutor .

QueryByExampleExecutor provides some method like findOne(Example<S> example) and findAll(Example<S> example) which will give you result back based on the Example object you pass.

Creating Example is simple

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

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

This will match all Person which have firstName = Dave

Read more on Spring Data Query by Example .

You need to use custom query to create your own search query .

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

Now you can add as many param as you want

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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