Преобразование Entity-объекта в объект с помощью фреймворка MapStruct - PullRequest
0 голосов
/ 28 мая 2020

У меня есть сущность, которая получает userType во время запроса к базе данных.

public class OrderEntityXml {

    @Id
    @SequenceGenerator(name = "jpa.Sequence.t.order", sequenceName = "T_ORDER_SEQ", allocationSize = 1)
    @GeneratedValue(generator = "jpa.Sequence.t.order", strategy = GenerationType.SEQUENCE)
    private Long id;

    private String customer;

    @Type(type = "Order")
    @Column(name = "order_xml")
    private Order order;

    public OrderEntityXml() {
    }

Есть контейнер для получения от сущности.

Этот контейнер использует Джексон.

Этот контейнер также использует маршаллер и демаршаллер

@XmlRootElement
@JacksonXmlRootElement(localName = "order")
public class Order implements Serializable {


    private String customer;

    @XmlElement(name = "orderItem")
    @JacksonXmlProperty(localName = "orderItem")
    @JacksonXmlElementWrapper(useWrapping = false)
    private List<OrderItem> orderItems = new ArrayList<>();

    public Order() {
    }

public class OrderItem {

    private String sku;

    private Double price;

    public OrderItem() {
    }

Я настроил MapStruct.

  • CycleAvoidingMappingContext - (Это необходимо, чтобы избежать Цикл (и появление переполнения стека))
@Component
public class CycleAvoidingMappingContext {

    private Map<Object, Object> knownInstances = new IdentityHashMap<Object, Object>();

    @BeforeMapping
    public <T> T getMappedInstance(Object source, @TargetType Class<T> targetType) {

        T t = (T) knownInstances.get(source);
        return t;
    }

    @BeforeMapping
    public void storeMappedInstance(Object source, @MappingTarget Object target) {
        knownInstances.put( source, target );
    }
}
public interface CommonMapper<D, E> {

    D toDto(E e, @Context CycleAvoidingMappingContext context);

    E toEntity(D d, @Context CycleAvoidingMappingContext context);

    Iterable<D> toListDto(Iterable<E> entityList);

    Iterable<E> toListEntity(Iterable<D> dtoList);

}

  • MapperUtils - (это утилиты для MapStruct)

public static Order convertToDto(OrderEntityXml orderEntityXml){
        Order order = new Order();

        String customer = orderEntityXml.getCustomer();
        order.setCustomer(customer);

        Order xmlOrder = orderEntityXml.getOrder();
        List<OrderItem> orderItems = xmlOrder.getOrderItems();
        order.setOrderItems(orderItems);

        return order;
    }

    public static OrderEntityXml convertToEntity(Order order){

        OrderEntityXml orderEntityXml = new OrderEntityXml();
        String customer = order.getCustomer();
        orderEntityXml.setCustomer(customer);

        List<OrderItem> orderItems = order.getOrderItems();

        Order orderInEntity = new Order();

        orderInEntity.setOrderItems(orderItems);
        orderInEntity.setCustomer(customer);

        orderEntityXml.setOrder(orderInEntity);

        return orderEntityXml;
    }
  • OrderDtoMapper - это базовый c интерфейс для меня, для генерации классов
@Mapper(componentModel = "spring")
public interface OrderDtoMapper extends CommonMapper<Order, OrderEntityXml> {

    @Override
    default Order toDto(OrderEntityXml orderEntityXml, CycleAvoidingMappingContext context) {

        return convertToDto(orderEntityXml);
    }

    @Override
    default OrderEntityXml toEntity(Order order, CycleAvoidingMappingContext context) {

        return convertToEntity(order);
    }


    @Override
    default Iterable<Order> toListDto(Iterable<OrderEntityXml> entityList) {

        Iterable<Order> collect = StreamSupport.stream(entityList.spliterator(), false)
            .map(MapperUtils::convertToDto)
            .collect(Collectors.toList());

        return collect;
    }
}

Для каждого типа данных вам придется создавать свои собственные утилиты, а это громоздко.

Могу ли я удалить лишний код и настроить его через интерфейсы MapStruct? Я подозреваю, что усложнил код.

Может ли кто-нибудь сказать вам, что нужно настроить или какую технологию можно применить к mapStruct, чтобы сделать код унифицированным

1 Ответ

1 голос
/ 01 июня 2020

Глядя на опубликованные объекты, я не понимаю, зачем вам использовать CycleAvoidingMappingContext. Между вашими объектами нет cycli c зависимости.

Чтобы избежать написанного вручную кода, вы можете использовать аннотацию MapStruct @Mapping для настройки того, как определенные поля должны быть сопоставлены.

Итак, в вашем случае это будет что-то вроде:

@Mapper(componentModel = "spring")
public interface OrderDtoMapper extends CommonMapper<Order, OrderEntityXml> {

    @Override
    @Mapping(target = "orderItems", source = "order.orderItems")
    Order toDto(OrderEntityXml orderEntityXml, @Context CycleAvoidingMappingContext context);

    @Override
    @Mapping(target = "order", source = "order")
    OrderEntityXml toEntity(Order order, CycleAvoidingMappingContext context);

    Order cloneOrder(Order order);
}

Единственные необходимые настройки:

  • Для toDto, чтобы сообщить MapStruct, что когда вы сопоставляете OrderEntityXml с Order вы хотите сопоставить order.orderItems с orderItems.
  • Для toEntity, чтобы сообщить MapStruct, что вы при сопоставлении от Order до OrderEntityXml вы хотите сопоставить параметры метода order с order объекта `OrderEntity Xml
  • Дополнительно мы добавляем Order cloneOrder(Order), чтобы MapStruct создавал новый объект при сопоставлении между Order в противном случае будет использоваться тот же объект.

customer в обоих случаях будет автоматически сопоставлен, так как он совпадает с обеих сторон.

Нет необходимости предоставлять собственный метод для отображение Iterable, потому что Map Struct сделает это автоматически. Он знает

...