Reputation: 6606
I'm newer to Spring and Java and working on getting a form to work in Spring MVC, using JSP, JSTL, Hibernate/JPA and a model class. I can get the page to display fine, but the post that is supposed to create the user is giving me an error.
Failed to convert property value of type java.lang.String to required type com.everesttech.recruiting.searchtool.entity.Role for property user.role; nested exception is java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [com.everesttech.recruiting.searchtool.entity.Role] for property role: no matching editors or conversion strategy found
This error makes sense because the form is passing back an ID for the Role while the User object expects a Role object to be passed. However I'm not sure how to work around this, while still allowing my JSP to be backed by my actual Entity objects as I want to use Spring/Hibernate validations. Is there just a way to declare that form:select as a Role object? Any advice or solutions are much appreciated. Thanks in advanced.
I have provided the relevant sections of code from my project. I did not include code from my service layer and entity layer since I know they are working. However I can include those as well if needed. User has a many to one relationship with Role.
UserController
/**
* display the user add page
*
* @return returns the view name
*/
@RequestMapping(value="/user/add", method=RequestMethod.GET)
public ModelAndView showUserAdd(ServletRequest request, @ModelAttribute("userModel") UserModel userModel, ModelMap modelmap) {
ModelMap modelMap = new ModelMap();
userModel.setUser(new User());
userModel.setRoles(userService.getRoles());
modelMap.addAttribute("userModel", userModel);
return new ModelAndView("user/add", modelMap);
}
/**
* handle post request to handle user creation, if successful displays user list page
*
* @param user User object from JSP
* @param result BindingResult from JSP
* @return returns view name
*/
@RequestMapping(value="/user/add", method=RequestMethod.POST)
public String userAdd(UserModel userModel, BindingResult result) {
if (result.hasErrors()) {
logger.warn("Error binding UserModel for /user/add, returning to previous page");
List<ObjectError> errors = result.getAllErrors();
for (ObjectError error : errors) {
logger.warn(error.getCode() + " - " + error.getDefaultMessage());
}
return "user/add";
}
try {
User user = userModel.getUser();
userService.addUser(user);
}
catch (DuplicateKeyException e) {
logger.warn("Duplicate record found in User table", e);
}
catch (Exception e) {
logger.error("Error occurred while trying to create a new user", e);
}
return "/user";
}
This is the model I am passing to the JSP witch contains a User object, which is the Hibernate Entity, and a list of Roles, another hibernate Entity.
import java.util.List;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import com.everesttech.recruiting.searchtool.entity.Role;
import com.everesttech.recruiting.searchtool.entity.User;
@Scope(value="request")
@Component("userModel")
public class UserModel {
public User user;
public List<Role> roles;
public UserModel() {
}
public UserModel(User user, List<Role> roles) {
this.user = user;
this.roles = roles;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
@Override
public String toString() {
return "UserModel [user=" + user + ", roles=" + roles + "]";
}
}
Here is my JSP.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="sf" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-8 col-md-6">
<h3>User - Add</h3>
<br>
<spring:hasBindErrors name="userModel">
<div class="alert alert-danger">
<sf:errors path="userModel.user.firstName"></sf:errors>
<sf:errors path="userModel.user.lastName"></sf:errors>
<sf:errors path="userModel.user.email"></sf:errors>
<sf:errors path="userModel.user.userName"></sf:errors>
<sf:errors path="userModel.user.password"></sf:errors>
<sf:errors path="userModel.user.role"></sf:errors>
</div>
</spring:hasBindErrors>
<c:url var="formAction" value="/user/add" />
<sf:form commandName="userModel" modelAttribute="userModel" method="post" action="${formAction}">
<div class="form-group">
<label for="first-name">First Name</label>
<sf:input path="user.firstName" id="first-name" class="form-control" placeholder="First Name" />
</div>
<div class="form-group">
<label for="last-name">Last Name</label>
<sf:input path="user.lastName" id="last-name" class="form-control" placeholder="Last Name" />
</div>
<div class="form-group">
<label for="email">Email</label>
<sf:input path="user.email" id="email" class="form-control" placeholder="Email" />
</div>
<div class="form-group">
<label for="user-name">Username</label>
<sf:input path="user.userName" id="user-name" class="form-control" placeholder="Username" />
</div>
<div class="form-group">
<label for="password">Password</label>
<sf:password path="user.password" id="password" class="form-control" placeholder="" />
</div>
<div class="form-group">
<label for="confirm-password">Confirm Password</label>
<input type="password" id="confirm-password" class="form-control" placeholder="" />
</div>
<div class="form-group">
<label for="role">Role</label>
<sf:select path="user.role" id="role" class="form-control" >
<c:forEach var="r" items="${userModel.roles}">
<sf:option value="${r.getId()}">${r.getFriendlyName()}</sf:option>
</c:forEach>
</sf:select>
</div>
<button type="submit" class="btn btn-default">Save</button>
<button type="button" class="btn btn-default">Cancel</button>
</sf:form>
</div>
</div>
</div>
Upvotes: 1
Views: 4139
Reputation: 6606
For anyone interested I found a solution in the Spring documentation. I had to create a class which would handle the conversion from String to Role and register it. This has to make a trip to the database to create the Role object but I don't see another way of handling it.
Here is my class.
import javax.inject.Inject;
import org.springframework.core.convert.converter.Converter;
import com.everesttech.recruiting.searchtool.dao.RoleDao;
import com.everesttech.recruiting.searchtool.entity.Role;
final class StringToRole implements Converter<String, Role> {
@Inject
RoleDao roleDao;
@Override
public Role convert(String source) {
return roleDao.find(Long.parseLong(source));
}
}
I added/edited the following in my mvc-context.xml file.
<!-- Enables the Spring MVC @Controller programming model -->
<mvc:annotation-driven conversion-service="conversionService"></mvc:annotation-driven>
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<list>
<bean class="com.examplecompany.exampleapp.example.converter.StringToRole"/>
</list>
</property>
</bean>
This works and I'm now able to submit the form with no issues. However now my hibernate validations are not appearing on the JSP when the User object does not meet the validation requirements. As an example First Name has a @NotBlank annotation. I can see that is being detected and I'm logging it. However they are not appearing on the jsp in the form:error tags. If anyone knows how to solve that issue please let me know.
Upvotes: 2