I have a domain class named Parent as follows
public class Parent {
public List<Child> childList = new ArrayList<Child>();
public void setChildList(List<Child> childList) {
this.childList = childList;
}
public List<Child> getChildList() {
return childList;
}
public void addChild(Child child) {
childList.add(child);
child.setParent(this);
}
// other getters and setters
}
Notice Parent/Child is a bidirectional relationship. A addChild convenience method takes care of the bidirectional references.
And a Child class as follows
public class Child {
private Parent parent;
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
private String descripton;
// other getters and setters
}
I have a Spring form as follows
<form:form method="POST" action="addCommand.xhtml" commandName="command">
<div>
<p>1° child:</p>
<form:input path="childList[0].description"/>
</div>
<div>
<p>2° child:</p>
<form:input path="childList[1].description"/>
</div>
<div>
<p>3° child:</p>
<form:input path="childList[2].description"/>
</div>
<input type="submit"/>
</form:form>
Has someone an idea how to bind a child element through addChild convenience method ? I need it because i want to save a cascace Parent/Child bidirectional relationship in Hibernate.
Added to Jacob’s answer:
Hi Jacob,
The purpose of AutoPopulatingList.ElementFactory’s createElement(int index) method is sets up each Child created “just in time”. Then if you use something as follows:
protected Object formBackingObject(HttpServletRequest request) throws Exception {
Parent parent = new Parent();
parent.setChildList(
new AutoPopulatingList.ElementFactory() {
public Object createElement(int index) {
Child child = new Child();
// You have just added A NEW CHILD to command object
parent.addChild(child);
return child;
}
});
return parent;
}
So if i submit THREE Child objects, my Parent command object will have SIX Child objects instead.
You could TEST it according to:
public class BinderTest {
private Parent parent;
private MockHttpServletRequest request;
private ServletRequestDataBinder binder;
@BeforeMethod
public void setUp() {
parent = new Parent();
parent.setChildList(
new AutoPopulatingList.ElementFactory() {
public Object createElement(int index) {
Child child = new Child();
// You have just added A NEW CHILD to command object
parent.addChild(child);
return child;
}
});
request = new MockHttpServletRequest();
binder = new ServletRequestDataBinder(parent, "parent")
}
@DataProvider(name="bindParameter")
public Object [][] bindParameter() {
return new Object [][] {
{new String [] {"d0", "d1", "d2", "d3", "d4"}}
};
}
@Test(dataProvider="bindParameter")
public void bind(String [] descriptionArray) {
// Notice five childs created (descriptionArray.length)
int i = 0;
for(String description: descriptionArray)
request.addParameter("childList[" + i++ + "].description", description);
// It will fail
// parent.getChildList().size() return TEN Child objects
assertEquals(parent.getChildList().size(), descriptionArray.length);
}
}
So in order to pass it, you need the following
parent = new Parent();
parent.setChildList(
new AutoPopulatingList.ElementFactory() {
public Object createElement(int index) {
Child child = new Child();
// child now reference parent
child.setParent(parent);
return child;
}
});
When the form is submitted, parent comand object will reference Child objects because they has been added to childList. Both bidirectional references have been set up.
When you bind to childList[1].description, this gets translated behind the scenes into
getChildList().get(1).setDescription().Question: does this run as written? I’d expect it to fail, since the list doesn’t have any objects in it to bind to, unless somewhere else (say in a controller) you are adding some empty Child objects to the childList (the alternative to this is to use some kind of lazy list such as Spring’s AutoPopulatingList).
If you are either manually adding Child objects, or using a lazy list (which allows you to specify a factory method for creating new objects), I’d set up the parent/child relationship at the point the Child objects are added to the list.
To set up the parent/child relationship if using a lazy list: For example, if you’re using Spring’s AutoPopulatingList, you can pass in a custom class that implements an ElementFactory interface, which has one method createElement. You could create a ChildElementFactory that takes a Parent object in its constructor, and uses it to set up the relationship.
Maybe code will be clearer: