Hi,
This bug belongs to bug https://sourceforge.net/tracker2/?func=detail&aid=2556896&group_id=133517&atid=727368,
but because that bug is deleted I have to open another one.
I discovered an additional bug concerning super classses/interfaces mapping and have added that to the test case that was attached to bug https://sourceforge.net/tracker2/?func=detail&aid=2556896&group_id=133517&atid=727368.
This bug contains an modified version of that test case to reproduce this bug, so it should be replaced with this version.
The bug: the mapped fields when mapping the super classes/interfaces is ignored.
In the method MappingProcessor.processSuperTypeMapping all the mapped fields are collected in the variable mappedFields. However these fields aren't used during the mapping of a super class/interface, which should!
Example (like in the test case):
We have:
public Member {
private IntWrapper price;
public IntWrapper deterPrice() {
return price;
}
public void setPrice(IntWrapper price) {
this.price = price
}
}
public class Taxer extends Member {
private int softNr;
public int getSofNr() {
return this.sofiNr;
}
public void setSofNr(int sofNr) {
this.sofNr = sofNr;
}
}
We have MemberDto and TaxerDto that look the same.
The mapping config contains a mapping between Member -> MemberDto for the one-way-mapping field price with a customized getter.
The configuration also contains a mapping between Taxer -> TaxerDto.
When mapping an object TaxerDto to an existing Taxer object, Dozer will map the super classes/interfaces first: MemberDto -> Member and then TaxerDto -> Taxer.
First it will map Member -> MemberDto. This mapping will not map the price as it's a one-way mapping. The field is however mapped when Dozer processes the mapping TaxerDto -> Taxer, as a default mapping (we use wildcard is true). It will then throw an exception that it can't find the read method of price in Taxer as it uses the default get method which doesn't exists. It does this to determine the existing value. Ofcourse this is wrong as the field has a special mapping as indicated in the MemberDto -> Member mapping that is ignored however.
The test case will show the bug.
Work around: Duplciate the price mapping from Member-> MemberDto in Taxer -> TaxerDto (see the mapping file of the test case).
Solution: when processing the super classes, the mapped fields should be taken in consideration. This is done after the super classes are mapped, but not while mapping them.
Note that in this bug we assume that Dozer already contains the Super class/interface mapping as desribed in the https://sourceforge.net/tracker2/?func=detail&aid=2556896&group_id=133517&atid=727368. But you could probable also reproduce it without this modification when not using interfaces and only super class mappings.
Good luck,
Ed
advanced test case
Hi,
I was planning to use the workaround as described above, but solved the bug in the meantime as it had more impact on my code then expected.
The solution seem to be rather simple and has a nice performance improvement in my case as it overcomes quite a few double field mappings (or mappings that shouldn't run) because the mapping in the super class/interface was ignored like explained above.
Ofcourse the performance highly depends on the mappings in your super classes/interface.
It makes no sense to provide you with a patch as my code is already patched a lot.
Changes in the code:
In the method MappingProcessor.processSuperTypeMapping I changed one line (the last argument is new):
map(map, srcObj, destObj, true, mapId, mappedFields);
As you can see I specify the mappedFields in the method call when mapping a super class/interface.
So as a consequecuense the signature of this method is changed:
private void map(ClassMap classMap, Object src, Object destObj, boolean bypassSuperMappings, String mapId, List<String> superMappedFields) {
And I added a new method:
private void map(ClassMap classMap, Object src, Object destObj, boolean bypassSuperMappings, String mapId) {
map(classMap, src, destObj, bypassSuperMappings, mapId, null);
}
That simple calls the modfified methods with null as superMappedFields, such that other method calls still work.
In the modified method I changed also only one line of code:
List<String> mappedParentFields = superMappedFields;
And that's it...
So now when mapping the super class, it will consider the already mapped fields and will ignore the fields that are contained in this List.
The test case that's attached to this bug will run successful with this modifications and all dozer tests run just as before these modifications (some tests did already fail).
*** Another IMPORTANT change that I made that I forgot in bug https://sourceforge.net/tracker2/?func=detail&aid=2556896&group_id=133517&atid=727368 is removing the following call:
if (mapping == null) {
mapping = findInterfaceMapping(destClass, srcClass, mapId);
}
In het method call ClassMappings.find(...).
This call is redunant due to the code changes in bug https://sourceforge.net/tracker2/?func=detail&aid=2556896&group_id=133517&atid=727368 and can lead to problems due to conflicts. So you must remove this. It even gives a slightly performance improvement as it's not needed anymore as the new interface/super class code already does all this.
What errors you can expect?:
Suppose that you have an interface:
public interface Item {
Product getProduct();
}
and
public interface ItemLuxe extends Item {
seProduct(Product prd);
}
And we have ItemDto and ItemLuxeDto counterparts.
And a mapping in the dozer xml between Item and ItemDto.
Now suppose that you are mapping an item ItemLuxeDto to an ItemLuxe. Dozer will NOT map the product as you would expect as a default mapping between the field product with wildcard is true!
Why Not?
Easy: Dozer tries to get the ClassMap between ItemLuxeDto -> ItemLuxe, and doesn't find it. It will then try the interfaces (the removed call above) and will find the mapping ItemDto -> Item and will use that one. However, this mapping will NOT map the product as it only contains a getter for the product and no setter (write method), such that it will simple ignore this.
Just make a test case yourself and try it.... I have a test case in my own code, that difficult to copy/paste.
Please let me know how this will be processed?
Goodluck,
Ed
Hi,
I have the same problem.
2 interfaces: I1 and I2 mapped to class C, in custom mappings.
But I use proxy to use this interfaces, so my real class is: ProxyXX.
When I want to use Dozer with I2, using the proxy (or it could be another implementation or sub interface).
First dozer is searching for direct mapping in findClassMap with
ClassMap mapping = (ClassMap) customMappings.get(ClassMapKeyFactory.createKey(srcLookupClass, destLookupClass, mapId));
Of course it does not exist and then:
if (mapping == null) {
mapping = findInterfaceMapping(customMappings, destClass, srcClass, mapId);
}
But the problem is that it returns the first assignable interface found in mappings. So it's not really good for me.
In my case: I1.
The result is that all I1 fields are used but not I2.
How to make findInterfaceMapping return the better interface and not the first one?
Thanks
Christophe
I think that your solution lies in the bugs:
- https://sourceforge.net/tracker2/?func=detail&aid=2556896&group_id=133517&a
tid=727368
- https://sourceforge.net/tracker2/?func=detail&aid=2560608&group_id=133517&atid=727368
I patched it myself and it works just fine....
I have attached my patched dozer jar. You can give it a try. Just make sure you create a class that implements The MapperHelper interface as described in the above bug. It will take care of the unproxy operation.
Works very well. I had the same problems as you.
Ed
Hmmmm can't attach the jar file, as it's just too big (isn't allowed to attache files larger then 256 Kb).
If you want, let me know, I email it to you.
-- Ed
Thanks for your proposition
, but I already wrote a correction.
First I change ClassMapFinder.findClassMap() and comment the findInterfaceMapping
Second time I change findInterfaceMappings to find all interfaces mapping (and not only super interfaces as it actually does)
Third I add:
private void checkForClassMapping(Class srcClass, List superClasses, Class superDestClass) {
List mappings = ClassMapFinder.findInterfaceMappings(this.customMappings, srcClass, superDestClass);
if (mappings != null) {
superClasses.addAll(mappings);
}
[...]
}
Because if I have Superclasses on src and 3 level inheritance on dest, Dozer will not do the job.
With this patches it's OK
Hi,
I hope that with your comment/vote the Dozer team will give this bug higher priority and correct this bug asap.
-- Ed