Quero padronizar meus processos de mapeamento para seguir um esquema específico. Por isso, introduzi algumas interfaces genéricas que ajudam a reduzir o esforço ao escrever novos mapeadores para novos tipos. Aqui está a interface para os tipos:
import lombok.NonNull;
public interface MappableCyclic<IN extends MappableCyclic<IN, OUT>, OUT extends MappableCyclic<OUT, IN>>
{
void beforeMapping(@NonNull IN in, @NonNull OUT out, @NonNull ReferenceCycleTracking context);
void afterMapping(@NonNull IN in, @NonNull OUT out, @NonNull ReferenceCycleTracking context);
}
ReferenceCycleTracking
é uma implementação típica para um contexto mapstruct para evitar recursão infinita ao mapear objetos com dependências cíclicas. Além disso, há uma superinterface genérica para mapeadores:
public interface MappableCyclicMapper<IN extends MappableCyclic<IN, OUT>, OUT extends MappableCyclic<OUT, IN>>
{
@NonNull OUT map(@NonNull IN in, @NonNull @Context ReferenceCycleTracking context);
@BeforeMapping default void beforeMapping(
@NonNull IN in,
@NonNull @MappingTarget OUT out,
@NonNull @Context ReferenceCycleTracking context)
{
out.beforeMapping(out, in, context);
}
@AfterMapping default void afterMapping(
@NonNull IN in,
@NonNull @MappingTarget OUT out,
@NonNull @Context ReferenceCycleTracking context)
{
out.afterMapping(out, in, context);
}
@NonNull Class<OUT> outType();
@NonNull OUT create(IN in);
/**
* object factory should be called by mapstruct during generated {@link #map(MappableCyclic, ReferenceCycleTracking)}
* implementation
*/
@ObjectFactory default @NonNull OUT lookupOrCreate(@NonNull IN in, @NonNull ReferenceCycleTracking context)
{
OUT out = context.get(in, outType());
if (out == null)
{
out = create(in);
context.put(in, out);
}
return out;
}
}
E finalmente aqui está a interface do mapeador para dois dos meus tipos mapeáveis:
@Mapper interface Map_TaskGroup_EntityDTO_EntityJPA extends MappableCyclicMapper<TaskGroupEntityDTO, TaskGroupEntityJPA>
{
Map_TaskGroup_EntityDTO_EntityJPA INSTANCE = Mappers.getMapper(Map_TaskGroup_EntityDTO_EntityJPA.class);
@NonNull TaskGroupEntityJPA map(@NonNull TaskGroupEntityDTO input, @NonNull @Context ReferenceCycleTracking context);
@Override default @NonNull Class<TaskGroupEntityJPA> outType() { return TaskGroupEntityJPA.class; }
@Override default @NonNull TaskGroupEntityJPA create(TaskGroupEntityDTO in) { return new TaskGroupEntityJPA(in.name()); }
@ObjectFactory
@Override default @NonNull TaskGroupEntityJPA lookupOrCreate(
@NonNull TaskGroupEntityDTO taskGroupEntityDTO, @NonNull ReferenceCycleTracking context)
{
return MappableCyclicMapper.super.lookupOrCreate(taskGroupEntityDTO, context);
}
}
Eu esperava que o mapstruct usasse o @ObjectFactory
método fornecido na superinterface, mas isso não acontece. Então, tentei ajudar o mapstruct com um método de fábrica de objetos adicional na subinterface acima. No entanto, a implementação para essa interface gerada pelo mapstruct ignora meus métodos de fábrica de objetos:
@Override
public TaskGroupEntityJPA map(TaskGroupEntityDTO input, ReferenceCycleTracking context) {
TaskGroupEntityJPA target = context.get( input, TaskGroupEntityJPA.class );
if ( target != null ) {
return target;
}
if ( input == null ) {
return null;
}
String name = null;
TaskGroupEntityJPA taskGroupEntityJPA = new TaskGroupEntityJPA( name );
context.put( input, taskGroupEntityJPA );
beforeMapping( input, taskGroupEntityJPA, context );
afterMapping( input, taskGroupEntityJPA, context );
return taskGroupEntityJPA;
}
Como posso ter certeza de que meus métodos de fábrica de objetos não serão mais ignorados?