Código do formulário na form.jsp
<form:form action="${s:mvcUrl('PC#gravar').build() }" method="post" commandName="produto">
<div>
<label>Título</label>
<form:input path="titulo" />
<form:errors path="titulo" />
</div>
<div>
<label>Descrição</label>
<form:textarea rows="10" cols="20" path="descricao" />
<form:errors path="descricao" />
</div>
<div>
<label>Páginas</label>
<form:input path="paginas" />
<form:errors path="paginas" />
</div>
<div>
<label>Data de Lançamento</label>
<form:input path="dataLancamento" />
<form:errors path="dataLancamento" />
</div>
<c:forEach items="${tipos}" var="tipoPreco" varStatus="status">
<div>
<label>${tipoPreco}</label>
<form:input path="precos[${status.index}].valor" />
<form:hidden path="precos[${status.index}].tipo" value="${tipoPreco}" />
</div>
</c:forEach>
<button type="submit">Cadastrar</button>
</form:form>
Código do Produto.java
package br.com.casadocodigo.loja.models;
import java.util.Calendar;
import java.util.List;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import org.springframework.format.annotation.DateTimeFormat;
@Entity
public class Produto {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String titulo;
private String descricao;
private int paginas;
@ElementCollection
private List<Preco> precos;
@DateTimeFormat
private Calendar dataLancamento;
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
public String getDescricao() {
return descricao;
}
public void setDescricao(String descricao) {
this.descricao = descricao;
}
public int getPaginas() {
return paginas;
}
public void setPaginas(int paginas) {
this.paginas = paginas;
}
public List<Preco> getPrecos() {
return precos;
}
public void setPrecos(List<Preco> precos) {
this.precos = precos;
}
public Calendar getDataLancamento() {
return dataLancamento;
}
public void setDataLancamento(Calendar dataLancamento) {
this.dataLancamento = dataLancamento;
}
@Override
public String toString() {
return "Produto [titulo=" + titulo + ", descricao=" + descricao + ", paginas=" + paginas + "]";
}
}
Código do ProdutosController.java
package br.com.casadocodigo.loja.controllers;
import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.bind.annotation.*;
import br.com.casadocodigo.loja.daos.ProdutoDAO;
import br.com.casadocodigo.loja.models.Produto;
import br.com.casadocodigo.loja.models.TipoPreco;
import br.com.casadocodigo.loja.validation.ProdutoValidation;
@Controller
@RequestMapping("/produtos")
public class ProdutosController {
@Autowired
private ProdutoDAO produtoDao;
@InitBinder
public void InitBinder(WebDataBinder binder){
binder.addValidators(new ProdutoValidation());
}
@RequestMapping("form")
public ModelAndView form(Produto produto) {
System.out.println("Spring MVC carrega views/produtos/form.jsp");
ModelAndView modelAndView = new ModelAndView("produtos/form");
modelAndView.addObject("tipos", TipoPreco.values());
return modelAndView;
}
@RequestMapping(value="/produtos", method=RequestMethod.POST)
public ModelAndView gravar(@Valid Produto produto, BindingResult result, RedirectAttributes redirectAttributes){
if(result.hasErrors()){
return form(produto);
}
produtoDao.gravar(produto);
redirectAttributes.addFlashAttribute("message","Produto cadastrado com sucesso");
return new ModelAndView("redirect:produtos");
}
@RequestMapping(value="/produtos", method=RequestMethod.GET)
public ModelAndView listar(){
List<Produto> produtos = produtoDao.listar();
ModelAndView modelAndView = new ModelAndView("produtos/lista");
modelAndView.addObject("produtos", produtos);
return modelAndView;
}
}
Ao carregar a URL http://localhost:8080/casadocodigo/produtos/form recebo a seguinte exception:
org.apache.jasper.JasperException: An exception occurred processing JSP page /WEB-INF/views/produtos/form.jsp at line 39
36: <c:forEach items="${tipos}" var="tipoPreco" varStatus="status">
37: <div>
38: <label>${tipoPreco}</label>
39: <form:input path="precos[${status.index}].valor" />
40: <form:hidden path="precos[${status.index}].tipo" value="${tipoPreco}" />
41: </div>
42: </c:forEach>
Se eu retirar a tag form:input e deixar apenas a tag input nos campos de preço a JSP carrega normalmente.
Exception do Tomcat:
Stacktrace:] with root cause
org.springframework.beans.NotReadablePropertyException: Invalid property 'precos[0].valor' of bean class [br.com.casadocodigo.loja.models.Produto]: Bean property 'precos[0].valor' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
at org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:619)
at org.springframework.beans.AbstractNestablePropertyAccessor.getPropertyValue(AbstractNestablePropertyAccessor.java:610)
at org.springframework.validation.AbstractPropertyBindingResult.getActualFieldValue(AbstractPropertyBindingResult.java:99)
at org.springframework.validation.AbstractBindingResult.getFieldValue(AbstractBindingResult.java:229)
at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:120)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:168)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:188)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:154)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.autogenerateId(AbstractDataBoundFormElementTag.java:141)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.resolveId(AbstractDataBoundFormElementTag.java:132)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:116)
at org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:422)
at org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.java:142)
at org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:84)
at org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java:80)
at org.apache.jsp.WEB_002dINF.views.produtos.form_jsp._jspx_meth_form_005finput_005f3(form_jsp.java:473)
at org.apache.jsp.WEB_002dINF.views.produtos.form_jsp._jspx_meth_c_005fforEach_005f0(form_jsp.java:433)
at org.apache.jsp.WEB_002dINF.views.produtos.form_jsp._jspService(form_jsp.java:254)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:439)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:395)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:339)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:747)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:485)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:410)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:337)
at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:168)
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1257)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:624)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:218)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:956)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:442)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1082)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:623)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Unknown Source)