Specification with Spring Data Jpa passing null parameter

1

I'm using Specifications Spring Data JPA, but I'm having a hard time! By not passing any parameters in the two fields I want it to return all records to me. But when doing the query it is returning me only those that have the respective two null / empty fields in the bank!

      public class SistemaSpecs {

public static Specification<Sistema> descricao(String descricao) {
    return (root, query, builder) -> 
      builder.or(builder.equal(root.get(Sistema_.descricao),  descricao));
     }
public static Specification<Sistema> sigla(String sigla) {
    return (root, query, builder) -> 
      builder.or(builder.equal(root.get(Sistema_.sigla),sigla));
     }
  }

The method that calls and makes a query is this one.

 public Page<Sistema> pesquisar(Sistema sistema, Pageable pageable) {
    Specifications<Sistema> where = 
         where(SistemaSpecs.descricao(sistema.getDescricao()))
            .or(SistemaSpecs.sigla(sistema.getSigla()));
     return sistemaRepository.findAll(where, pageable);
 }

How can I retrieve all the records when the last parameter is null, without second the same structure?

    
asked by anonymous 28.02.2018 / 13:50

2 answers

0

You have to put a check inside your function to not perform the search when the parameters are null.

public Page<Sistema> pesquisar(Sistema sistema, Pageable pageable) {
     if(sistema.getDescricao() != null && sistema.getSigla() != null){
          Specifications<Sistema> where = 
          where(SistemaSpecs.descricao(sistema.getDescricao()))
             .or(SistemaSpecs.sigla(sistema.getSigla()));
          return sistemaRepository.findAll(where, pageable);
    }else{
          return sistemaRepository.findAll();
    }
}

I hope I have helped.

    
28.02.2018 / 14:00
0

In the class SystemSpecs that you created each method represents a criterion for your query, there it is the responsibility of the user of the class to perform the checks, and as you commented, ends up generating a lot of code if I have to repeat the validations everywhere that I will need to make this query.

My suggestion is that instead of creating a class with the query criteria methods, you create a class that implements Specification and within this class you define your query clauses by putting the necessary validations, hence your validations will be in one only place.

Although I do not know the code of your System class I will create a random example to illustrate this situation:

We have the following Entity class for states (São Paulo, Rio de Janeiro, etc.):

@Entity
    public classe Estado Serializable{

        private static final long serialVersionUID = 1L;

        private Long id;
        private String nomeEstado;
        private String sigla;

        //Contrutores, Gets e Sets

From this class you will create your repository (I am assuming you are using this approach), whereas the repository should inherit the JpaSpecificationExecutor related entity, in this case the State class.

@Repository
public interface EsdadoRepository extends JpaRepository<Estado,Long >, JpaSpecificationExecutor<Estado> { 
    }

Great, our scenery ready! Now comes the creation of a class that implements the interface Specification regarding our state entity. It is in this class that we will put our criteria and field validations.

public class EstadoSpecification implements Specification<Estado> {

        private static final long serialVersionUID = 1L;

        private String nomeEstado;
        private String sigla;

        List<Predicate> predicates = new ArrayList<>();

    public EstadoSpecification(String nomeEstado,String sigla) {
        super();
        this.nomeEstado = nomeEstado;
        this.estado = sigla;
    }

    @Override
    public Predicate toPredicate(Root<ItemVendedor> root, CriteriaQuery<?> query, CriteriaBuilder builder) {

        if (!nomeEstado.isEmpty()) {
            predicates.add(builder.like(builder.upper(root.get("nomeEstado")), "%" + nomeEstado.toUpperCase() + "%"));; 
        }

        if(!sigla.isEmpty()) {
           predicates.add(builder.like(builder.upper(root.get("sigla")), "%" + sigla.toUpperCase() + "%"));;
        }   
        return builder.and(predicates.toArray(new Predicate[1]));
        }
}

Then, within a method of querying the state in your service class, you inform the query parameters (description and state) as follows:

    public List<Estado> buscarEstados(String nomeEstado, String sigla) {

            Specification<Estado> spec = new EstadoSpecification(nomeEstado,estado); 

            List<Estado> lista = EstadoRepository.findAll(spec);

    return lista;
}

The query will use as a criterion only the field that you fill, if you omit both, it will bring all states.

This is what I understood from your post, a way to create a dynamic query in which I as the query user can omit some parameter value without this generating an error. I hope I have helped.

    
23.05.2018 / 00:51