Indrajeet Gour
Indrajeet Gour

Reputation: 4510

Spring Jpa - play with multiple findby method in code

I have Spring boot base API on the top of my rdbms system, I do all operations but when it is about selecting the data from the system, my GET mapping endpoint has many checks which decide which findbyAttributes method should I call.

I have created multiple repository methods like findbyId, findbyIdAndName findbyIdAndNameAndAge.

But consider my scenario where I have around 30 columns in the table which the end-user may want to query at some point of time using my GET endpoint, which I have achieved but due to increasing number of request coming for each column/attributes of my entity, my code cognitive complexity increased a lot.

I know there has to be some different way than calling each findby method behind multiple if-else loops which is too bad I guess.

My code looks like this currently -

public Page<myEntity> getDataFromDatabase(
      @RequestParam(value = "id", required = false, defaultValue = "NA") String id,
      @RequestParam(value = "name", required = false, defaultValue = "NA") String name,
      @RequestParam(value = "age", required = false, defaultValue = "NA") String age,
      @RequestParam(value = "address", required = false, defaultValue = "NA") String address,
      Pageable pageable) {

    Page<myEntity> finalData = null;

    if (!id.equals("NA")
        && name.equals("NA")
        && age.equals("NA")
        && address.equals("NA")) {
      finalData = service.findById(id, pageable);

    } else if (id.equals("NA")
        && !name.equals("NA")
        && age.equals("NA")
        && address.equals("NA")) {
      finalData = service.findByName(name, pageable);
    } else if (id.equals("NA")
        && name.equals("NA")
        && !age.equals("NA")
        && address.equals("NA")) {
      finalData = service.findByAge(age, pageable);
    } else if (id.equals("NA")
        && name.equals("NA")
        && age.equals("NA")
        && !address.equals("NA")) {
      finalData = service.findByAddress(address, pageable);
    }else if (!id.equals("NA")
        && !name.equals("NA")
        && !age.equals("NA")
        && !address.equals("NA")) {
      finalData = service.findByIdAndNameAndAgeAndAddress(id,name,age,address, pageable);
    }else if (!id.equals("NA")
        && !name.equals("NA")
        && age.equals("NA")
        && address.equals("NA")) {
      finalData = service.findByIdAndName(id,name, pageable);
    }
    // few more else if as new column is being added
    // I think I did it but this will not last for long 

}

Understand if I wanted to add all the findBy attributes based on mentioned conditional logic.

If someone can help on this what would make my day shine.

Note - I read about Jpa Specification where they map like findAll method logic with multiple specifications which moreover feels like the same as each time I need to create certain condition base calling each get method.

Also, please let me know if it the right approach to go with, my idea is to write less code with best practises, so from GET endpoint all the JpaRepository findby method can be called via framework only I should not have to write multiple if-else.

Upvotes: 1

Views: 1683

Answers (2)

abi_pat
abi_pat

Reputation: 602

As suggested by @ggr, you can create a method as follows -

public Specification<Gateway> getSpecification(Map<String, String> attributeToValueMap) {
        return (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList();
            attributeToValueMap.forEach((attribute, value) -> {
                if(!StringUtils.isEmpty(value)) {
                    predicates.add(criteriaBuilder.equal(root.get(attribute), value));
                }
            });
            return query.where(criteriaBuilder.and(predicates.toArray(new Predicate[0]))).getRestriction();
        };

    }

So basically, instead of taking multiple parameters as separate variables, add them in a Map and pass that map to getSpecification() method.

Upvotes: 0

ggr
ggr

Reputation: 292

You should really consider Jpa Specification, this is probably the best solution. It will be easy to build your specifications with this method, even for 30 attributes.

You can check Hibernate Query too

First create a method like this

 public Specification<MyEntity>  myEntitySpecification(String id, String name) {
         return (root, criteriaQuery, criteriaBuilder) -> {

            List<Predicate> predicateList  = new ArrayList<>();
            if (!id.equals("NA")){
                predicateList.add(criteriaBuilder.equal(root.get("id"), id));
            }
            if (!name.equals("NA")){
                predicateList.add(criteriaBuilder.equal(root.get("name"), name));
            }

            return criteriaBuilder.and(predicateList.toArray(new Predicate[predicateList.size()]));
        };
    }

Then call it


    public Page<MyEntity> getDataFromDatabase(
            @RequestParam(value = "id", required = false, defaultValue = "NA") String id,
            @RequestParam(value = "name", required = false, defaultValue = "NA") String name,
            @RequestParam(value = "age", required = false, defaultValue = "NA") String age,
            @RequestParam(value = "address", required = false, defaultValue = "NA") String address,
            Pageable pageable) {



        Page<MyEntity> finalData = testRepository.findAll(myEntitySpecification(id, name), pageable);

return finalData ;
}

Upvotes: 1

Related Questions