Java >> Java tutorial >  >> Java

Konvertering af enhed til DTO ved hjælp af ModelMapper

I dette indlæg vil jeg vise, hvordan vi kan opnå konverteringen af ​​entitet til DTO ved hjælp af ModelMapper-biblioteket . Vi vil grundlæggende oprette en simpel REST API til orders mens den viser transformationen af ​​Entitet til DTO og omvendt.

Forståelse af Enterprise Architecture

I de fleste virksomhedsarkitekturer vil du have REST API'er. En forbruger af disse API'er sender en anmodning, og serveren svarer med et svar. Transformationen af ​​anmodning til svar sker bag API'en. Du udfører forretningslogik og ændrer disse objekter.

Traditionelt er der tre lag i arkitekturen. Weblag, forretningslag og databaselag.

Så dit objekt i databaselaget vil være helt anderledes end det samme objekt i weblaget. Databaseenheder fra databaselaget indeholder visse felter, som du ikke har brug for i weblaget. Mere så bør ethvert objekt fra weblaget være brugervenligt. Brugerne behøver ikke at gætte, hvad de har med at gøre. Det skal være selvforklarende. Dette vil være mere tydeligt, når jeg viser implementeringen af ​​dette.

Adskillelse af lag mellem enhed og DTO

Data Transfer Objects (DTO) er de objekter, der flyttes fra et lag til et andet. Disse objekter er mere brugervenlige og indeholder kun de mest påkrævede felter.

På den anden side repræsenterer databaseenheder databasetabeller. En masse automatisk genererede felter kan være unødvendige for brugere at kende til. Ikke desto mindre er de en del af databaseenheder. I DTO ignorerer vi disse felter. Da disse felter er automatisk genereret, kan vores databaselagskode klare det.

Men når objektet bevæger sig fra weblaget til databaselaget, skal det transformeres for at dette lag kan bruges. I det næste afsnit vil jeg vise, hvordan vi kan opnå denne konvertering fra enhed til DTO ved hjælp af ModelMapper-biblioteket.

Enheden til DTO ved hjælp af ModelMapper

ModelMapper-bibliotek giver en nemmere måde at konvertere et enhedsobjekt til DTO og omvendt.

I denne demo har jeg et scenarie, hvor en kunde bestiller en vare. Der oprettes en ordre på varen. Vi gemmer ordreoplysninger, kundeoplysninger og kundens adresse.

For at kunne bruge dette bibliotek i vores applikation skal du tilføje afhængigheden som følger:

implementation 'org.modelmapper:modelmapper:2.3.0'

Også hvis vi vil bruge ModelMapper biblioteksfunktioner, tilføjer vi en bean for det samme som følger:

        @Bean
	public ModelMapper modelMapper()
	{
		return new ModelMapper();
	}

Tidligere har jeg oplyst, at en kunde vil kunne bestille. Så vi implementerer dette ved at have en REST API, der vil skabe ordredetaljer, kundedetaljer.

Domænelag

I denne arkitektur har vi ordrer, som kunder bestiller på bestemte adresser.

I et databaseentitetsdiagram vil det se ud som nedenfor:

En kunde kan bestille flere varer, altså flere ordrer. Flere ordrer kan gå til en enkelt adresse.

Vores domæneobjekter vil se ud som nedenfor, begyndende med Order:


package com.betterjavacode.modelmapperdemo.models;

import javax.persistence.*;
import java.io.Serializable;

@Entity(name = "Order")
@Table(name = "orders")
public class Order implements Serializable
{
    private static final long serialVersionUID = 7385741327704693623L;

    public Order()
    {

    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private long id;

    @Column(name ="order_item")
    private String orderItem;

    @Column(name = "description")
    private String description;


    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;


    @ManyToOne
    @JoinColumn(name = "address_id")
    private Address address;
    
    // Getters and setters omitted for demo purposes


}

Adressen vil være:


package com.betterjavacode.modelmapperdemo.models;

import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@Entity(name = "Address")
@Table(name = "address")
public class Address implements Serializable
{
    private static final long serialVersionUID = -439961851267007148L;

    public Address()
    {

    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private long id;

    @Column
    private String street;

    @Column
    private String city;

    @Column
    private String state;

    @Column
    private String country;

    @Column
    private int zipcode;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List orderList = new ArrayList<>();


}

Og kunden vil være:


package com.betterjavacode.modelmapperdemo.models;

import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

@Entity(name = "Customer")
@Table(name = "customer")
public class Customer implements Serializable
{
    private static final long serialVersionUID = -2205735699915701334L;

    public Customer()
    {

    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private long id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column
    private String email;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List orderList = new ArrayList<>();


}

Disse tre objekter ordre, kunde og adresse repræsenterer vores databaseenheder og en del af databaselaget. Resten af ​​persistensen er ligetil med repositories.

Weblag

Weblag fokuserer for det meste på de controllere, som vi opretter til vores API'er. Disse controllere er ansvarlige for at modtage anmodningen fra klienten. Også de objekter, som vi vil eksponere gennem API'er, vil være DTO-objekter. Dette DTO-objekt for Order vil se ud som nedenfor:


package com.betterjavacode.modelmapperdemo.dtos;

public class OrderDTO
{
    String orderItem;
    String orderDescription;
    String customerFirstName;
    String customerLastName;
    String customerEmail;
    String streetAddress;
    String cityAddress;
    String stateAddress;
    String countryAddress;
    int zipcodeAddress;

