Note that there are some explanatory texts on larger screens.

plurals
  1. PODozer Not Mapping Collection Properly
    primarykey
    data
    text
    <p>So I am having an issue that someone here might be able to help me out with Dozer.</p> <p><strong>Background:</strong> I have Dozer set up to map my persistence entities to their DTO classes. It is pretty simple, I am just creating an exact replica of my entity class as a POJO and allow dozers wild card to see that the name of the field matches the sources field. I am handling hibernates lazy loading issue with a custom mapper as done <a href="https://stackoverflow.com/questions/5552379/prevent-dozer-from-triggering-hibernate-lazy-loading">here</a>. I am telling Dozer how to map each class via a class that scans for an annotation called @EntityMapping(DTOxxx.class) within the entity. Then it adds it to the mapper addMapping(builder)</p> <p><strong>The issue:</strong> (Read the research at the end, for up to date information, but it will help to gain context by reading all of this too) The issue is that Dozer isn't mapping my collections properly in SOME instances. For example, in my CategoryEntity class I have a collection of other entities that Dozer needs to map. What happens is that dozer finds the collection which has in this case 2 items and only maps 1 item in the new DTO class collection.</p> <p><img src="https://i.stack.imgur.com/48VnB.png" alt="enter image description here"></p> <p>As you can see in the image after toDomain is called (this has the mapper.map(source, desination) dozer call in it) the DTO only has 1 of the 2 objects that it should have mapped into it from the entity. Here is the toDomain method encase you want to see it:</p> <pre class="lang-java prettyprint-override"><code>@Transactional(readOnly=true) public &lt;T extends DomainObject&gt; T toDomain(Class&lt;T&gt; clazz, Entity entity) { if (entity == null) { return null; } T domain = getCachedDomainObjects(clazz, entity.getId()); if (domain == null) { domain = dozerMapper.map(entity, clazz); cacheDomainObject(domain); } return domain; } </code></pre> <p><em>I have made sure its not grabbing a cached entity if you are thinking that.</em></p> <p>So I am a bit stumped as to why this is happening in some cases, and not in other cases. I can't see any obvious differences in the occasions it does work and the occasions it doesn't work. If anyone has ran into an issue like this before and thinks they will be able to help me out, that would be fantastic! Here are my classes from the example of the issue:</p> <p><strong>CategoryEntity.java:</strong></p> <pre class="lang-java prettyprint-override"><code>@EntityMapping(Category.class) @javax.persistence.Entity(name = "categories") public class CategoryEntity implements Entity, PureTable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(unique = true, nullable = false) private int id = Entity.UNSAVED_ID; @OneToMany(mappedBy = "pk.category", fetch = FetchType.LAZY) @Cascade({CascadeType.SAVE_UPDATE}) private Set&lt;IncidentJoinCategoryEntity&gt; incidentJoinCategories = new HashSet&lt;IncidentJoinCategoryEntity&gt;(); @Override public int getId() { return this.id; } public void setId(int id) { this.id = id; } public Set&lt;IncidentJoinCategoryEntity&gt; getIncidentJoinCategories() { return incidentJoinCategories; } public void setIncidentJoinCategories(Set&lt;IncidentJoinCategoryEntity&gt; incidentJoinCategories) { this.incidentJoinCategories = incidentJoinCategories; } } </code></pre> <p>This class has a DTO class that matches its values entirely:</p> <p><strong>Category.java:</strong></p> <pre class="lang-java prettyprint-override"><code>public class Category { int id; Set&lt;IncidentJoinCategory&gt; incidentJoinCategories= new HashSet&lt;IncidentJoinCategory&gt;(); @Override public int getId() { return id; } @Override public void setId(int id) { this.id = id; } public Set&lt;IncidentJoinCategory&gt; getIncidentJoinCategories() { return incidentJoinCategories; } public void setIncidentJoinCategories(Set&lt;IncidentJoinCategory&gt; incidentJoinCategories) { this.incidentJoinCategories = incidentJoinCategories; } } </code></pre> <p><strong>!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! RESEARCH !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</strong></p> <p><strong>EDIT #1:</strong></p> <p>Okay! So I have spent hours debugging this issue to find out what is going on here. Turns out the issue is in MappingProcessor class line 749 (Dozer 5.4.0) or line 766 for latest sources (but I have not checked if this is still an issue in the latest sources, doubt that it is fixed though).</p> <p>This line is</p> <pre><code>((Set) field).addAll(result); </code></pre> <p>What it is trying to map here is a</p> <pre><code>HashSet&lt;IncidentJoinCategoryEntity&gt; </code></pre> <p>The addAll(result) is only adding 1 item to the ((Set) field) collection. Result which has 2 items in it (during debug it is also size 2 I will provide my snapshot of the variables) is only adding 1 value to the ((Set)field) cast.</p> <pre><code>result LinkedHashSet&lt;E&gt; (id=220) map LinkedHashMap&lt;K,V&gt; (id=248) accessOrder false entrySet HashMap$EntrySet (id=251) hashSeed -1187793029 header LinkedHashMap$Entry&lt;K,V&gt; (id=253) keySet HashMap$KeySet (id=5829) loadFactor 0.75 modCount 2 size 2 table HashMap$Entry&lt;K,V&gt;[16] (id=258) threshold 12 useAltHashing false values null field HashSet&lt;E&gt; (id=221) map HashMap&lt;K,V&gt; (id=247) entrySet HashMap$EntrySet (id=5856) hashSeed 1372273954 keySet HashMap$KeySet (id=5821) loadFactor 0.75 modCount 2 size 1 table HashMap$Entry&lt;K,V&gt;[16] (id=5822) threshold 12 useAltHashing false values null </code></pre> <p><img src="https://i.stack.imgur.com/NKx36.png" alt="enter image description here"></p> <p><strong>EDIT #2:</strong></p> <p>Downloaded the source for more debugging:</p> <pre><code>if (field == null) { Class&lt;? extends Set&lt;?&gt;&gt; destSetType = (Class&lt;? extends Set&lt;?&gt;&gt;) fieldMap.getDestFieldType(destObj.getClass()); return CollectionUtils.createNewSet(destSetType, result); } else { System.out.println("----IN----"); // Bug #1822421 - Clear first so we don't end up with the removed orphans again Set ret = (Set) field; ret.clear(); //((Set) field).addAll(result); for(Object res : result) { System.out.println("FOUND " + res.toString()); ret.add(res); } System.out.println("END SIZE " + ret.size()); System.out.println("----OUT----"); return ret; } </code></pre> <p>Output for this case:</p> <pre><code>----IN---- FOUND nz.co.doltech.ims.project.shared.domains.joins.IncidentJoinCategory@3e2 FOUND nz.co.doltech.ims.project.shared.domains.joins.IncidentJoinCategory@3e2 END SIZE 1 ----OUT---- </code></pre> <p>Its output is 2 items, but as you can see @3e2 they are the same item for some reason. So when you call addAll it removes the duplicate and leaves us with just 1 item. Why is Dozer accidentally mapping 2 of the same values? I checked to make sure the source objects collection didn't have the same items doubled up, and sure enough it isn't. Strange indeed.</p> <p><strong>EDIT #3:</strong></p> <p>I have done further tests with little luck here. It is indeed an issue with Dozer mapping 2 of the same values and addAll knocks the duplicate off, making it just one item in the list. Unfortunately I can't debug the recursive methods in addToSet very easily to identify why this is happening.</p> <p>Will update if I figure anything else out, otherwise I am out of ideas on this one haha.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload