Spring list of data binding interface type - how?

I tried to find the answer on the Internet, but could not. It should be simple for pro Spring Devs ... so here it comes:

In a few words, I want to link the List of interface type: Enumerate the form and return the data (possibly changed by the user through the form. The problem is that it does not work :(

my code (short version) is the command / model class, which is passed to the form:

public class RoomsFormSearchResultCommand extends RoomsFormSearchCommand { @SuppressWarnings("unchecked") private List<IRoom> roomsList = LazyList.decorate(new ArrayList<Room>(), FactoryUtils.instantiateFactory(Room.class)); public List<IRoom> getRoomsList() { return roomsList; } public void setRoomsList(final List<IRoom> roomsList) { this.roomsList = roomsList; } (...) 

then in the form I use it like this (short version):

  <form:form method="post" action="reserve" commandName="roomsResultsCmd"> (...) <c:forEach var="room" items="${roomsResultsCmd.roomsList}" varStatus="status"> <tr> <td><form:input path="roomsList[${status.index}].roomNumber" readonly="true"/> (...) 

The form is displayed normally, but after sending it I get:

 2012-01-22 21:31:55 org.apache.catalina.core.StandardWrapperValve invoke SEVERE: Servlet.service() for servlet [wyspa] in context with path [/wyspa] threw exception [Request processing failed; nested exception is org.springframework.beans.InvalidPropertyException: Invalid property 'roomsList[0]' of bean class [com.wyspa.controller.command.RoomsFormSearchResultCommand]: Illegal attempt to get property 'roomsList' threw exception; nested exception is org.springframework.beans.NullValueInNestedPathException: Invalid property 'roomsList' of bean class [com.wyspa.controller.command.RoomsFormSearchResultCommand]: Could not instantiate property type [com.wyspa.entity.IRoom] to auto-grow nested property path: java.lang.InstantiationException: com.wyspa.entity.IRoom] with root cause org.springframework.beans.NullValueInNestedPathException: Invalid property 'roomsList' of bean class [com.wyspa.controller.command.RoomsFormSearchResultCommand]: Could not instantiate property type [com.wyspa.entity.IRoom] to auto-grow nested property path: java.lang.InstantiationException: com.wyspa.entity.IRoom at org.springframework.beans.BeanWrapperImpl.newValue(BeanWrapperImpl.java:633) at org.springframework.beans.BeanWrapperImpl.growCollectionIfNecessary(BeanWrapperImpl.java:863) at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:770) at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:555) 

(...)

The deal is that when I change the list to the "instances" list, everything works fine!

 public class RoomsFormSearchResultCommand extends RoomsFormSearchCommand { @SuppressWarnings("unchecked") //notice that the List is now List<Room> private List<Room> roomsList = LazyList.decorate(new ArrayList<Room>(), FactoryUtils.instantiateFactory(Room.class)); 

In this case, the data is transferred to the controller appropriately.

Since I'm used to devlop on interfaces, and I'm pretty crazy, I would REALLY prefer not to translate the List<IRoom> (which comes back from the services) to a List<Room> , which apparently matches Spring. Is it possible to work with List<IRoom> in this case, or is Spring just not supporting it?

// Of course, Room implements IRoom - but I think you already got this ...

I would be very happy for any help / suggestions!

Best regards, Nirwan

+4
source share
3 answers

I have exactly the same problem. Going to the next will not fix the problem. It looks like a spring binding ignores utils factory and tries to instantiate a null object:

 @SuppressWarnings("unchecked") private List<IRoom> roomsList = LazyList.decorate(new ArrayList<IRoom>(), FactoryUtils.instantiateFactory(Room.class)); 

The workaround is to automatically increase the nested path in your controller:

 @InitBinder protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) { binder.setAutoGrowNestedPaths(false); super.initBinder(request, binder); } 

The problem is that you will lose a convenient nested path like user.account.address.street. You must ensure that none of the users, accounts, or addresses matter. This causes a lot of problems. That's why I came here, see if I can find a better solution.

+3
source

If you don't need a list for automatic growth, you can save the form object in a session to avoid the unpleasant side effects of disabling automatic growing nested paths.

 @Controller @SessionAttributes(types = RoomsFormSearchResultCommand.class) public final class SearchController { @InitBinder protected void initBinder(final WebDataBinder binder) { binder.setAutoGrowNestedPaths(false); } @RequestMapping(method = RequestMethod.GET) public String showForm(final Model model) { RoomsFormSearchResultCommand form = ... // create or load form model.addAttribute(form); } @RequestMapping(method = RequestMethod.POST) public String onSubmitUpdateCart( @ModelAttribute final RoomsFormSearchResultCommand form, final BindingResult result, final SessionStatus status) { // if result has no errors, just set status to complete status.setComplete(); } } 
+1
source

Try the following lines

 @SuppressWarnings("unchecked") private List<IRoom> roomsList = LazyList.decorate(new ArrayList<IRoom>(), FactoryUtils.instantiateFactory(Room.class)); 

not enough time to try this, but it makes sense.

0
source

Source: https://habr.com/ru/post/1392373/


All Articles