JavaRush /Java Blog /Random-TW /Spring並不可怕,一個DTO層
Павел
等級 11

Spring並不可怕,一個DTO層

在 Random-TW 群組發布
文章內容 我們繼續談論Spring。今天我們來分析DTO模式,想要了解的可以閱讀這裡。關於 DTO 最困難的事情是理解為什麼需要它。讓我們開始猜測蔬菜,同時,寫一些程式碼,也許一路上事情會變得更清晰。建立一個spring-boot項目連結h2Lombok。建立套件:實體、儲存庫、服務、實用程式。在實體中,建立一個 Product 實體:
package ru.java.rush.entities;

import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.annotations.GenericGenerator;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Accessors(chain = true)
@Entity
@Data
public class ProductEntity {

    @Id
    @Column
    @GenericGenerator(name = "generator", strategy = "increment")
    @GeneratedValue(generator = "generator")
    Integer id;

    @Column
    String name;

    @Column
    Integer purchasePrice;//закупочная цена

}
實作ProducRepository、ProducService 類別和IiiateUtil 類別(與上一篇文章類似)。假設我們以每公斤 20 盧布的批發價購買土豆,以每公斤 14 盧布的價格購買胡蘿蔔。我們會將購買的產品入庫。讓我們將以下記錄加入資料庫: [Id =1, name= “Potatoes”, buyPrice = 20] [Id =2, name= “Carrots”, buyPrice = 14] 作為正派的投機者,我們必須有利可圖圖地出售我們的商品,為此,我們將其包裝精美,然後我們將提高價格。也就是說,我們把髒兮兮、毫無吸引力的蔬菜堆成一堆,但將會有來自奢侈品領域的乾淨的優質純素產品。同意,這將不再是我們批量購買的同一產品(物件)。對於新產品,我們建立一個 dto 包,並在其中建立ProductDto 類
package ru.java.rush.dto;

import lombok.Data;

@Data
public class ProductDto {
    Integer id;
    String name;
    Integer purchasePrice;
    String  packaging;//упаковка
    Integer salePrice;//цена реализации
}
ProductDto 有兩個 ProductEntity 沒有的變數:「包裝」和「售價」。dto 物件可能包含與實體完全相同的變量,也可能有更多或更少的變數。我們記得將一個物件轉換為另一個物件是一個映射問題。在 utils 套件中,我們將建立 MappingUtils 類
package ru.java.rush.utils;

import org.springframework.stereotype.Service;
import ru.java.rush.dto.ProductDto;
import ru.java.rush.entities.ProductEntity;

@Service
public class MappingUtils {
//из entity в dto
    public ProductDto mapToProductDto(ProductEntity entity){
        ProductDto dto = new ProductDto();
        dto.setId(entity.getId());
        dto.setName(entity.getName());
        dto.setPurchasePrice(entity.getPurchasePrice());
        return dto;
    }
//из dto в entity
    public ProductEntity mapToProductEntity(ProductDto dto){
        ProductEntity entity = new ProductEntity();
        entity.setId(dto.getId());
        entity.setName(dto.getName());
        entity.setPurchasePrice(dto.getPurchasePrice());
        return entity;
    }
}
我們只需用另一個物件的類似欄位填充一個物件的欄位即可。在 ProductService 類別中,我們實作了搜尋單一產品或產品清單的方法,但在此之前我們使用上面編寫的方法將實體轉換為 dto。
private final ProductRepository productRepository;
private final MappingUtils mappingUtils;


//для листа продуктов мы использовали стрим
public List<ProductDto> findAll() {
    return productRepository.findAll().stream() //создали из листа стирим
            .map(mappingUtils::mapToProductDto) //оператором из streamAPI map, использовали для каждого element метод mapToProductDto из класса MappingUtils
.collect(Collectors.toList()); //превратor стрим обратно в коллекцию, а точнее в лист
}

