2016-03-25 10 views
2

Heute begann ich MapStruct zu verwenden, um meine Modell zu DTO-Konverter für mein Projekt zu erstellen, und ich fragte mich, ob es zyklische Verweise automatisch behandelt, aber es stellte sich heraus, dass es nicht.Verhindern zyklische Referenzen beim Konvertieren mit MapStruct

Dies ist der Konverter i zu testen gemacht:

package it.cdc.snp.services.rest.giudizio; 

import org.mapstruct.Mapper; 
import org.mapstruct.Mapping; 
import org.mapstruct.Mappings; 
import org.mapstruct.factory.Mappers; 
import org.springframework.stereotype.Component; 

import it.cdc.snp.dto.entita.Avvisinotifica; 
import it.cdc.snp.dto.entita.Corrispondenza; 
import it.cdc.snp.model.notifica.AvvisoDiNotificaModel; 
import it.cdc.snp.model.notifica.NotificaModel; 
import it.cdc.snp.model.procedimento.ProcedimentoModel; 

@Component 
@Mapper(componentModel="spring") 
public interface NotificaMapper { 

    NotificaMapper INSTANCE = Mappers.getMapper(NotificaMapper.class); 

    @Mappings({ 
     @Mapping(source = "avvisinotificas", target = "avvisinotificas"), 
    }) 
    NotificaModel<ProcedimentoModel> corrispondenzaToNotificaModel(Corrispondenza notifica); 

    @Mappings({ 
     @Mapping(source = "corrispondenza", target = "notifica"), 
    }) 
    AvvisoDiNotificaModel avvisinotificaToAvvisoDiNotificaModel(Avvisinotifica avvisinotifica); 


} 

Dies ist der Test:

 Notifica sourceObject1 = new Notifica(); 
     sourceObject1.setId(new Long(1)); 
     Avvisinotifica sourceObject2 = new Avvisinotifica(); 
     sourceObject2.setId(new Long(11)); 
     List<Avvisinotifica> tests= new ArrayList<>(); 
     tests.add(sourceObject2); 
     sourceObject1.setAvvisinotificas(tests); 
     sourceObject2.setCorrispondenza(sourceObject1); 

     NotificaModel destObject1 = new NotificaModel<>(); 
     Avvisinotifica destObject2 = new Avvisinotifica(); 

     NotificaModel converted = mapper.corrispondenzaToNotificaModel(sourceObject1); 

notifica, Avvisinotifica und ihre jeweiligen Modelle sind einfache POJOs mit Getter und Setter so i don Ich denke, es ist erforderlich, um den Code zu veröffentlichen (Notifica erweitert Corrispondenza, wenn Sie sich wunderten)

Dieser Code wird in einen unendlichen Zyklus, nichts sehr überraschend hier (obwohl ich hoffte, dass es mit diesen Situationen fertig wird). Und während ich denke, dass ich einen eleganten Weg finden kann, es manuell zu handhaben (ich dachte über die Verwendung von Methoden mit @MappingTarget, um die referenzierten Objekte einzufügen), was ich frage mich ist, wenn es eine Möglichkeit gibt MapStruct zu sagen, wie zyklische Referenzen automatisch zu behandeln.

Antwort

3

Es gibt keine Erkennung oder spezielle Behandlung von Fällen wie dieser in MapStruct noch, aber es gibt eine Feature-Anforderung für es: #469. Wenn Sie irgendwelche Ideen haben, wie man mit Zyklen umgeht, lassen Sie bitte einen Kommentar zu diesem Thema fallen.

+0

Ich wünschte, ich habe! Man könnte entweder a-la-Hibernate machen: benutze eine Wrapper-Klasse und wenn der Getter aufgerufen wird, konvertiere ihn sofort. Oder, wie vom Ersteller des Problems angegeben, verwenden Sie eine Map, um bereits konvertierte Elemente zu speichern, und wenn Sie ein Objekt finden, das bereits während des Konvertierungsprozesses konvertiert wurde, verwenden Sie dieses im Setter, anstatt ein neues zu konvertieren. Aber ich bin wirklich nicht Experte genug, um solche Vorschläge mit Leichtigkeit zu machen – valepu

3

Notifica und Avvisinotifica helfen mir nicht, Ihre Modelle zu verstehen. So können sagen, dass Sie die oben Kind und Vater Modelle,

public class Child { 
    private int id; 
    private Father father; 
    // Empty constructor and getter/setter methods ommitted. 
} 

public class Father { 
    private int x; 
    private List<Child> children; 
    // Empty constructor and getter/setter methods ommitted. 
} 

public class ChildDto { 
    private int id; 
    private Father father; 
    // Empty constructor and getter/setter methods ommitted. 
} 

public class FatherDto { 
    private int id; 
    private List<Child> children; 
    // Empty constructor and getter/setter methods ommitted. 
} 

Sie einen Mapper wie dies schaffen sollte,

@Mapper 
public abstract class ChildMapper { 

    @AfterMapping 
    protected void ignoreFathersChildren(Child child, @MappingTarget ChildDto childDto) { 
     childDto.getFather().setChildren(null); 
    } 

    public abstract ChildDto myMethod(Child child); 
} 

Die @AfterMapping Annotation bedeutet, dass das Verfahren innerhalb der erzeugten Quelle importiert werden, nach die Abbildung der Eigenschaften. Daher wird die Mapper-Implementierung wie folgt aussehen:

@Component 
public class ChildMapperImpl extends ChildMapper { 

    @Override 
    public ChildDto myMethod(Child child) { 
     if (child == null) { 
      return null; 
     } 

     ChildDto childDto = new ChildDto(); 

     childDto.setId(child.getId()); 
     childDto.setFather(child.getFather()); 

     ignoreFathersChildren(child, childDto); 

     return childDto; 
    } 
} 

In dieser Implementierung hat das Kind den übergeordneten Satz. Dies bedeutet, dass eine Zyklusreferenz existiert, aber unter Verwendung der ignoreFathersChildren(child, childDto) Methode entfernen wir die Referenz (wir setzen sie als Null).

===

aktualisieren

Mit der mapstruct Version 1.2.0.Final Sie es besser machen können,

@Mapper 
public interface ChildMapper { 

    @Mappings({ 
//   @Mapping(target = "father", expression = "java(null)"), 
     @Mapping(target = "father", qualifiedByName = "fatherToFatherDto")}) 
    ChildDto childToChildDto(Child child); 

    @Named("fatherToFatherDto") 
    @Mappings({ 
     @Mapping(target = "children", expression = "java(null)")}) 
    FatherDto fatherToFatherDto(Father father); 
} 
+1

Eine Alternative möglich ab MapStruct 1.2.0.Beta1 (gestern veröffentlicht) wäre, einen Kontextparameter zu verwenden, der bereits gemappte Objekte verfolgt. Ein vollständiges Beispiel, wie dies gemacht wird, finden Sie [hier] (https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-mapping-with-cycles/src/main/java/org/mapstruct /Beispiel). – Gunnar