DozerMapper / dozer

Dozer is a Java Bean to Java Bean mapper that recursively copies data from one object to another.
https://dozermapper.github.io/
Apache License 2.0
2.09k stars 481 forks source link

Infinite mapping with Hibernate Bug #190

Closed BenDol closed 7 years ago

BenDol commented 10 years ago

I have an object that I am mapping called IncidentEntity, which is an Entity object in Hibernate. This object has a number of other Entity relationships for example SectionEntity and ProcessEntity. I have spent a lot of time setting up Dozer so it will deal with the lazy loading on Set's and on Join relationships.

dozer.statistics.enabled=false
org.dozer.util.DozerProxyResolver=org.dozer.util.HibernateProxyResolver
dozer.autoregister.jmx.beans = false
public class LazyLoadSensitiveMapper implements CustomFieldMapper {
    public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) {
        // If field is initialized, Dozer will continue mapping
        boolean stopMapping = false;

        if(sourceFieldValue instanceof PersistentCollection) {
            stopMapping = !((PersistentCollection) sourceFieldValue).wasInitialized();
        }

        return stopMapping;
    }
}

These two work for the most part. But for some reason its not working on the IncidentEntity after I manually lazy load the ProcessEntity (or SectionEntity) then attempt to map the object. I get an infinite mapping of a ProcessEntity which has a Set of SectionEntity objects and a SectionEntity has a ProcessEntity so it has this infinite recursive map issue (even though in theory it shouldn't happen).

Tested on 5.5.1

IncidentEntity.java

/**
 * Database entity for the 'incidents' table records.<br>
 * Entity domain object is {@link nz.co.doltech.ims.shared.domains.Incident}
 * @author Ben Dol
 * 
 */
@javax.persistence.Entity(name = "incidents")
@Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL)
public class IncidentEntity implements Entity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(unique = true, nullable = false)
    private int id = Entity.UNSAVED_ID;

    ...

    @ManyToOne(fetch = FetchType.LAZY, targetEntity = ProcessEntity.class)
    @JoinColumn(name="process_state", referencedColumnName="id")
    private ProcessEntity processState;

    @ManyToOne(fetch = FetchType.LAZY, targetEntity = SectionEntity.class)
    @JoinColumn(name="section_state", referencedColumnName="id")
    private SectionEntity sectionState;

    @ManyToOne(fetch = FetchType.LAZY, targetEntity = ProcessEntity.class)
    @JoinColumn(name="process", referencedColumnName="id")
    private ProcessEntity process;

    @ManyToOne(fetch = FetchType.LAZY, targetEntity = SectionEntity.class)
    @JoinColumn(name="section", referencedColumnName="id")
    private SectionEntity section;

    ...

    public SectionEntity getSection() {
        return section;
    }
    public void setSection(SectionEntity section) {
        this.section = section;
    }

    public ProcessEntity getProcess() {
        return process;
    }
    public void setProcess(ProcessEntity process) {
        this.process = process;
    }

    public ProcessEntity getProcessState() {
        return processState;
    }
    public void setProcessState(ProcessEntity processState) {
        this.processState = processState;
    }

    public SectionEntity getSectionState() {
        return sectionState;
    }
    public void setSectionState(SectionEntity sectionState) {
        this.sectionState = sectionState;
    }
    ...
}

ProcessEntity.java

/**
 * Database entity for the 'processes' table records.<br>
 * Entity domain object is {@link nz.co.doltech.ims.shared.domains.Process}
 * @author Ben Dol
 * 
 */
@javax.persistence.Entity(name = "processes")
@Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL)
public class ProcessEntity implements Entity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id = Entity.UNSAVED_ID;

    ...

    @OneToMany(fetch=FetchType.LAZY, mappedBy="process")
    //@Cascade({CascadeType.SAVE_UPDATE})
    private Set<SectionEntity> sections = new HashSet<SectionEntity>();

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

    ...

    public Set<SectionEntity> getSections() {
        return sections;
    }
    public void setSections(Set<SectionEntity> sections) {
        this.sections = sections;
    }

    ...
}

SectionEntity.java

/**
 * Database entity for the 'sections' table records.<br>
 * Entity domain object is {@link nz.co.doltech.ims.shared.domains.Section}
 * @author Ben Dol
 * 
 */
