Menu

Dozer suggestions

seb
2005-08-17
2013-05-02
  • seb

    seb - 2005-08-17

    Hi guys,

    Thx for the new release :)

    Each day comes with a least one new suggestion :

    1/ I have a bean with fields and getter()/setter() methods, and another method called getX() which IS NOT a getter() method (no X field behind). When i want to map my bean (wildcards enabled), i get the following error message :
    Unable to determine write method for field: X
    I have to explicitly add a <field-exclude> section in my dozerBeanMapping.xml to avoid it. No other way ?

    2/ The bug where lower level objects were new()ed is fixed. Perfect. But the problem still remain for List (and probably Array) type fields. I have a class named Test1 with a field which is a List, and containing 2 objects. I map it on Test2 class with an empty List.
    My field in Test2 now contains 2 objects. And now, i map Test2 on Test1. The field in Test1 now contains my 2 objects twice !! (actually 4 objects ;) ). You can't update objects in a List-type field.
    I think it'll be safe to compare objects in List fields (with equals() method) before adding them.

    3/ The last one ;)
    I have to use Dozer with an application server (Websphere). I have many EARs running on it, each containing one or more WARs.
    Castor jar is still used by the home-made framework, and so, added in ws.ext.dirs.
    I want to define a dozerBeanMapping.xml file in each WAR (i don't want to share mappings).
    At this time, i found only one way to solve my 'problem' :

    - put dozer.jar in WEB-INF/lib
    - put dozerBeanMapping.xml at the root of my Java Source folder
    - alter the source code of Dozer :
        * in the static{} block of net.sf.dozer.util.mapping.Mapper, change the line (108) :
            org.exolab.castor.mapping.Mapping castorMapping = new org.exolab.castor.mapping.Mapping();
        with
            org.exolab.castor.mapping.Mapping castorMapping = new org.exolab.castor.mapping.Mapping(Thread.currentThread().getContextClassLoader());
       
        * read() method of net.sf.dozer.util.mapping.util.Map, change the line (60) :
            org.exolab.castor.mapping.Mapping castorMapping = new org.exolab.castor.mapping.Mapping();
        with
            org.exolab.castor.mapping.Mapping castorMapping = new org.exolab.castor.mapping.Mapping(Thread.currentThread().getContextClassLoader());

    It works fine !

    So, here's a suggestion :
    add a new register() method, where you can specify :
        - classLoader
        - mapping file(s)
    This method could be called before the Mapper.map(...) one.
    In a idyllic vision, i can add Dozer.jar in ws.ext.dirs (no more jars in the WEB-INF/lib of my WARs), and each WAR can choose its own classloader and its mapping file(s).

    Et voil !

    Bye
    Seb

     
    • Franz Garsombke

      Franz Garsombke - 2005-08-17

      1. This has been added to the code and will be released today.

      2. Need to research this one.

      3. This should be finished by the end of today.

       
    • Franz Garsombke

      Franz Garsombke - 2005-08-17

      1. I did find a bug with this. Dozer was adding 2 more objects which were ArrayLists. I fixed this. I still think the functionality should be to add more objects from prime. How do we know what should and shouldn't be added?

      So A has an arraylist with 2 AFOO objects. We map this to prime..and the AFOO objects had a classmap with AFOOPRIME. So now APRIME has an arraylist with 2 AFOOPRIME objects. Mapping prime back to AFOO we should see 4 AFOO objects. It currently works this way.

      Let me know if you have questions.

      Franz

       
    • seb

      seb - 2005-08-18

      Hi,

      I found the bug too while testing your example :)
      I'll try to explain my point of view better than in my first message ...

      Suppose i have A with a field aList (ArrayList) containing 2 AFoo objects. I also have APrime with a field aList containing AFoo-type objects.

      I map A to APrime. (The bug is fixed ;-) ). I have APrime with 2 AFoo objects in its aList field.
      I modify the first AFoo object in APrime.
      Now, i want to map back APrime to A in order to UPDATE my first AFoo object in A.

      In your example, i'll have 4 AFoo objects in A, true ?
      So dozer doesn't use the setter() method on A for its aList field ??
      If i understand well, dozer makes something like that (sorry for this very simplified vision ;-) ) :

      Iterator it = aPrime.getAList().iterator();
      while (it.hasNext()) {
        a.getAList().add(it.next());
      }

      I think this will be better (this code is NOT optimized of course) :

      Iterator it = aPrime.getAList().iterator();
      while (it.hasNext()) {
        if (a.getAList().contains(it.next())) {
          a.getAList().set(a.getAList().indexOf(it.next()),
            Mapper.map(it.next(), AFoo.class));
        } else {
          a.getAList().add(it.next());
        }
      }

      As you can see, it's not optimized at all. You just have to code the equals() method on AFoo class in order to make contains() and indexOf() methods work. In this case, the behaviour for list-type fields will be 'add or update' like.

      I wish i've been clear with my explanations ...

      Bye
      Seb

       
    • seb

      seb - 2005-08-18

      Sorry, but i miss something on my previous post :

      The new method should be :

      Iterator it = aPrime.getAList().iterator();
      while (it.hasNext()) {
        AFoo afoo = (AFoo) Map(it.next(), AFoo.class);
        if (a.getAList().contains(afoo)) {
         int index = a.getAList().indexOf(afoo);
         a.getAList().set(index, Mapper.map(afoo, a.getList().get(index));
        } else {
         a.getAList().add(afoo);
        }
      }

       
    • Franz Garsombke

      Franz Garsombke - 2005-08-18

      Hey Seb -

      I will put this fix in today. Tell me if it what you were expecting :) Our unit tests seems to reflect that it works. I am sure you will find another scenario that doesn't work :)

      Franz

       
    • Franz Garsombke

      Franz Garsombke - 2005-08-19

      I think we decided that determining whether or not something should be updated is not the direction we want to go in. When we are doing a mapping to FooPrime's List our test case has a conversion from one List of complex objects to an entirely different List of complex objects. When we map those back I guess you could say we are comparing apples and oranges. I feel much safer doing a generic 'add' for everything. Thoughts?

       
    • Richard Lemesle

      Richard Lemesle - 2005-08-19

      Hi,

      I can't agree with you with that :
      "When we map those back I guess you could say we are comparing apples and oranges"

      We don't compare apples and oranges, we map apples to oranges and then we compare those oranges with the previous ones.

      Look at this example :

      Tom : Person
         - child Peter : Person
              age : 12
              ...
         - child Sarah : Person
              age : 10
              ...
      ...

      Now I map that to a structure PersonDTO that only take some of these informations :

      Tom : PersonDTO
         - child Peter : PersonDTO
              age : 12
         - child Sarah : PersonDTO
              age : 10

      Then I need to modify Sarah age (age is probably not a good property choice ;-) :

      Tom : PersonDTO
         - child Peter : PersonDTO
              age : 12
         - child Sarah : PersonDTO
              age : 11

      Now I want to map back my DTOs to my Person objects and I actually obtain that :

      Tom : Person
         - child Peter : Person
              age : 12
              ...
         - child Sarah : Person
              age : 10
              ...
         - child Peter : Person
              age : 12
              ...
         - child Sarah : Person
              age : 11
              ...
      ...

      I think that a good way to solve that is to put a flag on 1-N relationships attributes (in the mapping file)to tell dozer if it's a "cumulative" mapping or a "non cumulative" one.

      A default value for this property may be defined in the same way the wildcard property is defined.

      Am I wrong ?

      Richard.

       
    • Ben Sullins

      Ben Sullins - 2005-08-19

      Ok, this is a very good example.  The Dozer team will talk it over and see what we can do.

       
    • Franz Garsombke

      Franz Garsombke - 2005-08-19

      Great point. I will put this in the next release.

       
    • Franz Garsombke

      Franz Garsombke - 2005-08-19

      Look under the simple property mapping listing for documentation. Basically -

      <field relationshipType="cumulative"> default

      <field relationshipType="non-cumulative">

       
    • seb

      seb - 2005-08-22

      Hi guys,

      Thx for this awesome release !! Great work !
      But it seems i've found a little bug easily fixeable ;-)

      I was testing cumulative/non cumulative feature. At the end, some data was missing ...

      Here's my 'example' :

      Consider A class with a list field which contains AFoo objects (AFoo has a, b and c fields).
      Consider APrime class with a lis field which contains AFooPrime objects (AFooPrime has only a and b fields).
      equals() method for AFoo and AFooPrime just compares a fields.
      I'm on non-cumulative mode.

      I have a A instance with 1 AFoo object like that :
      a = "a"
      b = null
      c = "c"

      Let's map A on APrime. My AFooPrime object is :
      a = "a"
      b = null

      OK. Let's modify AFooPrime loke that :
      a = "a"
      b = "b"

      And now, let's map APrime on A. I should have my AFoo like that :
      a = "a"
      b = "b"
      c = "c" ... but this one is null instead :(

      Looking into the source code, i found your 'mistake'.
      In MappingProcessor, line 488. set() method just replace the object, but doesn't update it. I fixed this by addind the following line before result.set(...) :

      map(result.get(index), destValue, null);

      Don't know if it's the better way, but it works for me :-) My AFoo object is not well updated 8-)

      (... launching a new test unit ... ;-) )

      Bye
      Seb

       
      • Franz Garsombke

        Franz Garsombke - 2005-08-22

        Thanks for your diligence Seb. I put your patch in. Just let me know when you want to go full time on dozer :). You have definitely help the application become more bullet-proof.

        Franz

         

Log in to post a comment.