JavaRush /Java 博客 /Random-ZH /Spring — это не страшно, прослойка из DTO
Павел
第 11 级

Spring — это не страшно, прослойка из DTO

已在 Random-ZH 群组中发布
СОДЕРЖАНИЕ ЦИКЛА СТАТЕЙ Продолжаем говорить про Spring. Сегодня будем разбирать паттерн DTO, для понимания можно почитать тут. Самое сложное в DTO - это понять, зачем оно нужно. Давайте займемся спекуляцией овощей, и заодно, попишем code, может по ходу дела что то и проясниться. Создайте spring-boot проект , подключите h2 и Lombok. Создайте пакеты: entities, repositories, services, utils. В entities создайте сущность 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 и класс ItiiateUtil (аналогично прошлой статье). Допустим мы прикупли картофель по оптовой цене 20 рублей за кг., и морковки по 14 рублей за кг. Приобретенные продукты положим в хранorще. Дополним БД записями: [Id =1, name= “Картофель”, purchasePrice = 20] [Id =2, name= “Морковь”, purchasePrice = 14] Как порядочные спекулянты, мы должны выгодно впарить свой товар, для этого давайте красиво упакуем его и накрутим цену. То есть, были у нас грязные и не красивые овощи, наваленные кучей, а станут чистенькие премиум-веган продукты сегмента лакшери. Согласитесь, это будет уже не тот продукт(an object) который мы купor оптом. Для нового продукта создадим пакет 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 может содержать точно такие же переменные, How и entity, or их может быть больше, or меньше. Мы помним, что конвертация одного an object в другой – это дело маппинга. В пакете 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;
    }
}
Просто заполняем поля из одного an object, аналогичными полями из другого an object. В классе ProductService реализуем методы для поиска одного продукта or списка продуктов, но перед эти мы конвертируем entity в 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
    );
}
What будет если мы сейчас положим эти овощи на витрину? А давайте посмотрим. Для этого в ItiiateUtil напишем следующий code и запустим.
System.out.println("\nВитрина магазина");
for (ProductDto dto : productService.findAll()) {
    System.out.println(dto);
}
На выходе получим: Витрина магазина ProductDto(id=1, name=Картофель, purchasePrice=20, packaging=null, salePrice=null) ProductDto(id=2, name=Морковь, purchasePrice=14, packaging=null, salePrice=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 и выкладывание на витрину заменяем на следующий code
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以最好的方式,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