//для одиночного продукта обошлись проще
public ProductDto findById(Integer id) {
    return mappingUtils.mapToProductDto( //в метод mapToProductDto
            productRepository.findById(id) //поместor результат поиска по id
                    .orElse(new ProductEntity()) //если ни чего не нашли, то вернем пустой entity
    );
}
如果我們現在把這些蔬菜放在展示架上會發生什麼事?讓我們來看看。為此,請在 ItiiateUtil 中編寫以下程式碼並運行它。
System.out.println("\nВитрина магазина");
for (ProductDto dto : productService.findAll()) {
    System.out.println(dto);
}
輸出為: 商店顯示 ProductDto(id=1, name=Potatoes,purchasePrice=20,packaging=null, salePrice=null) ProductDto(id=2, name=Carrots,purchasePrice=14,packaging=null, ullsalePrice=null) 好吧,我不會!沒有人會買這樣的蔬菜:它們很髒,沒有包裝,而且售價不詳。現在是業務邏輯的時候了。我們在 ProductService 類別中實作它。讓我們先向此類添加幾個變數:
private final Integer margin = 5;//это наша накрутка на цену
private final String packaging = "Упаковано в лучшем виде";//так будет выглядеть упаковка
對於每個操作:包裝和價格標記,我們將在同一類別中建立一個單獨的方法:
// упаковываем товар
public void pack(List<ProductDto> list) {
    list.forEach(productDto ->
            productDto.setPackaging(packaging)
    );
}

// делаем деньги
public void makeMoney(List<ProductDto> list) {
    list.forEach(productDto ->
            productDto.setSalePrice(productDto.getPurchasePrice() * margin)
    );
}
我們回到ItiiateUtil,將顯示器上的顯示替換為以下程式碼
List<ProductDto> productDtos = productService.findAll();

productService.pack(productDtos);
productService.makeMoney(productDtos);

System.out.println("\nВитрина магазина");
for (ProductDto dto : productDtos)) {
    System.out.println(dto);
}
我們執行: 商店展示 ProductDto(id=1, name=Potatoes,purchasePrice=20,packaging=以最佳方式包裝, salePrice=100) ProductDto(id=2, name=Carrots,purchasePrice=14,packaging=Packed以最pack好的方式,salePrice = 70) 該產品包裝精美,有價格,但是您是否在窗口上的某個地方看到您批量購買它的價格和其他一些ID。我們用一個檔案修改上面寫的程式碼:
List<ProductDto> productDtos = productService.findAll();

productService.pack(productDtos);
productService.makeMoney(productDtos);

System.out.println("\nВитрина магазина");
for (ProductDto dto : productDtos) {
    System.out.println(String.format(
            "Купите: %s , по цене:  %d", dto.getName(), dto.getSalePrice()
    ));
}
InitiateUtils 類別最終應該如下所示:
@Service
@RequiredArgsConstructor
public class InitiateUtils implements CommandLineRunner {

    private final ProductService productService;

    @Override
    public void run(String... args) throws Exception {

        List<ProductEntity> products = new ArrayList<>(
                Arrays.asList(
                        new ProductEntity()
                                .setName("Картофель")
                                .setPurchasePrice(20),
                        new ProductEntity()
                                .setName("Морковь")
                                .setPurchasePrice(14)
                ));

        productService.saveAll(products);

        List<ProductDto> productDtos = productService.findAll();

        productService.pack(productDtos);
        productService.makeMoney(productDtos);

        System.out.println("\nВитрина магазина");
        for (ProductDto dto : productDtos) {
            System.out.println(String.format(
                    "Купите: %s , по цене:  %d", dto.getName(), dto.getSalePrice()
            ));
        }
    }
}
讓我們啟動: 商店櫥窗 購買:土豆,價格:100 購買:胡蘿蔔,價格:70 另一件事!現在我們認為dto 帶來了一些好處,除了一堆額外的程式碼: 1. 我們可以在不更改資料庫中的物件的情況下執行業務邏輯(假設我們不需要在資料庫中包含有關包裝和銷售價格的字段)這張表)。即使沒有價格標籤的包裝,馬鈴薯也能在儲存中保存得很好,甚至是多餘的。2. 在List<ProductDto> ProductDtos = ProductService.findAll()這行中,我們建立了一個物件緩存,以便在業務邏輯中使用。這是如果我們把一些貨物放在商店的後面的房間裡。3. 這使我們能夠執行兩個業務操作:打包和標記,但我們只向資料庫發出一次請求(從效能角度來看,對資料庫的查詢相當困難)。你可以把產品包裝起來,貼上價格標籤,然後把它展示出來——逐漸從雜物間拿起它,而不是每次都跑到儲藏室去。對於「為什麼這麼難?」這個問題,人們也試著尋找答案,閱讀下一篇
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION