Olá pessoal,
Estou desenvolvendo um projeto pessoal para estudar e não ficar parado, visto que não consigo estágio na área. O projeto é um controle de estoque bem simples e durante o desenvolvimento tive problemas mas nada que não fosse resolvido com pesquisa até o presente momento que estou preso a um problema que não estou conseguindo entender bem como resolve-lo.
O problema é o seguinte, no sistema um produto possui uma categoria (ex: Eletrodoméstico, Informática, Livros), estes possuem uma subcategoria (ex: Televisão, Notebook, Técnico), o problema está quando preciso exibir uma lista de subcategorias para uma categoria e exibi-la na tela em um select.
Estou utilizando na aplicação o Spring e levar dados para a tela o thymeleaf.
Classe Categoria
package com.frazao.projeto.model;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.hibernate.validator.constraints.NotEmpty;
@Entity
@Table(name = "tab_categoria_produto")
public class Categoria {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long codigo;
@NotEmpty(message = "Categoria não pode ser vazia.")
private String descricao;
@OneToMany(mappedBy = "categoria")
private List<Subcategoria> subcategorias;
public Long getCodigo() {
return codigo;
}
public void setCodigo(Long codigo) {
this.codigo = codigo;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((codigo == null) ? 0 : codigo.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Categoria other = (Categoria) obj;
if (codigo == null) {
if (other.codigo != null)
return false;
} else if (!codigo.equals(other.codigo))
return false;
return true;
}
public List<Subcategoria> getSubcategorias() {
return subcategorias;
}
public void setSubcategorias(List<Subcategoria> subcategorias) {
this.subcategorias = subcategorias;
}
}
Classe Subcategoria
package com.frazao.projeto.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import org.hibernate.validator.constraints.NotBlank;
@Entity
@Table(name = "subcategoria")
public class Subcategoria {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long codigo;
@NotBlank(message = "O nome da descrição é obrigatório.")
@Size(max = 12, message = "O número máximo de caracteres é 12.")
private String nome;
@NotNull(message = "Selecione uma categoria.")
@JoinColumn(name = "cod_categoria")
@ManyToOne
private Categoria categoria;
public Long getCodigo() {
return codigo;
}
public void setCodigo(Long codigo) {
this.codigo = codigo;
}
public String getNome() {
return nome;
}
public void setNome(String nome) {
this.nome = nome;
}
public Categoria getCategoria() {
return categoria;
}
public void setCategoria(Categoria categoria) {
this.categoria = categoria;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((codigo == null) ? 0 : codigo.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Subcategoria other = (Subcategoria) obj;
if (codigo == null) {
if (other.codigo != null)
return false;
} else if (!codigo.equals(other.codigo))
return false;
return true;
}
}
Controller
package com.frazao.projeto.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.frazao.projeto.model.Categoria;
import com.frazao.projeto.model.Produto;
import com.frazao.projeto.model.Subcategoria;
import com.frazao.projeto.repository.Categorias;
import com.frazao.projeto.repository.SubCategorias;
import com.frazao.projeto.service.ProdutoService;
@Controller
@RequestMapping("/produtos")
public class ProdutoController {
@Autowired
private ProdutoService produtoService;
@Autowired
private Categorias categorias;
@Autowired
private SubCategorias subcategorias;
// tela :: Home
@GetMapping("/novo")
public ModelAndView home() {
ModelAndView mv = new ModelAndView("CadastroProduto");
mv.addObject(new Produto());
return mv;
}
// tela :: Categoria
@GetMapping("/categoria")
public ModelAndView categoria() {
ModelAndView mv = new ModelAndView("Categorias");
mv.addObject(new Categoria());
return mv;
}
// tela :: Subcategoria
@GetMapping("/categoria/subcategoria")
public ModelAndView subcategoria() {
ModelAndView mv = new ModelAndView("Subcategorias");
mv.addObject(new Subcategoria());
return mv;
}
// função :: Cadastrar produtos
@PostMapping
public String cadastrar(@Validated Produto produto, Errors errors, RedirectAttributes attributes) {
if (errors.hasErrors()) {
return "CadastroProduto";
}
produtoService.salvar(produto);
attributes.addFlashAttribute("mensagem", "Produto cadastrado com sucesso.");
return "redirect:/produtos/novo";
}
// função :: Pesquisar produtos
@GetMapping
public ModelAndView pesquisar() {
ModelAndView mv = new ModelAndView("Produtos");
return mv;
}
// função :: Cadastrar categorias
@PostMapping("/novo/categoria")
public String cadastrarCategoria(@Validated Categoria categoria, Errors errors, RedirectAttributes attributes) {
if (errors.hasErrors()) {
return "Categorias";
}
categorias.save(categoria);
attributes.addFlashAttribute("mensagem", "Nova categoria adicionada.");
return "redirect:/produtos/categoria";
}
// função :: Salvar subcategorias
@PostMapping("/novo/categoria/subcategoria")
public String cadastrarSubcategoria(@Validated Subcategoria subcategoria, Errors errors,
RedirectAttributes attributes) {
if (errors.hasErrors()) {
return "Categorias";
}
subcategorias.save(subcategoria);
attributes.addFlashAttribute("mensagem", "Subcategoria foi salva.");
return "redirect:/produtos/categoria/subcategoria";
}
// função :: Entregar uma lista de categorias para a tela.
@ModelAttribute("categorias")
public List<Categoria> todasCategorias() {
return categorias.findAll();
}
}
Tela que deveria exibir a lista de subcategorias
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
layout:decorator="LayoutPadrao">
<head>
<title>Lista de Produtos</title>
</head>
<section layout:fragment="conteudo">
<form class="form-horizontal" th:action="@{/produtos/novo/categoria}" method="POST" th:object="${categoria}">
<div th:insert="~{MensagemSucesso :: mensagemSucesso}"></div>
<div th:insert="~{MensagemErro :: mensagemErro}"></div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Nova Categoria</h3>
</div>
<div class="panel-body">
<div class="form-group" th:classappend="${#fields.hasErrors('descricao')}? 'has-error'">
<label for="categoria" class="col-sm-2 control-label">Categoria</label>
<div class="col-sm-4">
<input type="text" class="form-control" id="categoria" placeholder="Nome da nova categoria" name="descricao">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">Salvar</button>
</div>
</div>
</div>
</div>
</form>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Categorias</h3>
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>#</td>
<td>Categoria</td>
<td>Subcategoria</td>
<td></td>
</tr>
</thead>
<tbody>
<tr th:each="categoria :${categorias}">
<td th:text="${categoria.codigo}"></td>
<td th:text="${categoria.descricao}"></td>
<td>
<div class="col-sm-4">
<select class="form-control">
<option th:text="${categoria.subcategorias}"></option> <--- aqui
</select>
</div>
<a th:href="@{/produtos/categoria/subcategoria}"><button type="button" class="btn btn-default">+</button></a>
</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
</html>
Da seguinte forma que estou fazendo consigo popular o select com subcategorias em suas respectivas categorias, mas somente o caminho, por exemplo Livro(categoria) - com.frazao.projeto.model.Subcategoria@20 (subcategoria). Não tenho ideia de como popular essa lista com as strings.
Se puderem me ajudar ou me passar um material de referência ficarei muito grato!