Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass an "filter" object to spring data jpa repository to create a query

I need to pass an object to a spring boot endpoint (in the @RequestBody) and use that object to query the database using spring data jpa.

My Class

@Entity(name = "PESSOA")
public class Pessoa {

private Long codigo;
private String nome;
private String cpf;
private String estadoCivil;
private LocalDate dataNascimento;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Long getCodigo() {
    return codigo;
}

@Column
public String getNome() {
    return nome;
}

@Column
public String getCpf() {
    return cpf;
}

@Column
public String getEstadoCivil() {
    return estadoCivil;
}

@Column
public LocalDate getDataNascimento() {
    return dataNascimento;
}

public void setCpf(String cpf) {
    this.cpf = cpf;
}

public void setEstadoCivil(String estadoCivil) {
    this.estadoCivil = estadoCivil;
}

public void setDataNascimento(LocalDate dataNascimento) {
    this.dataNascimento = dataNascimento;
}

public void setCodigo(Long codigo) {
    this.codigo = codigo;
}

public void setNome(String nome) {
    this.nome = nome;
}

}

This is the class that contains my endpoints:

@RestController
public class PessoaService {

@Autowired
private PessoaRepository pessoaRepository;

@GetMapping("/pessoa")
public List<Pessoa> all() {
    return pessoaRepository.findAll();
}


@GetMapping("/pessoa/{id}")
public Pessoa get(@PathVariable Long id) {
    return pessoaRepository.findById(id).get();
}

@PostMapping("/pessoa")
public Pessoa create(@RequestBody Pessoa pessoa) {
    return pessoaRepository.saveAndFlush(pessoa);
}

@PutMapping("/pessoa/{id}")
public Pessoa update(@PathVariable Long id, @RequestBody Pessoa pessoa) {
    Pessoa pessoaExistente = pessoaRepository.findById(id).get();
    BeanUtils.copyProperties(pessoa, pessoaExistente);
    return pessoaRepository.saveAndFlush(pessoaExistente);
}

@DeleteMapping("/pessoa/{id}")
public Pessoa delete(@PathVariable Long id) {
    Pessoa pessoaExistente = pessoaRepository.findById(id).get();
    pessoaRepository.delete(pessoaExistente);
    return pessoaExistente;
}

}

This is my repository:

@Repository
public interface PessoaRepository extends JpaRepository<Pessoa, Long> {

}

There is a way to create something like:

@GetMapping("/pessoa")
public List<Pessoa> filter(@RequestBody PessoaFilter filter) {
    return pessoaRepository.findByFilter(filter);
}

Spring data provides something that can be used like a filter?

like image 591
Gunter Amorim Avatar asked Aug 30 '18 17:08

Gunter Amorim


1 Answers

You can do this with specifications. See this tutorial on Spring's blog.

What you are calling PessoaFilter gets passed into the query method as a predicate, once you add an interface to the repository that accepts the filtering version of the method.

Implement this interface:

public interface Specification<T> {
    Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);
}

which could be implemented here like:

public interface PessoaSpecifications {

    static <Pessoa> Specification<Pessoa> byCpf(String someCpf) {
        return new Specification<Pessoa> {
            public Predicate toPredicate(Root<Pessoa> root, CriteriaQuery query, CriteriaBuilder cb) {
                return cb.equal(root.get(Customer_.cpf), someCpf);
            }
        };
    }
}

and make your repository extend JpaSpecificationExecutor in addition to whatever it extended before. You'll be able to pass your specification into the query:

pessoaRepo.findAll(PessoaSpecifications.byCpf("asdf"));

The same article goes on to describe an alternative way of doing this using Querydsl which avoids the hassle of creating the code generating the predicate. Once you add the querydsl plugin to your pom.xml and extend your repository interface with QueryDslPredicateExecutor, you can write stuff like

List<Pessoa> p = pessoaRepo.findAll(pessoa.cpf.eq("asdf"));
like image 118
Nathan Hughes Avatar answered Oct 09 '22 05:10

Nathan Hughes