DEV Community

Guilherme Manzano
Guilherme Manzano

Posted on

1

Anotações do curso Spring Boot, JPA e Hibernate — Parte 2

Alt Text

Continuação do artigo da semana passa sobre Spring Boot, caso não tenha visto a parte 1.

Métodos HTTP
Nas classes SERVICE, nós vamos criar os métodos de busca e inserção que vamos utilizar em nosso programa. Como exemplo, vou utilizar a classe AlunoService. Onde tiver // serão os comentários explicando o que cada parte de código está fazendo.

// Vamos começar chamando a classe AlunoRepository como um atributo da classe, que faz a transação com o banco de dados
@Autowired
private AlunoRepository alunoRepository;
// Aqui temos nosso primeiro método, que busca todos os elementos da Classe. Aqui ele chama a classe do repositório e a função findAll(), retornando então a lista com os resultados
public List findAll() {
return this.alunoRepository.findAll();
}

// Este método busca um aluno, pelo id dele. Recebe como parâmetro o ID e retorna a chamada da classe repositório, que chama a função findById(), que busca um elemento pelo id. A função orElse é chamado caso não encontre nenhum aluno pelo id escolhido.
public Aluno getOne(int id) {
return alunoRepository.findById(id).orElse( new Aluno() );
}

// Este método vai salvar um ano no banco. O retorno é a chamada da classe repositório, que chama o método save(), passando como parâmetro o aluno.
public Aluno save(Aluno aluno) {
return this.alunoRepository.save(aluno);
}

// Este método é um pouco mais complexo, ele vai atualizar os dados de um Aluno, através do ID dele. O Optional<> vai encapsular o retorno de métodos e informar se um valor do tipo está presente ou ausente, no nosso programa, ele vai verificar se consegue encontrar o aluno, pelo ID que foi passado. Depois, a variável update recebe um valor null e temos um if, que verifica se o ID foi encontrado. Em caso negativo, ele retorna o update como nulo (ou seja, nada é alterado). Em caso positivo, o update vai chamar o método get(), que pega os dados que estão sendo passados na requisição. Depois, o update chama os métodos setNome() e setRA(), que vão salvar os novos valores dos atributos do aluno que foram alterados. Por fim, o update vai chamar a classe aluno repositor, que por sua vez chama o métodos save(), passando os valores salvos como parâmetro.
public Aluno update(int id, Aluno aluno) {
Optional a = this.alunoRepository.findById(id);
Aluno update = null;
if(a.isPresent()) {
update = a.get();
update.setNome(aluno.getNome());
update.setRa(aluno.getRa());
update = this.alunoRepository.save(update);
}
return update;
}

// Vamos deletar um registro de Aluno, utilizando um método sem retorno e recebendo um ID como parâmetro. Neste método, basta o atributo classe repositório chamar a função deleteById(), passando um id como parâmetro.
public void delete(int id) {
this.alunoRepository.deleteById(id);
}

// Este método realiza a busca de um Aluno, recebendo como parâmetro o nome do mesmo. Ela irá retornar o atributo classe repositório, que chama o método findByNome(), o qual recebe o nome como parâmetro.
public List findByNome(String nome) {
return this.alunoRepository.findByNome(nome);
}

// Este método é similar ao anterior, mas realiza a busca de um Aluno que o parâmetro nome contenha um “pedaço” do nome, ou seja, é uma função de busca que não precisa buscar pelo parâmetro todo, tornando-a mais abrangentes em determinadas situações. Ela irá retornar o atributo classe repositório, que chama o método findByNome(), o qual recebe o nome como parâmetro. O método irá retornar o atributo classe repositório, que chama o método findByNomeContains(), o qual recebe o nome como parâmetro.
public List findByNomeContains(String nome) {
return this.alunoRepository.findByNomeContains(nome);
}

//Método para exibir os resultados em páginas, evitando trazer uma quantidade muito alta de registros de uma vez (para projetos com grande volume de dados). Passamos como parâmetro a quantidade de páginas que queremos exibir e a qtd de linhas por página. Depois, importamos a classe PageRequest que trabalha com isso e criamos uma nova variável, que recebe a quantidade de páginas e linhas. Por fim, realizamos a busca dos dados.
public Page paginacao(int pagina, int linhas) {
PageRequest pageRequest = PageRequest.of(pagina, linhas);
return this.produtoRepository.findAll(pageRequest);
}

Nas classes CONTROLLER, nós vamos utilizar os métodos de busca (que criamos nos SERVICE) para se comunicar com o banco de dados. Como exemplo, vou utilizar a classe AlunoController.
// Vamos começar chamando a classe AlunoService como sendo um atributo da nossa classe.
@Autowired
private AlunoService alunoService;
// Aqui vamos trazer a lista de todos os alunos. Vamos começar usando a anotação @GetMapping, passando como value(url) o caminho alunos. O método tem como retorno a chamada de alunoService, que chama a função findAll().
@GetMapping(value = “alunos”)
public List getAll() {
return this.alunoService.findAll();
}

// Vamos usar novamente a anotação @GetMapping, passando como value(url) o caminho alunos especificando o ID. O argumento terá a anotação @PathVariable que recebe um id. O método tem como retorno a chamada de alunoService, que chama a função getOne(), passando o id.
@GetMapping(value=”alunos/{id}”)
public Aluno getOne(@PathVariable int id){
return this.alunoService.getOne(id);
}

// Este método criará novos registros em nosso banco de dados. Utilizaremos a anotação @PostMapping (passando o caminho em value). O método terá a anotação @RequestBody, que pedirá o corpo da requisição (onde será passado os atributos e valores dos alunos). O retorno é o atributo alunoService, que chama nosso método save(), passando os dados do aluno que inserimos
@PostMapping(value=”alunos”)
public Aluno create(@RequestBody Aluno aluno) {
return this.alunoService.save(aluno);
}

// Este método atualizará os registros de um aluno em nosso banco de dados. Utilizaremos a anotação @PatchtMapping (passando o caminho em value, com o ID). O método terá a anotação @RequestBody, que pedirá o corpo da requisição (onde será passado os atributos e valores dos alunos) e o @PathVariable. O retorno é o atributo alunoService, que chama nosso método update(), passando o ID e os dados do aluno.
@PatchMapping(value=”alunos/{id}”)
public Aluno update(@RequestBody Aluno aluno, @PathVariable int id) {
return this.alunoService.update(id, aluno);
}

// Este método vai apagar os registros de um aluno em nosso banco de dados. Utilizaremos a anotação @DeletetMapping (passando o caminho em value, com o ID). O método terá a anotação @PathVariable para o ID. O retorno é o atributo alunoService, que chama nosso método delete(), passando o ID do aluno.
@DeleteMapping(value=”alunos/{id}”)
public void delete(@PathVariable int id) {
this.alunoService.delete(id);
}

// Este método vai buscar um aluno pelo nome. Utilizaremos a anotação @GetMapping (passando o caminho em value, com o parâmetro nome). O método terá a anotação @PathVariable, que recebe o nome. O retorno é o atributo alunoService, que chama nosso método findByNome(), passando o nome.
@GetMapping (value = “alunos/search/{nome}”)
public List search(@PathVariable String nome) {
return this.alunoService.findByNome(nome);
}

// Este método vai buscar um aluno pelo nome. Utilizaremos a anotação @GetMapping (passando o caminho em value, com o parâmetro nome). O método terá a anotação @PathVariable, que recebe o nome. O retorno é o atributo alunoService, que chama nosso método findByNome(), passando o nome.
@GetMapping (value = “alunos/searchnew/{nome}”)
public List searchBy(@PathVariable String nome) {
return this.alunoService.findByNomeContains(nome);
}

// Este método vai exibir todos os resultados, dividios em páginas. Utilizaremos a anotação @GetMapping (passando o caminho em value, com o parâmetro nome). O método terá a anotação @RequestParam, que recebe o caminho da pagina e da linha e o valor inicial deles. O retorno será a chama da classe produtoServico, que vai chamar o método de paginacao, recebendo os argumentos de pagina e linhas.
@GetMapping(value = “produtos/paginador”)
public Page paginacao(@RequestParam (value = “pagina”, defaultValue = “0”) int pagina, @RequestParam (value = “linhas”, defaultValue = “5”) int linhas){
return this.produtoService.paginacao(pagina, linhas);
}

// OFF-TOPIC: Vou mostrar um exemplo de como seria esta busca pelo navegador. Vamos ter a url completa que chama nosso método (http://localhost:8081/produtos/paginador) e passaremos dois parâmetros em nossa busca. Em “pagina=1” estamos dizendo para exibir a pagina 1 e em “linhas=1” para exibir a busca a partir da primeira linha.
http://localhost:8081/produtos/paginador?pagina=1&linhas=1
ComamandLineRunner e populando dados direto no banco
O Spring Boot fornece duas interfaces (CommandLineRunner e ApplicationRunner) para rodar partes específicas de códigos quando a aplicação for totalmente inicializada. Estas interface são chamadas um pouco antes do run(), uma vez que a SpringApplication é concluído. Aqui vamos utilizar um exemplo para popularmos (inserir) dados no banco de dados sempre que a aplicação iniciar, não mostrarei todas as Classes utilizadas para não ficar muito extenso. Vou utilizar // dentro do código para explicar alguns trechos. Vamos começar declarando os atributos das classes que vamos utilizar (ProdutoRepository, CategoriaRepository e ImagemRepository) com a anotação @Autowired por cima delas (Injeção de Dependências).

Embaixo, nós temos nosso método principal que está rodando o Spring Boot, não vamos mexer nada nele. E, em seguida, teremos nosso método que implementos da interface CommandLineRunner e continuarei a explicação agora dentro do próprio código:

@SpringBootApplication
public class ApicursoApplication implements CommandLineRunner {
@Autowired
private ProdutoRepository produtorepository;
@Autowired
private CategoriaRepository categoriarepository;
@Autowired
private ImagemRepository imagemRepository;
public static void main(String[] args) {
SpringApplication.run(ApicursoApplication.class, args);
}
@Override
public void run(String… args) throws Exception {
// Aqui vamos cadastrar um novo item na tabela de Categorias, instanciando um objeto chamado de cat1. A classe irá chamar o builder() (logo explicarei mais sobre ele) e chamará os métodos com os campos que serão preenchidos. Vamos passar o nome da categoria, sua descrição e chamar o método build() para construir nossa instância do objeto. Depois, chamaremos pelo this o atributo categoriarepositoy, que chama o método save() e passaremos o cat1 como argumento. Faremos a mesma coisa para criar dois outros produtos e um imagem (do produto), o Produto e Imagem são duas outras tabelas do nosso banco e a imagem receberá apenas uma url como parâmetro.
Categoria cat1 = Categoria.builder().nome(“Eletrodomésticos”).descricao(“Eletrodoméstico”).build();
this.categoriarepository.save(cat1);
Produto prod1 = Produto.builder().categoria(cat1).nome(“Geladeira”).descricao(“Geladeira”).preco(2500).build();
this.produtorepository.save(prod1);
Produto prod2 = Produto.builder().categoria(cat1).nome(“Fogão”).descricao(“Fogão”).preco(1000).build();
Imagem img =Imagem.builder().url(“http://localhost:8080").build();
// Este pedaço de código irá unir o que criamos das tabelas produtos e imagem, e, depois, salvá-las em suas próprias tabelas (fará um JOIN entre as tabelas)
img.setProdutos(Arrays.asList(prod2));
prod2.setImagens(Arrays.asList(img));
this.imagemRepository.save(img);
this.produtorepository.save(prod2);
}
}

Manipulando Tabelas
A anotação @builder permite produzir automaticamente o código necessário para instanciar uma classe de maneira mais fácil, como neste exemplo: Person.builder().name(“Adam Savage”) .city(“San Francisco”).build();

Já comentei sobre as anotações @ManyToOne, @ManyToMany, @JoinTable, @JoinColumn anteriormente, aqui estamos referenciando a ligação das tabelas, utilizando as chaves primárias e estrangeiras das mesmas. Utilizando o fetch=FetchType.EAGER, ele irá carregar todos os produtos que estão relacionado a uma determinada categoria (chamado de Eager Loading, do Hibernate).
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name = “categoria_id”)
private Categoria categoria;
@ManyToMany
@JoinTable( name = “produto_imagem”,
joinColumns = @JoinColumn(name=”produto_id”),
inverseJoinColumns = @JoinColumn(name=”imagem_id”)
)
private List imagens;

O @JsonIgnore é usado no nível de campos, para marcar uma propriedade (ou lista de propriedades) que serão ignoradas, ou seja, não vão ser exibidas.
@OneToMany(mappedBy = “categoria”)
@JsonIgnore
private List produtos;
// Passando a anotação @Query, nós podemos criar uma busca de dados personalizada. Colocando como parâmetro o nativeQuery como true, é possível passar comandos de busca em SQL puro. O nome do método de busca que estamos criando é o procuraPorNome.
@Query(value = “SELECT * FROM Aluno WHERE nome = :nome”, nativeQuery = true)
public List procuraPorNome(String nome);

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

AWS Q Developer image

Your AI Code Assistant

Implement features, document your code, or refactor your projects.
Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay