推土机不正确地映射集合

时间:2013-06-27 23:39:20

标签: java hibernate collections mapping dozer

所以我遇到一个问题,这里有人可以帮我解决Dozer。

背景:我已将Dozer设置为将我的持久性实体映射到其DTO类。这很简单,我只是创建一个我的实体类的精确复制品作为POJO并允许推土机外卡看到该字段的名称与sources字段匹配。我正在使用自定义映射器处理hibernates延迟加载问题here。我告诉Dozer如何通过一个类来映射每个类,该类扫描实体中名为@EntityMapping(DTOxxx.class)的注释。然后将它添加到mapper addMapping(builder)

问题:(最后阅读研究,了解最新信息,但这也有助于通过阅读所有内容来获取背景信息) 问题是Dozer没有在SOME实例中正确映射我的集合。例如,在我的CategoryEntity类中,我有一组Dozer需要映射的其他实体。发生的事情是,推土机在这种情况下找到了2个项目的集合,并且只在新的DTO类集合中映射了1个项目。

enter image description here

正如你在toDomain调用之后在图像中看到的那样(这里有mapper.map(源,desination)推土机调用)DTO只有它应该从实体映射到它的2个对象中的1个。这是你想看到它的toDomain方法:

@Transactional(readOnly=true)
public <T extends DomainObject> T toDomain(Class<T> 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;
}

如果您正在考虑的话,我已经确定它不会抓取缓存的实体。

所以我有点难以理解为什么在某些情况下会发生这种情况,而不是在其他情况下。在它工作的场合和不起作用的场合,我看不出任何明显的差异。如果有人之前遇到过这样的问题,并认为他们能够帮助我,那就太棒了!以下是问题示例中的类:

CategoryEntity.java:

@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<IncidentJoinCategoryEntity> incidentJoinCategories =
        new HashSet<IncidentJoinCategoryEntity>();

    @Override
    public int getId() {
        return this.id;
    }
    public void setId(int id) {
        this.id = id;
    }

    public Set<IncidentJoinCategoryEntity> getIncidentJoinCategories() {
        return incidentJoinCategories;
    }
    public void setIncidentJoinCategories(Set<IncidentJoinCategoryEntity> 
        incidentJoinCategories) {
        this.incidentJoinCategories = incidentJoinCategories;
    }
}

此类有一个完全匹配其值的DTO类:

Category.java:

public class Category {

    int id;

    Set<IncidentJoinCategory> incidentJoinCategories= 
    new HashSet<IncidentJoinCategory>();

    @Override
    public int getId() {
        return id;
    }
    @Override
    public void setId(int id) {
        this.id = id;
    }

    public Set<IncidentJoinCategory> getIncidentJoinCategories() {
        return incidentJoinCategories;
    }
    public void setIncidentJoinCategories(Set<IncidentJoinCategory> 
        incidentJoinCategories) {
        this.incidentJoinCategories = incidentJoinCategories;
    }
}

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!研究!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

编辑#1:

好!所以我花了几个小时调试这个问题,以了解这里发生了什么。事实证明问题出在MappingProcessor类749行(Dozer 5.4.0)或766行的最新来源(但我还没有检查这是否仍然是最新来源中的一个问题,但是怀疑它是否已修复)。

此行

((Set) field).addAll(result);

这里想要映射的是

HashSet<IncidentJoinCategoryEntity>

addAll(result)只向((Set)字段)集合添加1个项目。其中包含2个项目的结果(在调试期间它也是大小2我将提供变量的快照)只是向((Set)字段)强制转换添加1个值。

result  LinkedHashSet<E>  (id=220)  
    map LinkedHashMap<K,V>  (id=248)    
        accessOrder false   
        entrySet    HashMap$EntrySet  (id=251)  
        hashSeed    -1187793029 
        header  LinkedHashMap$Entry<K,V>  (id=253)  
        keySet  HashMap$KeySet  (id=5829)   
        loadFactor  0.75    
        modCount    2   
        size    2   
        table   HashMap$Entry<K,V>[16]  (id=258)    
        threshold   12  
        useAltHashing   false   
        values  null    
field   HashSet<E>  (id=221)    
    map HashMap<K,V>  (id=247)  
        entrySet    HashMap$EntrySet  (id=5856) 
        hashSeed    1372273954  
        keySet  HashMap$KeySet  (id=5821)   
        loadFactor  0.75    
        modCount    2   
        size    1   
        table   HashMap$Entry<K,V>[16]  (id=5822)   
        threshold   12  
        useAltHashing   false   
        values  null    

enter image description here

编辑#2:

下载源代码以进行更多调试:

if (field == null) {
  Class<? extends Set<?>> destSetType = (Class<? extends Set<?>>) 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;
}

此案例的输出:

----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----

它的输出是2项,但是你可以看到@ 3e2由于某种原因它们是同一个项目。因此,当您调用addAll时,它会删除重复内容,只留下1个项目。为什么Dozer意外地映射了2个相同的值?我检查以确保源对象集合没有相同的项目加倍,并且肯定不是。确实很奇怪。

编辑#3:

我做了进一步的测试,运气不大。这确实是一个问题,Dozer映射2具有相同的值,并且addAll将副本关闭,使其成为列表中的一个项目。遗憾的是,我无法轻松调试addToSet中的递归方法,以确定原因。

如果我想出其他任何事情,我会更新,否则我对这一点没有想法哈哈。

1 个答案:

答案 0 :(得分:1)

事实证明这实际上并不是一个Dozer bug。调试表明Dozer是罪魁祸首,但我认为不是这样。原因我认为这是因为我转换到另一个具有相同问题的映射器,所以除非这个新映射器具有相同的问题(lol),否则它不是Dozer。如果有人知道为什么会发生这种情况,我会很感激你的帮助。

我现在的猜测是我用来处理延迟加载敏感集合的hibernate自定义字段映射器。我首先忽视这一点的唯一原因是因为当我开始调试Dozer时,似乎Dozer在从addToSet返回之前映射了字段,所以我错误地认为它已经应用了自定义字段映射。