Announcement Announcement Module
Collapse
No announcement yet.
Form "Complex" Model (nested entities): MVC, SWF Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Form "Complex" Model (nested entities): MVC, SWF

    Hello,

    I have some problems to understand if there are some differences in how form/model binding works in Spring MVC / Spring Web Flow; in particular when I use "complex" models (nested entities).

    I'm so sorry, but I wasn't able to find relevant documentation about possible differences, so let me explain through a very simple example.

    First, MVC case; then Web Flow case.

    Environment: SWF 2.3.1.RELEASE + Spring 3.1.0.RELEASE

    *** MVC case ***

    I have two objects: Bogus and NestedBogus

    First object: Bogus

    Code:
    package org.sbibiz.mvcjpa.bogus;
    
    import java.io.Serializable;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class Bogus implements Serializable {
    
    	private static final long serialVersionUID = 1L;
    
    	private static final Logger logger = LoggerFactory.getLogger(Bogus.class);
    
    	public Bogus() {
    		
    		// NOTICE THE LOGGER HERE!
    		
    		logger.info("Bogus Constructor called");
    	}
    
    	private String id;
    
    	private String name;
    
    	private NestedBogus nestedBogus;
    
    	public String getId() {
    
    		return id;
    	}
    
    	public void setId(String id) {
    
    		this.id = id;
    	}
    
    	public String getName() {
    
    		return name;
    	}
    
    	public void setName(String name) {
    
    		this.name = name;
    	}
    
    	public NestedBogus getNestedBogus() {
    
    		return nestedBogus;
    	}
    
    	public void setNestedBogus(NestedBogus nestedBogus) {
    
    		this.nestedBogus = nestedBogus;
    	}
    }
    Second object: NestedBogus

    Code:
    package org.sbibiz.mvcjpa.bogus;
    
    import java.io.Serializable;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class NestedBogus implements Serializable {
    	
    	private static final long serialVersionUID = 1L;
    
    	private static final Logger logger = LoggerFactory
    			.getLogger(NestedBogus.class);
    
    	public NestedBogus() {
    		
    		// NOTICE THE LOGGER HERE!
    		
    		logger.info("Nested Bogus Constructor called");
    	}
    
    	private String id;
    
    	private String name;
    
    	public String getId() {
    
    		return id;
    	}
    
    	public void setId(String id) {
    
    		this.id = id;
    	}
    
    	public String getName() {
    
    		return name;
    	}
    
    	public void setName(String name) {
    
    		this.name = name;
    	}
    }
    I have this controller (GET snippet):

    Code:
    @RequestMapping(value = "/bogus", method = RequestMethod.GET)
    public String ordering_GET(Locale locale, Model model) {
    
    	model.addAttribute("myBogus", new Bogus());
    
    	return "bogus";
    }
    And the view (FORM snippet):

    HTML Code:
    <sf:form action="${myURL}" method="post" modelAttribute="myBogus">
    	bogus.id: <sf:input path="id"/><br/>
    	bogus.name: <sf:input path="name"/><br/>
    	bogus.nestedBogus.id: <sf:input path="nestedBogus.id"/><br/>
    	bogus.nestedBogus.name: <sf:input path="nestedBogus.name"/>
    	<input type="submit" value="Go, Bogus! Go!" />
    </sf:form>
    EVERYTHING WORKS FINE!

    And the logger says to me:

    INFO : org.sbibiz.mvcjpa.bogus.Bogus - Bogus Constructor called
    INFO : org.sbibiz.mvcjpa.bogus.NestedBogus - Nested Bogus Constructor called

    *** SWF case ***

    In a flow, now, I have this view state (conf snippet):

    HTML Code:
    <var name="myBogus" class="org.sbibiz.mvcjpa.bogus.Bogus" />
    ...
    <view-state id="start" view="bogus" model="myBogus">
    ...
    </view-state>
    When I request the flow... EVERYTHING STOPS WORKING... :-(

    And I get:

    Code:
    INFO : org.sbibiz.mvcjpa.bogus.Bogus - Bogus Constructor called
    ERROR: org.springframework.web.servlet.tags.form.InputTag - An ELException occurred getting the value type for expression 'nestedBogus.id' on context [class org.sbibiz.mvcjpa.bogus.Bogus]
    org.springframework.binding.expression.EvaluationException: An ELException occurred getting the value type for expression 'nestedBogus.id' on context [class org.sbibiz.mvcjpa.bogus.Bogus]
    	at org.springframework.binding.expression.spel.SpringELExpression.getValueType(SpringELExpression.java:105)
    	at org.springframework.webflow.mvc.view.BindingModel.getFormattedValue(BindingModel.java:228)
    	at org.springframework.webflow.mvc.view.BindingModel.getFieldValue(BindingModel.java:142)
    	at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:120)
    	at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:178)
    	at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getPropertyPath(AbstractDataBoundFormElementTag.java:198)
    	at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getName(AbstractDataBoundFormElementTag.java:164)
    	at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.autogenerateId(AbstractDataBoundFormElementTag.java:151)
    	at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.resolveId(AbstractDataBoundFormElementTag.java:142)
    	at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.writeDefaultAttributes(AbstractDataBoundFormElementTag.java:126)
    [...]
    Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E:(pos 12): Field or property 'id' cannot be found on null
    	at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:205)
    	at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:72)
    	at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:57)
    	at org.springframework.expression.spel.standard.SpelExpression.getValueType(SpelExpression.java:117)
    	at org.springframework.binding.expression.spel.SpringELExpression.getValueType(SpringELExpression.java:100)
    	... 71 more
    [...]
    It seems to me that in the first case Spring "takes care" to set up the model properly.

    In the second one, I'm able to make it work only if I modify Bogus.java a little: private NestedBogus nestedBogus = new NestedBogus(); But that's not what I'd like...

    Am I wrong? Have I forgot something? Or what am I ignoring?

    Thank you very much!

    Regards.
    Last edited by emilio.remogna; Nov 27th, 2012, 09:00 AM. Reason: environment infos added

  • #2
    You should always be setting up your model properly yourself. Apparently Spring MVC does it for you in the first example, but I would consider this a bug - or at least a design pattern that should not be used. Dont rely on Spring to initialize your model, do it yourself always!

    Comment


    • #3
      Thank you for your reply and your suggestion, goldman.

      I think you're right when you say that it's better to set up models without relying on framework.
      This way offers greater control.

      I have to admit, then, that I've never noticed before this MVC's behaviour (usually I do all the stuff to initialize my models).

      Regards

      e.

      Comment

      Working...
      X