Announcement Announcement Module
Collapse
No announcement yet.
Multiple Select Not Saving Data Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Multiple Select Not Saving Data

    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...
    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>
    Database Example...
    Code:
     inventory_x_property_pk | inventory_fk | property_fk
    -------------------------+--------------+-------------
                           1 |           39 |         116
                           2 |           39 |         160
    (2 rows)
    POJO...

    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 .....
    
    }
    Controller...

    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();
    		}
    	}
    
    }
    Custom property editor:
    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();
    		
    	}
    	
    }
    Finally... the view:

    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>
    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.

    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

  • #2
    Is this the best way to implement it?

    I got it working but would like someone to confirm this is the right implementation, it's not much extra code, but I dont want to be rewriting something spring can achieve by convention.

    Just added this to the top of the controller to parse the HttpRequest header:

    Code:
    		String[] propertyIds = request.getParameterValues("properties");
    		
    		inventory.clearProperties(); // if editing, clear them so it deletes if you chose to unassign any or all.
    		
    		if (propertyIds != null)
    			for (int i = 0; i < propertyIds.length; i++)
    				inventory.addProperty(this.tvi.loadProperty(new Integer(propertyIds[i]).intValue()));
    		
    		... validation / submission ...

    Comment

    Working...
    X