   // Getters and Setters omitted for demo

}

Dette DTO-objekt inkluderer felter fra Ordre, Kunde og Adresse. Vores API vil modtage dette objekt i POST-anmodning, vi vil transformere det DTO-objekt til et enhedsobjekt ved hjælp af ModelMapper-biblioteket og derefter videregive det entitetsobjekt til vores Service klasse til at behandle videre.

OrderController vil være som følger:


package com.betterjavacode.modelmapperdemo.controllers;

import com.betterjavacode.modelmapperdemo.dtos.OrderDTO;
import com.betterjavacode.modelmapperdemo.models.Order;
import com.betterjavacode.modelmapperdemo.service.IOrderService;
import org.modelmapper.ModelMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/v1/betterjavacode/orders")
public class OrderController
{
    private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);

    @Autowired
    private IOrderService orderService;

    @Autowired
    private ModelMapper modelMapper;

    @PostMapping
    public OrderDTO createOrder(@RequestBody OrderDTO orderDTO)
    {
        Order order = convertToEntity(orderDTO);
        Order orderCreated = orderService.createOrder(order);

        return convertToDTO(orderCreated);
    }

    @GetMapping("/{customerId}")
    public List getAllOrders(@PathVariable("customerId") long customerId)
    {
        List orderList = orderService.getAllOrdersForCustomer(customerId);
        List orderDTOs = new ArrayList<>();
        for(Order order : orderList)
        {
            orderDTOs.add(convertToDTO(order));
        }
        return orderDTOs;
    }


    private Order convertToEntity (OrderDTO orderDTO)
    {
        LOGGER.info("DTO Object = {} ", orderDTO);

        Order order = modelMapper.map(orderDTO, Order.class);

        return order;
    }

    private OrderDTO convertToDTO (Order order)
    {
        OrderDTO orderDTO = modelMapper.map(order, OrderDTO.class);
        return orderDTO;
    }
}

Vi har en POST API til at oprette ordrer og en GET API til at hente ordrer til en kunde.

ModelMapper Library

I vores controller bruger vi ModelMapper bean for at konvertere DTO-objekt til entitet og entitetsobjekt til DTO.

Hvordan opnår ModelMapper-biblioteket egentlig dette?

Når en kortlægger kalder kortmetoden, analyserer den kilde- og destinationstyperne for at bestemme, hvilke egenskaber der skal matches. Den bruger en matchende strategi og konfiguration til at kortlægge disse egenskaber. Når egenskaberne er kortlagt, vil det kortlægge dataene.

Så hvis vi ser på vores DTO-klasse, har vi egenskaber som customerFirstName , customerLastName der matcher Customer Enhedsobjekt, mens egenskaber som streetAddress , cityAddress vil matche egenskaber fra Address objekt.

ModelMapper tilbyder også en måde til eksplicit at kortlægge egenskaberne, hvis du vælger at gøre det.


modelMapper.typeMap(Order.class, OrderDTO.class).addMappings(mapper -> {
  mapper.map(src -> src.getBillingAddress().getStreet(),
      Destination::setBillingStreet);
  mapper.map(src -> src.getBillingAddress().getCity(),
      Destination::setBillingCity);
});

Biblioteket tilbyder tre typer matchningsstrategier:

  1. Standard – I denne strategi matcher biblioteket kildeegenskaberne med destinationsegenskaber intelligent. Denne strategi er konfigureret som standard. Alle destinationsejendomsnavne-tokens skal matche.
  2. Løs – Egenskaber for kilde og destination matches løst. Hvis egenskabshierarkierne for kilde- og destinationsobjekter er forskellige, kan den løse strategi fungere. Navnet på den sidste destinationsegenskab skal have alle tokens matchede.
  3. Streng – Kildeegenskaber bør nøje matche destinationsegenskaber. Poletter matcher i en streng rækkefølge. Denne strategi tillader ingen tvetydighed.

En komplet demo af enhed til DTO ved hjælp af ModelMapper

Vi har vist vores REST-controller og domæneobjekter. Nu vil jeg vise, hvordan vi kan bruge postmand til at kalde denne REST API ved at sende et DTO-objekt til POST API.

Vi vil oprette en ordre på en vare, som en kunde bestiller.

I forespørgslen sendte jeg et DTO-objekt, der indeholder oplysninger om ordre, kunde og adresse.

I vores servicelag behandler vi konverterede enhedsobjekter, validerer forretningsregler og gemmer disse oplysninger for at oprette ordren.

Undgå teknisk gæld

Det er vigtigt at forstå konceptet med DTO- og Entity-objekter. Hvornår skal du bruge, hvilken slags objekt kan hjælpe dig med at undgå teknisk gæld. Fra personlig erfaring har jeg set mange juniorudviklere begå den fejl at bruge entitetsobjekter i et weblag. Afhængigt af din applikation kan dette øge systemets kompleksitet.

Konklusion

I dette indlæg viste jeg, hvordan vi kan konvertere entitet til DTO ved hjælp af modelmapper-biblioteket. Du kan downloade modelmapper-biblioteket her. Koden til denne demo er tilgængelig i mit GitLab-lager. Hvis du kunne lide dette indlæg, kan du overveje at abonnere på min blog her.

Referencer

  1. Model Mapper Library – ModelMapper

Java tag