@javax.persistence.Entity(name = "sections")
@Cache(usage=CacheConcurrencyStrategy.TRANSACTIONAL)
public class SectionEntity implements Entity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id = Entity.UNSAVED_ID;

    ...

    @ManyToOne(fetch = FetchType.LAZY, targetEntity = ProcessEntity.class)
    @JoinColumn(name="process", referencedColumnName="id")
    private ProcessEntity process;

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

    ...

    public ProcessEntity getProcess() {
        return process;
    }
    public void setProcess(ProcessEntity process) {
        this.process = process;
    }

    ...
}

Am I missing something? I have debugged the IncidentEntity object that I am mapping to make sure it hasn't recursively loaded before it is even sent off to be mapped and it hasn't.

Log output:

08:20:09,636 ERROR [org.dozer.MappingProcessor] - Field mapping error -->
  MapId: null
  Type: null
  Source parent class: nz.co.doltech.ims.server.entities.SectionEntity
  Source field name: process
  Source field type: class nz.co.doltech.ims.server.entities.ProcessEntity_$$_javassist_28
  Source field value: nz.co.doltech.ims.server.entities.ProcessEntity@792ee0e0
  Dest parent class: nz.co.doltech.ims.shared.domains.Section
  Dest field name: process
  Dest field type: nz.co.doltech.ims.shared.domains.Process
08:20:09,690 ERROR [org.dozer.MappingProcessor] - Field mapping error -->
  MapId: null
  Type: null
  Source parent class: nz.co.doltech.ims.server.entities.ProcessEntity
  Source field name: sections
  Source field type: class org.hibernate.collection.PersistentSet
  Source field value: [nz.co.doltech.ims.server.entities.SectionEntity@3209ee2d, nz.co.doltech.ims.server.entities.SectionEntity@29f3e605, nz.co.doltech.ims.server.entities.SectionEntity@de0cb4c, nz.co.doltech.ims.server.entities.SectionEntity@2142f1b2]
  Dest parent class: nz.co.doltech.ims.shared.domains.Process
  Dest field name: sections
  Dest field type: java.util.Set
08:20:09,734 ERROR [org.dozer.MappingProcessor] - Field mapping error -->
  MapId: null
  Type: null
  Source parent class: nz.co.doltech.ims.server.entities.SectionEntity
  Source field name: process
  Source field type: class nz.co.doltech.ims.server.entities.ProcessEntity_$$_javassist_28
  Source field value: nz.co.doltech.ims.server.entities.ProcessEntity@792ee0e0
  Dest parent class: nz.co.doltech.ims.shared.domains.Section
  Dest field name: process
  Dest field type: nz.co.doltech.ims.shared.domains.Process
08:20:09,785 ERROR [org.dozer.MappingProcessor] - Field mapping error -->
  MapId: null
  Type: null
  Source parent class: nz.co.doltech.ims.server.entities.ProcessEntity
  Source field name: sections
  Source field type: class org.hibernate.collection.PersistentSet
  Source field value: [nz.co.doltech.ims.server.entities.SectionEntity@3209ee2d, nz.co.doltech.ims.server.entities.SectionEntity@29f3e605, nz.co.doltech.ims.server.entities.SectionEntity@de0cb4c, nz.co.doltech.ims.server.entities.SectionEntity@2142f1b2]
  Dest parent class: nz.co.doltech.ims.shared.domains.Process
  Dest field name: sections
  Dest field type: java.util.Set
08:20:09,839 ERROR [org.dozer.MappingProcessor] - Field mapping error -->
  MapId: null
  Type: null
  Source parent class: nz.co.doltech.ims.server.entities.SectionEntity
  Source field name: process
  Source field type: class nz.co.doltech.ims.server.entities.ProcessEntity_$$_javassist_28
  Source field value: nz.co.doltech.ims.server.entities.ProcessEntity@792ee0e0
  Dest parent class: nz.co.doltech.ims.shared.domains.Section
  Dest field name: process
  Dest field type: nz.co.doltech.ims.shared.domains.Process
08:20:09,898 ERROR [org.dozer.MappingProcessor] - Field mapping error -->
  MapId: null
  Type: null
  Source parent class: nz.co.doltech.ims.server.entities.ProcessEntity
  Source field name: sections
  Source field type: class org.hibernate.collection.PersistentSet
  Source field value: [nz.co.doltech.ims.server.entities.SectionEntity@3209ee2d, nz.co.doltech.ims.server.entities.SectionEntity@29f3e605, nz.co.doltech.ims.server.entities.SectionEntity@de0cb4c, nz.co.doltech.ims.server.entities.SectionEntity@2142f1b2]
  Dest parent class: nz.co.doltech.ims.shared.domains.Process
  Dest field name: sections
  Dest field type: java.util.Set
at org.dozer.MappingProcessor.mapFromFieldMap(MappingProcessor.java:361)
    at org.dozer.MappingProcessor.mapField(MappingProcessor.java:307)
    at org.dozer.MappingProcessor.map(MappingProcessor.java:267)
    at org.dozer.MappingProcessor.mapToDestObject(MappingProcessor.java:216)
    at org.dozer.MappingProcessor.createByCreationDirectiveAndMap(MappingProcessor.java:196)
    at org.dozer.MappingProcessor.mapCustomObject(MappingProcessor.java:512)
    at org.dozer.MappingProcessor.mapOrRecurseObject(MappingProcessor.java:465)
    at org.dozer.MappingProcessor.mapFromFieldMap(MappingProcessor.java:361)
    at org.dozer.MappingProcessor.mapField(MappingProcessor.java:307)
    at org.dozer.MappingProcessor.map(MappingProcessor.java:267)
    at org.dozer.MappingProcessor.mapToDestObject(MappingProcessor.java:216)
    at org.dozer.MappingProcessor.createByCreationDirectiveAndMap(MappingProcessor.java:196)
    at org.dozer.MappingProcessor.mapCustomObject(MappingProcessor.java:512)
    at org.dozer.MappingProcessor.mapOrRecurseObject(MappingProcessor.java:465)
    at org.dozer.MappingProcessor.addToSet(MappingProcessor.java:757)
    at org.dozer.MappingProcessor.mapCollection(MappingProcessor.java:570)
    at org.dozer.MappingProcessor.mapOrRecurseObject(MappingProcessor.java:453)
    at org.dozer.MappingProcessor.mapFromFieldMap(MappingProcessor.java:361)
    at org.dozer.MappingProcessor.mapField(MappingProcessor.java:307)
    at org.dozer.MappingProcessor.map(MappingProcessor.java:267)
    at org.dozer.MappingProcessor.mapToDestObject(MappingProcessor.java:216)
    at org.dozer.MappingProcessor.createByCreationDirectiveAndMap(MappingProcessor.java:196)
    at org.dozer.MappingProcessor.mapCustomObject(MappingProcessor.java:512)
    at org.dozer.MappingProcessor.mapOrRecurseObject(MappingProcessor.java:465)
    at org.dozer.MappingProcessor.mapFromFieldMap(MappingProcessor.java:361)
    at org.dozer.MappingProcessor.mapField(MappingProcessor.java:307)
    at org.dozer.MappingProcessor.map(MappingProcessor.java:267)
       .....

I have no idea how to reproduce this issue but I'm farely sure it is a bug somewhere.

garethahealy commented 7 years ago

@BenDol ; is this still an issue?

garethahealy commented 7 years ago

Closing due to inactivity.

heloufir commented 6 years ago

Do you have a solution, I almost tried all the solution in the stackoverflow, ... and other forums, but none of them worked for me. I have the same issue

garethahealy commented 6 years ago

@eloufirhatim ; see the below. Someone else has hit a similar:

heloufir commented 6 years ago

@garethahealy thanks for your reply. I was using the Dozer

Before

<dependency>
    <groupId>net.sf.dozer</groupId>
    <artifactId>dozer</artifactId>
    <version>${dozer.version}</version>
</dependency>
<dependency>
    <groupId>net.sf.dozer</groupId>
    <artifactId>dozer-spring</artifactId>
    <version>${dozer.version}</version>
</dependency>

After, I switched for the DozerMapper/dozer plugin (this repository)

<dependency>
    <groupId>com.github.dozermapper</groupId>
    <artifactId>dozer-core</artifactId>
    <version>${dozer.version}</version>
</dependency>
<dependency>
    <groupId>com.github.dozermapper</groupId>
    <artifactId>dozer-spring4</artifactId>
    <version>${dozer.version}</version>
</dependency>

I thought I was using this from the begining 😄

But, can you explain to me the difference between these two libraries please? They are identical ? because, their configuration are pretty the same.

Thanks in advance.

garethahealy commented 6 years ago

Its the same project, but because of migrating to github and no longer controlling the sf site, the GAV changed.

heloufir commented 6 years ago

Ok, thank you for the information. Now all is working perfectly 😃

heloufir commented 6 years ago

Hello, The same issue is back again, when debugging I see that my object is not mapped like I configured in the XML mapping file, but what is weird, is when I restart the server... It works juste fine.

I cereated an issue for it: https://github.com/DozerMapper/dozer/issues/704

Can you please help. Thanks in advance