Dozer картирование / обновление коллекции

Я пытаюсь отобразить объект A-DTO на объект A-DO, каждый из которых имеет коллекцию (список) T-DTO и T-DO, соответственно. Я пытаюсь сделать это в контексте REST API. Это отдельный вопрос, правильный ли это подход - проблема, которую я решаю, - это вопрос обновления. По сути, если один из T-DTO внутри A-DTO изменяется, я хочу, чтобы это изменение было отображено в соответствующем T-DO внутри A-DO.

я нашел relationship-type="non-cumulative" в документации Dozer, так что объект внутри коллекции обновляется, если он присутствует. Но в итоге Дозер вставляет новый T-DO в коллекцию A-DO!

ПРИМЕЧАНИЕ: я реализовал equals! пока он основан на первичном ключе.

Есть идеи?

PS: и, если вы считаете, что обрабатывать обновления зависимой сущности "один ко многим" - это плохая идея, не стесняйтесь указывать на это… Я не уверен на 100%, что мне нравится такой подход, но мой REST foo не очень сильный.

ОБНОВИТЬ

equals реализация:

@Override
public boolean equals(Object obj) {
    if (obj instanceof MyDOClass) {
        MyDOClass other = (MyDOClass) obj;
        return other.getId().equals(this.getId());
    }
    return false;
}

2 ответа

Решение

У меня была такая же проблема, и я решил ее:

Бульдозер использует contains определить, находится ли член в коллекции.
Вы должны реализовать hashCode чтобы "contains" будет работать соответствующим образом.

Вы можете увидеть это на следующей странице документации: http://dozer.sourceforge.net/documentation/collectionandarraymapping.html В разделе: "Совокупное сопоставление и сопоставление некумулятивного списка (двунаправленное)"

Удачи!

Закончилось делать пользовательское отображение.

В конце концов я сделал свой собственный AbstractConverter, пожалуйста, найдите его ниже: У него есть некоторые ограничения, которые подходят для меня (возможно, не для вас).

  • будет обновляться на основе реализации "SameId"
  • удалит сирот (элемент из пункта назначения не в источнике).
  • Работает только в списке (достаточно для моих нужд).
  • Хотя конвертер примет решение обновить отображение объектов, которые делегируются обратно в Dozer, вам не нужно реализовывать отображение элементов в вашем списке.

Образец использования

public class MyConverter extends AbstractListConverter<ClassX,ClassY>{
   public MyConverter(){ super(ClassX.class, ClassY.class);}
   @Override
   protected boolean sameId(ClassX o1, ClassY o2) {
        return // your custom comparison here... true means the o2 and o1 can update each other.
   }
}

Объявление в mapper.xml

<mapping>
        <class-a>x.y.z.AClass</class-a>
        <class-b>a.b.c.AnotherClass</class-b>
        <field custom-converter="g.e.MyConverter">
            <a>ListField</a>
            <b>OtherListField</b>
        </field>
    </mapping>
public abstract class AbstractListConverter<A, B> implements MapperAware, CustomConverter {

    private Mapper mapper;
    private Class<A> prototypeA;
    private Class<B> prototypeB;

    @Override
    public void setMapper(Mapper mapper) {
        this.mapper = mapper;
    }

    AbstractListConverter(Class<A> prototypeA, Class<B> prototypeB) {
        this.prototypeA = prototypeA;
        this.prototypeB = prototypeB;
    }

    @Override
    public Object convert(Object destination, Object source, Class<?> destinationClass, Class<?> sourceClass) {
        if (destinationClass == null || sourceClass == null || source == null) {
            return null;
        }
        if (List.class.isAssignableFrom(sourceClass) && List.class.isAssignableFrom(destinationClass)) {
            if (destination == null || ((List) destination).size() == 0) {
                return produceNewList((List) source, destinationClass);
            }
            return mergeList((List) source, (List) destination, destinationClass);
        }
        throw new Error("This specific mapper is only to be used when both source and destination are of type java.util.List");
    }

    private boolean same(Object o1, Object o2) {
        if (prototypeA.isAssignableFrom(o1.getClass()) && prototypeB.isAssignableFrom(o2.getClass())) {
            return sameId((A) o1, (B) o2);
        }
        if (prototypeB.isAssignableFrom(o1.getClass()) && prototypeA.isAssignableFrom(o2.getClass())) {
            return sameId((A) o2, (B) o1);
        }
        return false;
    }

    abstract protected boolean sameId(A o, B t);

    private List mergeList(List source, List destination, Class<?> destinationClass) {
        return (List)
                source.stream().map(from -> {
                            Optional to = destination.stream().filter(search -> same(from, search)).findFirst();
                            if (to.isPresent()) {
                                Object ret = to.get();
                                mapper.map(from, ret);
                                return ret;
                            } else {
                                return create(from);
                            }
                        }
                ).collect(Collectors.toList());

    }

    private List produceNewList(List source, Class<?> destinationClass) {
        if (source.size() == 0) return source;
        return (List) source.stream().map(o -> create(o)).collect(Collectors.toList());
    }

    private Object create(Object o) {
        if (prototypeA.isAssignableFrom(o.getClass())) {
            return mapper.map(o, prototypeB);
        }
        if (prototypeB.isAssignableFrom(o.getClass())) {
            return mapper.map(o, prototypeA);
        }
        return null;
    }
}
Другие вопросы по тегам