Hi all,
This is probably really basic but I can't find any other posts or examples using this.
I originally had a form with a list of 'properties' (aka clients) in a simple single form:select and everything worked fine. Then I found I needed to make it a many-to-many relationship so I updated the hibernate mapping and pojo getter/setter methods to allow assigning it that way.
Now it lists the properties in the multiple select but no matter how many I select when I post the data the controller doesn't see anything selected but saves the other form data. When I manually add some inventory -> property links in the DB the edit form does select them when the view is rendered so my getter and property editor must be working fine.
Here's the code snippits. I think the problem lies in my POJO getter/setters as I followed the petclinic example app for the many-to-many relationship but they don't have it saving data, only retrieving.
Hibernate Mapping...
Database Example...Code:<set name="propertiesInternal" table="inventory_x_property" schema="tvi"> <key column="inventory_fk"/> <many-to-many column="property_fk" class="ovm.centraltvi.Property"/> </set>
POJO...Code:inventory_x_property_pk | inventory_fk | property_fk -------------------------+--------------+------------- 1 | 39 | 116 2 | 39 | 160 (2 rows)
Controller...Code:public class Inventory extends BaseEntity { static Log log = LogFactory.getLog(Inventory.class); private TV tv; private Set<Property> properties; private String serial; private Set<InventoryHistory> inventoryHistory; protected Set<Property> getPropertiesInternal() { if (this.properties == null) { this.properties = new HashSet<Property>(); } log.info("-- IN Inventory GetPropertiesInternal -- with selected properties: " + this.properties.size()); return this.properties; } protected void setPropertiesInternal(Set<Property> properties) { log.info("-- IN Inventory SetPropertiesInternal -- with selected properties: " + properties.size()); this.properties = properties; } public List<Property> getProperties() { List<Property> sortedHistory = new ArrayList<Property>(getPropertiesInternal()); PropertyComparator.sort(sortedHistory, new MutableSortDefinition("name", true, false)); log.info("-- IN Inventory GetProperties -- with selected properties: " + sortedHistory.size()); return Collections.unmodifiableList(sortedHistory); } public void setProperties(Set<Property> properties) { log.info("-- IN Inventory SetProperties -- with selected properties: " + properties.size()); setPropertiesInternal(properties); } public Integer getPropertiesLength() { log.info("-- IN Inventory getPropertiesLength -- with selected properties: " + this.properties.size()); return this.properties.size(); } public void addProperties(Property property) { log.info("-- IN Inventory addProperties: " + property.getName()); getPropertiesInternal().add(property); } ...... other members ..... }
Custom property editor:Code:-at-Controller -at-RequestMapping("/inventoryAdd.do") -at-SessionAttributes("inventory") public class InventoryAddForm { static Log log = LogFactory.getLog(InventoryAddForm.class); private final TVI tvi; -at-Autowired public InventoryAddForm(TVI tvi) { this.tvi = tvi; } -at-ModelAttribute("tvs") public Collection<TV> populateTVs() { return this.tvi.getTVs(); } -at-ModelAttribute("properties") public Collection<Property> populateProperties() { return this.tvi.getProperties(); } -at-RequestMapping(method = RequestMethod.GET) public String setupForm(Model model) { model.addAttribute("inventory", new Inventory()); return "inventoryForm"; } -at-RequestMapping(method = RequestMethod.POST) public String processSubmit(-at-ModelAttribute("inventory") Inventory inventory, BindingResult result, Model model, SessionStatus status) { log.info("Attempting to add inventory: [" + inventory.getTv().getId() + "] " + inventory.getTv().getMakeModel()); log.info("Properties Selected Count: " + inventory.getPropertiesLength()); java.util.List<Property> props = inventory.getProperties(); for (int i = 0; i < inventory.getPropertiesLength(); i++) log.info("Property found at " + i + " = " + props.get(i)); new InventoryValidator().validate(inventory, result); if (result.hasErrors()) { log.info("Failed adding inventory due to validation errors."); return "inventoryForm"; } else { log.info("Storing inventory."); this.tvi.storeInventory(inventory); status.setComplete(); log.info("Returning with inventory id: " + inventory.getId()); return "redirect:inventoryEdit.do?inventoryId=" + inventory.getId(); } } }
Finally... the view:Code:public class PropertyEditor extends PropertyEditorSupport { static Log log = LogFactory.getLog(PropertyEditor.class); private final TVI tvi; public PropertyEditor(TVI tvi) { this.tvi = tvi; } -at-Override public void setAsText(String text) throws IllegalArgumentException { log.info("Searching for Property with ID: " + text); if (text.equals("null")) { setValue(null); return; } Property prop = this.tvi.loadProperty(Integer.parseInt(text)); if (prop.getId() != null) { log.info("Found matching Property with ID: " + text); setValue(prop); } else log.warn("Unable to find matching Property with ID: " + text); } -at-Override public String getAsText() { Property property = (Property)getValue(); if (property == null) { log.info("Returning text id for Property as EMPTY"); return ""; } log.info("Returning text id for Property: " + property.getId().toString()); return property.getId().toString(); } }
When I log the count of properties selected in my submit function on the controller it's always 0 regardless of what is selected when I'm adding a new item so I'm thinking there is a binding disconnect somewhere.Code:<form:form modelAttribute="inventory"> <table> <tr> <th> TV: <form:errors path="tv" cssClass="errors"/> <br/> <form:select path="tv" items="${tvs}" itemValue="id" itemLabel="makeModel" /> </th> </tr> <tr> <th> Property: <form:errors path="properties" cssClass="errors"/> <br/> <form:select path="properties" > <!-- <form:option value="null" label="- NONE -" /> --> <form:options items="${properties}" itemValue="id" itemLabel="name"/> </form:select> </th> </tr> <tr> <th> Serial #: <form:errors path="serial" cssClass="errors"/> <br/> <form:input path="serial" size="30" maxlength="80"/> </th> </tr> <tr> <td> <c:choose> <c:when test="${inventory.new}"> <p class="submit"><input type="submit" value="Add Inventory"/></p> </c:when> <c:otherwise> <p class="submit"><input type="submit" value="Update Inventory"/></p> </c:otherwise> </c:choose> </td> </tr> </table> </form:form>
Thanks for any help you can give this Spring newbie... However, I must admit that I really like what I have seen so far in the MVC world of java development.
Sincerely,
Lindsey


Reply With Quote
