• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Problem with Spring MVC complex object data binding

 
Ranch Hand
Posts: 111
PHP Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I am still struggling with Spring MVC with what should be a fairly straightforward problem but what seems to be sparsly documented in Spring MVC documentation.

My project uses Spring MVC and Thymeleaf for the views, but the view rendering engine is not really relevant to the problem.

My application is centered around an Activity class which models an (indoor or outdoor) activity which is organized by a member and where fellow members can subscribe to. An Activity has, among others, a Category field and a Region field, which are dropdown fields which are modeled by Hibernate as many-to-one entities to DB lookup tables which contain an id and description field.

The code for the Activity entity class is as follows, the non relevant fields are omitted to shorten the code:



In the view, the user should be able to select a Region and Category from a select box. THe options are put in the Model using a @ModelAttribute annotated method on the class level.

THe problem is with the binding of the box to the lookup property fields.

For example the Category field is of the ActivityCategory type, which is an entity class containing an id and a description property.

In the view, the select box is filled with the list of possible options (allCategories which contains ActivityCategory instances), Thymeleaf takes care of selecting the current value by matching the "value" attribute value with the list:

</select>

The generated HTML looks like:


As we see, the value attributes contain a string representation of the object itself which is clearly not desired, to show the id values we could use ${cat.id} instead of ${cat} but then the selection of the current value (setting the 'selected="selected"' attribute) does not work anymore. THerefore I implemented a Converter which converts an ActivityCategory object to an int (the id value). In Thymeleaf, the converter is called by using the double accolades {{}}:



THe converter is created and added to Spring:



//In MvcConfig class



Now the HTML shows the id values for the options, which is much more logical:



But it still wrong after submitting, the id value cannot be bound to the Activity object which expects a ActivityCategory instead if an integer value, so a typeMismatch validation error is generated.

My handler method looks like:



I have looked at many posts but still found have no solution for this IMHO pretty trivial issue. How can the String value containing the id be accepted by the handler method and properly converted? Or can we not use the id value for this purpose? Looking for some hints...
 
Bartender
Posts: 1210
25
Android Python PHP C++ Java Linux
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Perhaps you can try the Formatter solution described in this blog.

If you are wondering (like me) why Formatters instead of Converters, I found this paragraph in spring docs useful:

consider the type conversion requirements of a typical client environment such as a web or desktop application. In such environments, you typically convert from String to support the client postback process, as well as back to String to support the view rendering process. In addition, you often need to localize String values. The more general core.convert Converter SPI does not address such formatting requirements directly. To directly address them, Spring 3 introduces a convenient Formatter SPI that provides a simple and robust alternative to PropertyEditors for client environments.

In general, use the Converter SPI when you need to implement general-purpose type conversion logic; for example, for converting between a java.util.Date and and java.lang.Long. Use the Formatter SPI when you’re working in a client environment, such as a web application, and need to parse and print localized field values. The ConversionService provides a unified type conversion API for both SPIs.



(Off topic: Is it just me or is Spring documentation one of the most confusing works of software documentation out there?! It confuses me far more often than it clarifies!)
 
Klaas van Gelder
Ranch Hand
Posts: 111
PHP Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks, this was indeeed the information I was looking for!
My implementation of a Formatter is a little different because of the way Thymeleaf compares the list of <select> values with the "value" attribute value to automatically generate the "selected" attribute.

My formatter lo looks like:



and is registered to Spring (together with the ActivityRegionFormatter for the other lookup field) by:



And now it works as expected!

The only remaining issue is that we have some code duplication because the two Formatter classes are almost the same, they only differ in the generic class that is passed in.
I tried to solve this by using a common interface LookupEntity which is implemented by the two lookup entity classes (ActivityCategory and RegionCategory) and use this common interface to define the formatter but unfortunately that did not work...

And yes I fully agree with you that the Spring documentation is not very clear about this... just as the documentation of many other frameworks and tools!

Karthik Shiraly wrote:Perhaps you can try the Formatter solution described in this blog.

If you are wondering (like me) why Formatters instead of Converters, I found this paragraph in spring docs useful:

consider the type conversion requirements of a typical client environment such as a web or desktop application. In such environments, you typically convert from String to support the client postback process, as well as back to String to support the view rendering process. In addition, you often need to localize String values. The more general core.convert Converter SPI does not address such formatting requirements directly. To directly address them, Spring 3 introduces a convenient Formatter SPI that provides a simple and robust alternative to PropertyEditors for client environments.

In general, use the Converter SPI when you need to implement general-purpose type conversion logic; for example, for converting between a java.util.Date and and java.lang.Long. Use the Formatter SPI when you’re working in a client environment, such as a web application, and need to parse and print localized field values. The ConversionService provides a unified type conversion API for both SPIs.



(Off topic: Is it just me or is Spring documentation one of the most confusing works of software documentation out there?! It confuses me far more often than it clarifies!)

 
Karthik Shiraly
Bartender
Posts: 1210
25
Android Python PHP C++ Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I don't know exactly why it doesn't work, or how to solve it. Since register.addFormatter(Formatter) is taking just a Formatter instance, it has to infer the target class using reflection.
Perhaps its type inference logic can't differentiate between Formatter<LookupEntity<A>> and Formatter<LookupEntity<B>>.

Maybe you can try explicitly associating with the type using the other variant, addFormatter(Class, Formatter).
Give the first argument as ActivityCategory/ActivityRegion, not LookupEntity<ActivityCategory/ActivityRegion>, and see if it works.
 
reply
    Bookmark Topic Watch Topic
  • New Topic