I'm developing a web application that has the following mapping: A user has a category; A category has several subcategories; a subcategory has several products. As the image below: WhatIamtryingtoimplementis:whentheadminregistersanewsupplierhewillinformthesupplier'spersonaldataandthecategoryofhisenterpriseandthesubcategoriesofhisproducts.Forwhenthesupplierlogsintotheplatform,alreadybringtheuserloggedinandthesubcategoriescorrespondingtotheuser,sohecanregisterhisproducts,asshownbelow:
Myusermodelis:
@EntitypublicclassUsuarioimplementsUserDetails,Serializable{privatestaticfinallongserialVersionUID=1L;@Id@GeneratedValueprivateLongid;@NotBlank(message="Nome é uma informação obrigatória.")
@Size(min = 2, max = 30)
private String nome;
private String nomeFantasia;
@NotBlank(message = "Rua é uma informação obrigatória.")
private String rua;
@NotBlank(message = "Bairro é uma informação obrigatória.")
private String bairro;
private String complemento;
@NotBlank(message = "Estado é uma informação obrigatória.")
private String estado;
@NotBlank(message = "Cidade é uma informação obrigatória.")
private String cidade;
@NotBlank(message = "É uma informação obrigatória.")
private String cnpjCpf;
private String telefone;
public String categoria;
public Usuario getUsuario() {
return usuario;
}
public void setUsuario(Usuario usuario) {
this.usuario = usuario;
}
@NotBlank(message = "E-mail é uma informação obrigatória.")
@Email(message = "Não é um e-mail válido")
private String email;
@NotBlank(message = "Senha é uma informação obrigatória.")
private String senha;
private String foto;
@OneToOne
@JoinColumn(name = "id_categoria", unique = true, nullable = false, updatable = false)
private Usuario usuario;
@ManyToMany
@JoinTable(name = "usuarios_roles", joinColumns = @JoinColumn(name = "usuario_id", referencedColumnName = "email"), inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "nomeRole"))
private List<Role> roles;
public List<Role> getRoles() {
return roles;
}
GETTERS E SETTERS...
Model Category
@Entity
public class Categoria {
@Id
@GeneratedValue
private Long id;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinColumn(name = "id_usuario")
private Usuario usuario;
@OneToMany
@JoinColumn(name = "subcategoria_id")
private List<Subcategoria> subcategorias;
GETTERS E SETTERS...
Model subcategory:
@Entity
public class Subcategoria {
@Id
@GeneratedValue
private Long id;
@NotBlank(message = "")
private String nome;
@OneToMany(mappedBy = "subcategoria")
public List<Produto> produtos;
@ManyToOne
@JoinColumn(name = "categoria_id")
public Categoria categoria;
GETTERS E SETTERS...
Model products:
@Entity
public class Produto implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name="produto_id")
public Subcategoria subcategoria;
GETTERS E SETTERS...
Controller users:
@Controller
@RequestMapping("/fornecedor")
public class UsuarioController {
@Autowired
private UsuarioService service;
@RequestMapping("/cadastrarFornecedor")
public ModelAndView cadastrarFornecedor(Usuario usuario) {
ModelAndView mv = new ModelAndView("fornecedor/form");
mv.addObject("usuario", usuario);
return mv;
}
@PostMapping("/save")
public ModelAndView save(@Valid Usuario usuario, String senhaconf, @RequestParam("files[]") MultipartFile[] files, String senha, BindingResult result, RedirectAttributes attributes) {
System.out.print(usuario);
if (result.hasErrors() || !senha.equals(senhaconf)) {
attributes.addFlashAttribute("mensagem", "[Verifique os campos!");
System.out.println("--------erro ao salvar: " + usuario.getId());
return cadastrarFornecedor(usuario);
}
String foto = files[0].getOriginalFilename();
usuario.setFoto(foto);
usuario.setSenha(new BCryptPasswordEncoder().encode(senha));
service.save(usuario);
attributes.addFlashAttribute("mensagem", "Evento cadastrado com sucesso!");
return findAll();
}
Form Page:
<section class="forms">
<form th:object="${usuario}" id="formQuantidade"
th:action="@{/fornecedor/save}" method="POST"
enctype="multipart/form-data">
<!--Input responsável em guardar o id-->
<div class="container-fluid">
<th:block th:include="/mensagemValidacao :: mensagem"></th:block>
<div class="row">
<input id="id" name="id" type="hidden" th:field="*{id}"
th:value="*{id}">
<div class="col-lg-6">
<div class="form-group">
<label>Nome do empreendimento:</label> <input type="text"
name="nome" th:field="*{nome}" id="inputNome"
class="form-control">
</div>
<div class="form-group">
<label>Nome fantasia:</label> <input type="text"
name="nomeFantasia" th:field="*{nomeFantasia}"
class="form-control" id="inputNomeFantasia">
</div>
<div class="form-group">
<label>Rua:</label> <input type="text" name="rua"
th:field="*{rua}" id="inputRua" class="form-control">
</div>
<div class="form-group">
<label>Bairro:</label> <input type="text" name="bairro"
th:field="*{bairro}" id="inputBairro" class="form-control">
</div>
<div class="form-group">
<label>Complemento:</label> <input type="text" name="complemento"
th:field="*{complemento}" id="inputComplemento"
class="form-control">
</div>
<div class="form-group">
<label>Cidade:</label> <input type="text" name="cidade"
th:field="*{cidade}" id="inputCidade" class="form-control">
</div>
<div class="form-group">
<label>Estado:</label> <select name="estado" class="form-control"
th:field="*{estado}" id="inputEstado">
<option th:value="AC">Acre</option>
<option value="AL">Alagoas</option>
<option value="AP">Amapá</option>
<option value="AM">Amazonas</option>
<option value="BA">Bahia</option>
<option value="CE">Ceará</option>
<option value="DF">Distrito Federal</option>
<option value="ES">Espírito Santo</option>
<option value="GO">Goiás</option>
<option value="MA">Maranhão</option>
<option value="MT">Mato Grosso</option>
<option value="MS">Mato Grosso do Sul</option>
<option value="MG">Minas Gerais</option>
<option value="PA">Pará</option>
<option value="PB">Paraíba</option>
<option value="PR">Paraná</option>
<option value="PE">Pernambuco</option>
<option value="PI">Piauí</option>
<option value="RJ">Rio de Janeiro</option>
<option value="RN">Rio Grande do Norte</option>
<option value="RS">Rio Grande do Sul</option>
<option value="RO">Rondônia</option>
<option value="RR">Roraima</option>
<option value="SC">Santa Catarina</option>
<option value="SP">São Paulo</option>
<option value="SE">Sergipe</option>
<option value="TO">Tocantins</option>
</select>
</div>
</div>
<div class="col-lg-6">
<div class="form-group">
<label>CPF/CNPJ:</label> <input type="text" placeholder=""
id="cpfcnpj" th:field="*{cnpjCpf}" name="cnpjCpf"
class="form-control">
</div>
<div class="form-group">
<label>Telefone:</label> <input type="text" placeholder=""
id="telefone" th:field="*{telefone}" name="telefone"
class="form-control">
</div>
<div class="form-group">
<label>Categoria:</label> <select name="usuario.categoria"
class="form-control" th:field="*{usuario.categoria}" id="inputCategoria">
<option value="Farmarcias">Farmacias</option>
<option value="Lanchonetes">Lanchonetes</option>
<option value="Material de construçao">Material de
Construçao</option>
<option value="Restaurantes">Restaurantes</option>
<option value="Pizzarias">Pizzarias</option>
</select>
</div>
<label for="quantidade">Subcategorias</label>
<div class="inputs">
<input type="text" name="campo[]" class="form-control" th:field="*{categoria.subcategoria}"
placeholder=""> <br>
</div>
<a href="javascript:void(0)" id="adicionarcampo"
class="btn btn-sm btn btn-success">Nova subcategoria</a>
<div class="form-group">
<label>Email:</label> <input type="email" name="email"
th:field="*{email}" id="inputEmail" class="form-control"
placeholder="[email protected]">
</div>
<div class="form-group">
<label>Senha:</label> <input type="password" name="senha"
th:field="*{senha}" id="inputSenha" class="form-control"
placeholder="Mínimo 6 caracteres">
</div>
<div class="form-group">
<label>Confirmar senha:</label> <input type="password"
placeholder="Mínimo 6 caracteres" name="senhaconf"
class="form-control">
</div>
</div>
<div class="col-lg-12">
<button type="submit" class="btn btn-primary">Salvar</button>
<button type="reset" class="btn btn-danger"
onclick="window.location.href='/fornecedor/lista';">
Cancelar</button>
</div>
</div>
</div>
The way it is it gives the following error:
org.springframework.beans.NotReadablePropertyException: Invalid property 'categoria.subcategoria' of bean class [com.bigboss.comprafacil.models.Usuario]: Bean property 'categoria.subcategoria' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
As if it does not have the sets and subcategories or users, can someone help me?