Results 1 to 6 of 6

Thread: Binding many-to-many list object on Spring MVC form

  1. #1
    Join Date
    Dec 2010
    Posts
    175

    Unhappy Binding many-to-many list object on Spring MVC form

    Hi,

    This should be a common scenario where we need to bind a list object to a spring form. However, I'm getting errors while trying to do that. I searched the forum and did find many posts but there are no answers.....

    The solution is working fine using JPA the objects gets persisted very well. The only problem is binding it to a form using Spring MVC.

    The domain model in question is explain here very well http://www.vaannila.com/hibernate/hi...tations-1.html

    For sake of clarity, I will copy the Student class here....
    Code:
    16.@Entity
    17.@Table(name = "STUDENT")
    18.public class Student {
    19. 
    20.    private long studentId;
    21.    private String studentName;
    22.    private Set<Course> courses = new HashSet<Course>(0);
    23. 
    24.    public Student() {
    25.    }
    26. 
    27.    public Student(String studentName) {
    28.        this.studentName = studentName;
    29.    }
    30. 
    31.    public Student(String studentName, Set<Course> courses) {
    32.        this.studentName = studentName;
    33.        this.courses = courses;
    34.    }
    35. 
    36.    @Id
    37.    @GeneratedValue
    38.    @Column(name = "STUDENT_ID")
    39.    public long getStudentId() {
    40.        return this.studentId;
    41.    }
    42. 
    43.    public void setStudentId(long studentId) {
    44.        this.studentId = studentId;
    45.    }
    46. 
    47.    @Column(name = "STUDENT_NAME", nullable = false, length = 100)
    48.    public String getStudentName() {
    49.        return this.studentName;
    50.    }
    51. 
    52.    public void setStudentName(String studentName) {
    53.        this.studentName = studentName;
    54.    }
    55. 
    56.    @ManyToMany(cascade = CascadeType.ALL)
    57.    @JoinTable(name = "STUDENT_COURSE", joinColumns = { @JoinColumn(name = "STUDENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "COURSE_ID") })
    58.    public Set<Course> getCourses() {
    59.        return this.courses;
    60.    }
    61. 
    62.    public void setCourses(Set<Course> courses) {
    63.        this.courses = courses;
    64.    }
    65. 
    66.}
    Question is: How can we bind "courses" on a Spring form??? NOTE: If you can provide an example of List<Course> that should I also work?

    Thank you.

  2. #2
    Join Date
    Dec 2010
    Posts
    175

    Default

    I thought this was a very common use case and many must have already implemented it.

    No one???

  3. #3
    Join Date
    Oct 2005
    Location
    Mobile, AL
    Posts
    345

    Default

    You bind the courses using nested properties. In order to bind nested properties you have to have a ordered collection so that each element can be referenced by index. You have two options, you can tell hibernate to use a TreeSet instead of the HashSet or you can add a transient method to return a "List" of courses and bind the list of courses in the form.

    Here is an example of how you bind nested properties:

    Assumptions: your command object id is "student".

    Code:
    <c:forEach items="${student.courses}" var="i" varStatus="courseRow">
            <input name="student.courses[${courseRow.index}].courseName" type="text"/>
    </c:forEach>

  4. #4
    Join Date
    Dec 2010
    Posts
    175

    Default

    Thank you for replying.

    As of now, I'm able to resolve the issue with the use of CustomCollectionEditor. However, I'm interested in your solution too to see which is easier and efficient.

    I've to bind all the Courses to set of checkboxes, also I'm not sure what you mean by telling hibernate to use Treeset?

    Did you mean change the definition of courses = new HashSet(); to new TreeSet();

    Will this solution work without the using any custom property editors? If you can provide some code sample that will be great.

  5. #5
    Join Date
    Aug 2006
    Location
    Arequipa-Peru / South America
    Posts
    2,796

    Default

    Hello Marty

    Are you sure is?

    Code:
    <c:forEach items="${student.courses}" var="i" varStatus="courseRow">
            <input name="student.courses[${courseRow.index}].courseName" type="text"/>
    </c:forEach>
    and not only?

    Code:
    <c:forEach items="${student.courses}" var="i" varStatus="courseRow">
            <input name="courses[${courseRow.index}].courseName" type="text"/>
    </c:forEach>
    I have this version and only work in this way

    Code:
    <c:forEach items="${wrapControlPreMatriculaModel.presMatriculaWrapPreMatricula}" 
    		var="wrapPreMatricula" varStatus="loopstatus">
         <input type="text"
                name="presMatriculaWrapPreMatricula[${loopstatus.index}].cambio" 
                value="x"/>
    </c:forEach>
    Your orange part do not work on my code if I use

    Code:
    <c:forEach items="${wrapControlPreMatriculaModel.presMatriculaWrapPreMatricula}" 
    	        var="wrapPreMatricula" varStatus="loopstatus">
         <input type="text" 
          	   name="wrapControlPreMatriculaModel.presMatriculaWrapPreMatricula[${loopstatus.index}].cambio" 
               value="x"/>     
    </c:forEach>
    You are repeating twice the color blue

    Code:
    <c:forEach items="${student.courses}" var="i" varStatus="courseRow">
            <input name="student.courses[${courseRow.index}].courseName" type="text"/>
    </c:forEach>
    BTW tiger.spring the same logic can be applied for Spring <form: tags
    - Manuel Jordan

    Kill Your Pride, Share Your Knowledge With All
    The Fear Of The LORD Is The Beginning Of Knowledge, But Fools Despise Wisdom And Discipline. Proverbs 1:7

    Blog


    Technical Reviewer of Apress

    • Pro SpringSource dm Server
    • Spring Enterprise Recipes: A Problem-Solution Approach
    • Spring Recipes: A Problem-Solution Approach, 2nd Edition
    • Pro Spring Integration
    • Pro Spring Batch
    • Pro Spring 3
    • Pro Spring MVC: With Web Flow
    • Pro Spring Security

  6. #6
    Join Date
    Dec 2010
    Posts
    175

    Default

    Marty/DrPompeji, thank you for your replies:

    It seems my issue was not resolved completely. Form, gets rendered properly when the a new request is sent to the controller. However, if there are any errors in the form and it has to be redisplayed then the binding of courses fails.

    Here is the User, Role, and RoleEditor:
    Code:
    public class UserDO {
    	@NotEmpty(message="Username must be between 4 and 7 characters.")
    	@Size(min=4)
    	private String userName;
    	
    	@NotEmpty(message="User must have at least one role.")
    	@Valid
    	private List<Role> roles;
    }
    
    public class Role {
    	private int roleId;
    
            @NotEmpty(message="Role ID cannot be blank.")
    	private String roleName;
    }
    
    public class RoleEditor extends CustomCollectionEditor {
    
    	private List<Role> roles;
    	
    	public RoleEditor(Class collectionType, boolean nullAsEmptyCollection) {
    		super(collectionType, true);
    	}
    	
    	public void setValue( Object object ){
    		if(object!=null)
    			System.out.println("Object is of type - " + object.getClass().getCanonicalName());
    
    			String[] roleIds = (String[])object;
    			roles=new ArrayList<Role>();
    			if(roleIds!=null && roleIds.length<=0)
    			for( int i=0; i<roleIds.length; i++ ){
    	            try {
    	                int id = Integer.parseInt(roleIds[i]);
    	                Role role = new Role();
    	                role.setRoleId(id);
    	                if(id==1) role.setRoleName("ADMIN");
    	                if(id==2) role.setRoleName("EVERYONE");
    	                roles.add(role);
    	            }catch( NumberFormatException ne ){}
    	        }
    		}
    	
    	public Object getValue(){
    		System.out.println("Roles are - " + roles);
            return roles;
        }
    
    }
    Here is the controller for managing user:
    Code:
    @Controller
    @SessionAttributes({"user","userroles"})
    public class UserController {
    
    	@InitBinder 
    	public void bindForm(WebDataBinder binder) {
    		binder.registerCustomEditor(List.class, new RoleEditor(List.class,true));
    	}
    	
    	@RequestMapping(value="/register", method=RequestMethod.GET)
    	public String showForm(Model model){
    		model.addAttribute("user",new UserDO());
    
    		//creating list of roles for the form
    		Role roleAdmin = new Role();
    		Role roleOthers = new Role();
    		roleAdmin.setRoleId(1);roleAdmin.setRoleName("ADMIN");
    		roleOthers.setRoleId(2);roleOthers.setRoleName("EVERYONE");
    		List<Role> roles = new ArrayList<Role>();
    		roles.add(roleAdmin);
    		roles.add(roleOthers);
    		model.addAttribute("userroles",roles);
    		
    		return "register";
    	}
    
    	@RequestMapping(value="/submitUserDeatils", method=RequestMethod.POST)
    	public String formSubmit(@ModelAttribute("user") @Valid UserDO user,BindingResult results, Model model){
    		if(results.hasErrors()){
    			return "register";
    		}
    		return "submitRequest";
    	}
    
    
    //following is the jsp form
    
    <form:form action="submitUserDeatils" modelAttribute="user">
    	<table>
    		<tr>
    			<td>User Name :</td><form:errors path="userName" cssClass="error"/> 
    			<td><form:input path="userName" /></td>
    		</tr>
    		<tr>
    			<td>User Role :</td><form:errors path="roles" cssClass="error"/> 
    			<td>
    				<form:checkboxes path="roles" items="${userroles}" itemLabel="roleName" itemValue="roleId"/>
    			</td>
    		</tr>
    
    		<tr>
    			<td></td>
    			<td><input type="submit" value="Submit" /></td>
    		</tr>
    	</table>
    </form:form>
    So, the form loads fine when GET request is submitted however upon submit (POST) if the form has any errors the list of roles binding with the form fails with following error:
    Code:
    //if I select both roles and submit I get 
    SystemOut     O Object is of type - java.lang.String[]
    SystemOut     O Object is of type - java.util.ArrayList
    ServletWrappe E   SRVE0068E: Uncaught exception thrown in one of the service methods of the servlet: /WEB-INF/views/sample/simpleForm.jsp. Exception thrown : java.lang.ClassCastException: java.util.ArrayList incompatible with [Ljava.lang.String;
    	at com.sample.domain.RoleEditor.setValue(RoleEditor.java:20)
    
    //if I select only one role and submit I get
    Property roles threw exception; nested exception is java.lang.ClassCastException: java.lang.String incompatible with [Ljava.lang.String;
    
    //Also the type of obect in RoleEditor is when one role is selected.
    [6/15/11 14:38:51:167 EDT] 0000001e SystemOut     O Object is of type - java.lang.String
    [6/15/11 14:38:51:199 EDT] 0000001e SystemOut     O Object is of type - java.lang.Integer
    [6/15/11 14:38:51:199 EDT] 0000001e SystemOut     O Object is of type - java.lang.Integer
    [6/15/11 14:38:51:199 EDT] 0000001e SystemOut     O Object is of type - com.sample.domain.Role
    Any suggestions????

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •