3
Well, I have a problem that never occurred to me before related to the Spring MVC Binding.
I have a form as below. The bindind "firstName" and "lastname" work well, but already the "logins.email" troublesome.
<f:form class="form-signin" method="post" action="addAgency" modelAttribute="tenant">
<div class="input-group">
<span class="input-group-addon entypo-user"></span>
<f:input path="firstName" type="text" class="form-control"
placeholder="Nome" />
</div>
<br>
<div class="input-group">
<span class="input-group-addon entypo-user"></span>
<f:input path="lastName" type="text" class="form-control"
placeholder="Sobrenome" />
</div>
<br>
<div class="input-group">
<span class="input-group-addon entypo-mail"></span>
<f:input path="logins.email" type="text" class="form-control"
placeholder="E-mail" />logins
</div>
<!-- Restante de Formulário -->
The Following stackTrace is displayed:
org.springframework.beans.NotReadablePropertyException: Invalid property 'logins.email' of bean class [br.com.joocebox.model.Agency]: Bean property 'logins.email' is not readable or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:725)
org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:716)
org.springframework.validation.AbstractPropertyBindingResult.getActualFieldValue(AbstractPropertyBindingResult.java:99)
org.springframework.validation.AbstractBindingResult.getFieldValue(AbstractBindingResult.java:229)
org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:120)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:168)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:188)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:154)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.autogenerateId(AbstractDataBoundFormElementTag.java:141)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.resolveId(AbstractDataBoundFormElementTag.java:132)
org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:116)
org.springframework.web.servlet.tags.form.AbstractHtmlElementTag.writeDefaultAttributes(AbstractHtmlElementTag.java:422)
org.springframework.web.servlet.tags.form.InputTag.writeTagContent(InputTag.java:142)
org.springframework.web.servlet.tags.form.AbstractFormTag.doStartTagInternal(AbstractFormTag.java:84)
org.springframework.web.servlet.tags.RequestContextAwareTag.doStartTag(RequestContextAwareTag.java:80)
org.apache.jsp.WEB_002dINF.views.landing.register_jsp._jspx_meth_f_005finput_005f2(register_jsp.java:275)
org.apache.jsp.WEB_002dINF.views.landing.register_jsp._jspx_meth_f_005fform_005f0(register_jsp.java:138)
org.apache.jsp.WEB_002dINF.views.landing.register_jsp._jspService(register_jsp.java:77)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.jasper.runtime.JspRuntimeLibrary.include(JspRuntimeLibrary.java:954)
org.apache.jasper.runtime.PageContextImpl.doInclude(PageContextImpl.java:688)
org.apache.jasper.runtime.PageContextImpl.include(PageContextImpl.java:682)
org.apache.tiles.request.jsp.JspRequest.doInclude(JspRequest.java:123)
org.apache.tiles.request.AbstractViewRequest.dispatch(AbstractViewRequest.java:47)
org.apache.tiles.request.render.DispatchRenderer.render(DispatchRenderer.java:47)
org.apache.tiles.request.render.ChainedDelegateRenderer.render(ChainedDelegateRenderer.java:68)
org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:259)
org.apache.tiles.template.InsertAttributeModel.renderAttribute(InsertAttributeModel.java:188)
org.apache.tiles.template.InsertAttributeModel.execute(InsertAttributeModel.java:132)
org.apache.tiles.jsp.taglib.InsertAttributeTag.doTag(InsertAttributeTag.java:299)
org.apache.jsp.WEB_002dINF.layouts.landing_jsp._jspx_meth_tiles_005finsertAttribute_005f0(landing_jsp.java:268)
org.apache.jsp.WEB_002dINF.layouts.landing_jsp._jspService(landing_jsp.java:131)
org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:432)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:390)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:334)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.apache.tiles.request.servlet.ServletRequest.forward(ServletRequest.java:265)
org.apache.tiles.request.servlet.ServletRequest.doForward(ServletRequest.java:228)
org.apache.tiles.request.AbstractClientRequest.dispatch(AbstractClientRequest.java:57)
org.apache.tiles.request.render.DispatchRenderer.render(DispatchRenderer.java:47)
org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:259)
org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:397)
org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:238)
org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:221)
org.apache.tiles.renderer.DefinitionRenderer.render(DefinitionRenderer.java:59)
org.springframework.web.servlet.view.tiles3.TilesView.renderMergedOutputModel(TilesView.java:114)
org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:267)
org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1221)
org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1005)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:952)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
Well, the problem is that I have the respective getters and setters in my design. Below are the models of reduced shape:
Agencia.java
@Entity
@Table(name="agency")
@NamedQuery(name="Agency.findAll", query="SELECT a FROM Agency a")
public class Agency implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id_agency")
private Long idAgency;
@Column(name="first_name")
private String firstName;
@Column(name="last_name")
private String lastName;
@Column(name="subdomain")
private String subdomain;
//bi-directional many-to-one association to Login
@OneToMany(mappedBy="agency")
private Set<Login> logins;
public Agency() {
}
public Long getIdLogin(){
return idAgency;
}
public void setIdLogin(Long idLogin) {
this.idAgency = idLogin;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getSubdomain() {
return this.subdomain;
}
public void setSubdomain(String subdomain) {
this.subdomain = subdomain;
}
public Set<Login> getLogins() {
return logins;
}
public void setLogins(Set<Login> logins) {
this.logins = logins;
}
@Override
public String toString() {
return "ID=" + idAgency + ", Sub-Dominio=" + subdomain;
}
}
Login.java
@Entity
@Table(name="login")
@Multitenant
@TenantDiscriminatorColumn(name="tenant_id", discriminatorType=DiscriminatorType.INTEGER, contextProperty=PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT)
@NamedQuery(name="Login.findAll", query="SELECT l FROM Login l")
public class Login implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id_login")
private Long idLogin;
@Column(name="email")
private String email;
@Column(name="master")
private Boolean master;
@Column(name="password")
private String password;
//bi-directional many-to-one association to Agency
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="fk_agency")
private Agency agency;
@Enumerated(EnumType.STRING)
@Column(name="user_role")
private Role role;
@Column(name="tenant_id", insertable=false, updatable=false)
private Long tenantId;
public Login() {
}
public Long getIdLogin() {
return this.idLogin;
}
public void setIdLogin(Long idLogin) {
this.idLogin = idLogin;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public Boolean getMaster() {
return this.master;
}
public void setMaster(Boolean master) {
this.master = master;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public Agency getAgency() {
return this.agency;
}
public void setAgency(Agency agency) {
this.agency = agency;
}
public Long getTenantId() {
return tenantId;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
So utluiz, I’m beginning to think that my business rule is wrong, because what I would like is a registration page where new customers would inform a first login and password system, because in the future new users could be registered. Should I do @Onetoone? Thank you
– João Manolo
@Joãomanolo It really seems that there are inconsistencies, but without knowing the domain of your system it is very difficult to say the "correct" way, since different situations require different solutions. If you need help, I suggest posting a new conceptual question putting your current model. There are some questions like that around here sometimes and they usually get good answers.
– utluiz
Hi @utluiz. It took me a long time to understand what I was doing here, but I came to the conclusion that I was totally wrong in my approach. After thinking a lot and analyzing some projects found on the web I could make some changes doing so what I would like to have done at first. Of course new problems have now arisen, but this is subject for another post. Thanks and Hugs!
– João Manolo
@Joãomanolo Legal! Very good to see that you have managed to learn and evolve. Success!
– utluiz