Announcement Announcement Module
Collapse
No announcement yet.
Form backing object retrieved by Hibernate problem Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Form backing object retrieved by Hibernate problem

    My form backing object has a Map field.
    When I create new backing object by "new" operator, form is rendered correct:

    form.jsp
    --- cut ---
    <spring:bind path="myForm.myObject.mapField[${key}].stringField">
    <input type="text" name="<c:out value="${status.expression}"/>" value="<c:out value="${status.value}"/>"/>
    </spring:bind>
    --- end cut ---

    but, when I use this form for edit this object retrieved from DB by Hibernate, I has error:
    org.springframework.beans.NullValueInNestedPathExc eption: Invalid property 'myObject.mapField[12]' of bean class [tld.domain.myObject]: Value of nested property 'myObject.mapField[12]' is null
    org.springframework.beans.BeanWrapperImpl.getNeste dBeanWrapper(BeanWrapperImpl.java:482) org.springframework.beans.BeanWrapperImpl.getBeanW rapperForPropertyPath(BeanWrapperImpl.java:399) org.springframework.beans.BeanWrapperImpl.getBeanW rapperForPropertyPath(BeanWrapperImpl.java:400) org.springframework.beans.BeanWrapperImpl.getPrope rtyValue(BeanWrapperImpl.java:537) org.springframework.validation.BindException.getFi eldValue(BindException.java:265) org.springframework.web.servlet.support.BindStatus .(BindStatus.java:115) org.springframework.web.servlet.tags.BindStatus.(B indStatus.java:38) org.springframework.web.servlet.tags.BindTag.doSta rtTagInternal(BindTag.java:103) org.springframework.web.servlet.tags.RequestContex tAwareTag.doStartTag(RequestContextAwareTag.java:7 0)

    at the same time expression <c:out value="${myForm.myObject.mapField[key].stringField}"/> display value of stringField correctly

    I did debug application and find the following conclusion: this error throws becaouse map retrived by hibernate from DB has type net.sf.hibernate.collection.Map, but class org.springframework.beans.BeanWrapperImpl on method getPropertyValue has the folowing logic:
    --- cut ---
    ....
    import java.util.Map
    ....
    else if (value instanceof Map) {
    Map map = (Map) value;
    return map.get(key);
    }
    -- end cut ---

    i.e. problem exists because myObject.mapField has type net.sf.hibernate.collection.Map instead java.util.Map after retrieving by Hibernate,
    net.sf.hibernate.collection.Map ia a persistent wrapper for a java.util.Map.

    I think this problem exists for eny collection mapped to DB with Hibernate.

    Any suggestions about elegant solving of this problem ?

  • #2
    You could add a getter to your backing object that clone Hibernate Map and returns a true Java Map
    Code:
      public Map getJavaMapField&#40;&#41; &#123;
        return new HashMap &#40;mapField&#41;;
      &#125;
    I do not know if this is elegant enough, but you should not use this getter in your business layer to add / update / remove objects as it will disable Hibernate transparent persistance.

    Comment


    • #3
      sorry, I have mistake about assumption of problem reason

      sorry, I have mistake about assumption of problem reason

      net.sf.hibernate.collection.Map implements java.util.Map and piece of code "value instanceof Map" can not be reason for exception as I assumed ...

      problem is on some other code, I do not know where
      but Spring bind tags can not extract collection elements retrieved from DB by Hibernate and genrate exception, at the same time it is work for java collections (java.util.HashMap for example) ...
      I can not explain this yet

      //

      thanx irbouho

      Comment


      • #4
        Just a guess: If you use lazy collections in Hibernate, then these collections can only be accessed as long as the hibernate session is open (typically this session is opened and closed again in the manager or DAO layer). So in this case you cannot access lazy collections in the view (jsp).

        This might explain why it works with the new non-hibernate object, but fails for hibernate managed collections.

        The solution would be the Open-Session-In-View-Pattern. I did not use it yet, but you will certainly find enough information about this, Spring supports it with an interceptor to open the hibernate session.

        My 2ct, hope this helps you!
        Sebastian

        Comment


        • #5
          some inattention

          thanx Sebastian

          but in my first post I did write about some interesting detail: JSTL expression
          Code:
          <c&#58;out value="$&#123;myForm.myObject.mapField&#91;key&#93;.stringField&#125;"/>
          located before <spring:bind> tag on the same form.jsp display this property correrctrly
          thereby lazy initialization innocent in the given situation IMHO

          Comment


          • #6
            it seems I found where is a problem
            my Map field has Integer keys
            Spring binding work correct only in case of String keys

            Comment


            • #7
              I would like to use Long or Integer as a key to a Map, but I was having the same problem with Spring not being able to bind this correctly (Only strings).

              Have you found a solution to this?

              Comment


              • #8
                I have the same problem. But in my case the collection is a java.util.List.

                When I analyzed the collection. I found that the collection.size() returned me 2 instead of 1. When I iterated through the collection, I found there was null object added to the collection. I don't know where this null object comes from, but during the iteration process I removed the null object manually. This resulted in proper binding.

                Could the orginal author of this post verify the Map size against the expected Map size?

                I believe hibernate adds the extra null object. Since I just started playing with Spring/Hibernate all I can do right now is an educated guess.

                I request Experts to share their thoughts.

                Thanks,
                Arun

                Comment


                • #9
                  The issue reported by lis and rosco has a simple cause.

                  The property path attribute used by the bind tag is just a regular String attribute.
                  So when the JSP engine processes JSP code like this:
                  Code:
                  <spring&#58;bind path="myForm.myObject.mapField&#91;$&#123;key&#125;&#93;.stringField">
                  The actual string value received by the BindTag is this:
                  Code:
                  myForm.myObject.mapField&#91;1&#93;.stringField
                  The DataBinder/BeanWrapper divide this path in the various components:
                  myForm, myObject, mapField, [1], stringField (or something similar).

                  Now, at this point, the "[1]" component is just a regular String. While the expression evaluator of an JSP engine may have more information to infer types, the data binding framework only has a String to work with. Spring simply can't obtain the required type information from a Map instance.

                  Of course, one workaround would be to amend the databinding code with something like:

                  Code:
                  // if the path element is a map ...
                  if&#40;!map.isEmpty&#40;&#41;&#41;
                  &#123;
                      Class requiredClass = map.keySet&#40;&#41;.iterator&#40;&#41;.next&#40;&#41;.getClass&#40;&#41;;
                      // Apply existing binding logic, e.g. call doTypeConversionIfNecessary&#40;&#41; etc
                  &#125;
                  This smells like a hack, but may be useful for users binding to Map instances with non-String keys.

                  p.s. It would be a good idea to move this thread to the Web forum

                  Comment


                  • #10
                    Arun,

                    You shoud really use Sets with Hibernate rather than Lists since you will end with a number of stray nulls if you use a List.

                    Rob

                    Comment

                    Working...
                    X