JavaRush /בלוג Java /Random-HE /אביב זה לא מפחיד, שכבת DTO
Павел
רָמָה

אביב זה לא מפחיד, שכבת DTO

פורסם בקבוצה
תוכן מחזור המאמר אנחנו ממשיכים לדבר על אביב. היום ננתח את דפוס ה-DTO, להבנה תוכלו לקרוא כאן . הדבר הקשה ביותר ב-DTO הוא להבין מדוע הוא נחוץ. נתחיל להעלות השערות על ירקות, ובמקביל נכתוב איזה קוד, אולי משהו יתבהר בדרך. צור פרויקט קפיצי אתחול , חבר את h2 ו- Lombok . צור חבילות: ישויות, מאגרים, שירותים, שימושים. בישויות, צור ישות מוצר:
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 רובל לק"ג. את המוצרים הנרכשים נעמיד באחסון. בואו נוסיף את הרשומות הבאות למסד הנתונים: [מזהה =1, שם= "תפוחי אדמה", מחיר קנייה = 20] [מזהה =2, שם= "גזר", קנייהמחיר = 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);
}
הפלט הוא: Store display ProductDto(id=1, name=Potatoes, purchasePrice=20, packaging=null, salePrice=null) ProductDto(id=2, name=Carrots, 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 ומחליפים את התצוגה בתצוגה בקוד הבא
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, שם=תפוחי אדמה, רכישהמחיר=20, אריזה=ארוזה בצורה הטובה ביותר, מחיר מבצע=100) ProductDto(id=2, שם=גזר, רכישהמחיר=14, אריזה=אריזה בצורה הטובה ביותר, salePrice=70) המוצר ארוז יפה, יש מחיר, אבל האם ראית איפשהו על החלון להציג את המחיר עבורו קניתם אותו בתפזורת ואיזה מזהה אחר. אנו משנים את הקוד שנכתב למעלה עם קובץ:
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()
    ));
}
class 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