स्प्रिंग बूट में मैपस्ट्रैक का उपयोग करके माता-पिता और बच्चों का नक्शा कैसे बनाएं?

Dec 01 2020

मेरे पास माता-पिता (उत्पाद) और बच्चे (पुस्तक, फर्नीचर) हैं, और उत्पाद इकाई को उत्पाद डीटीओ के लिए मैप करना चाहते हैं। जैसा कि आप देख सकते हैं, उत्पाद मैप किया गया है और डेटाबेस में एकल तालिका में संग्रहीत किया गया है। मैं माता-पिता, उत्पाद को कैसे मैप कर सकता हूं, जिसमें उसके बच्चे का अतिरिक्त विवरण है?

मैंने इसे , इस और इसे कुछ विचार प्राप्त करने के लिए देखा है लेकिन कोई भाग्य नहीं

सत्ता

@Entity
@Table(name = "product")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Product {
  @Id
  private long id;
  private String productName;
}

@Entity
@DiscriminatorValue("Book")
public class Book extends Product { 
  private String author;
  ...
}
@Entity
@DiscriminatorValue("Furniture")
public class Furniture extends Product {
  String color;
  ...
}

डीटीओ

public class ProductDto {
  private long id;
  private String productName;
  ...
}

public class BookDto extends ProductDto {
  private String author;
  ...
}
public class FurnitureDto extends ProductDto {
   String color;
   ... 
}

नक्शाकार

@Mapper(uses = {BookMapper.class,FurnitureMapper.class})
public interface ProductMapper {
    
    ProductDto productToProductDto(Product product);
    Product productDtoToProduct(ProductDto productDto);
}

@Mapper
public interface BookMapper {
    BookDto bookToBookDto(Book book);
    Book bookDtoToBook(BookDto bookDto);
}

@Mapper
public interface FurnitureMapper {
    FurnitureDto furnitureToFurnitureDto(Furniture furniture);
    Furniture furnitureDtoToFurniture(FurnitureDto furnitureDto);
}

सर्विस

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    ProductRepository productRepository;
    @Autowired
    ProductMapper productMapper;

    @Override
    public List<ProductDto> getAllProducts() {
        List<ProductDto> listOfProducts = new ArrayList<>();
        productRepository.findAll().forEach(i -> 
        listOfProducts.add(productMapper.productToProductDto(i)));
        return listOfProducts;
    }
}

संपादित

उत्पाद इकाई को उत्पाद dto में मैप करने के बाद मुझे निम्नलिखित परिणाम प्राप्त होते हैं। यह डेटा को बांधता नहीं है और इसमें अपने बच्चों के गुण शामिल नहीं हैं। क्या उपरोक्त मैपर सेक्शन सही है?

[
    {
        "id": 0,
        "productName": null
    },
    {
        "id": 0,
        "productName": null
    },
    ...
]

हालांकि परिणाम बलो की तरह होना चाहिए:

[
    {
        "id": 11,
        "productName": ABC,
        "author":"James"
    },
    {
        "id": 22,
        "productName": XYZ,
        "color":"Oak"
    },
    ...
]

जवाब

2 TasosP. Dec 10 2020 at 05:01

टीएल, डॉ

ऐसा करने का कोई साफ तरीका नहीं है । इसका कारण जावा का संकलन-समय-विधि चयन है। लेकिन विज़िटर पैटर्न का उपयोग करने का कुछ साफ तरीका है।

यह काम क्यों नहीं कर रहा है

जब आप संस्थाओं की सूची को टाइप करते हैं, जिसमें विभिन्न प्रकार (उत्पाद, पुस्तक, फर्नीचर) होते हैं, तो आपको प्रत्येक प्रकार के लिए एक अलग मैपिंग विधि (यानी एक अलग मैपस्ट्रक्चर मैपर) को कॉल करने की आवश्यकता होती है।

जब तक आप instanceofरास्ते से नहीं जाते, जैसा कि आमिर सुझाव देते हैं, और स्पष्ट रूप से मैपर का चयन करते हैं, आपको प्रति यूनिट वर्ग के लिए अलग-अलग मैपिंग विधियों को लागू करने के लिए विधि अधिभार का उपयोग करने की आवश्यकता होती है। समस्या यह है कि जावा संकलन-समय पर अतिभारित विधि का चयन करेगा और उस बिंदु पर, संकलक केवल Productवस्तुओं की एक सूची (आपके भंडार विधि द्वारा लौटाए गए) को देखता है । यह वास्तव में कोई फर्क नहीं पड़ता अगर MapStruct या स्प्रिंग या अपने स्वयं के कस्टम कोड ऐसा करने की कोशिश करता है। यह भी यही कारण है कि आपका ProductMapperहमेशा आह्वान किया जाता है: यह एकमात्र प्रकार है जो संकलन-समय पर दिखाई देता है।

विज़िटर पैटर्न का उपयोग करना

चूंकि हमें मैन्युअल रूप से सही मैपर चुनने की आवश्यकता है , इसलिए हमें यह चुनना है कि कौन सा तरीका क्लीनर या अधिक रखरखाव योग्य है। यह निश्चित रूप से राय है।

मेरा सुझाव निम्नलिखित तरीके से विज़िटर पैटर्न (वास्तव में इसका एक रूपांतर) का उपयोग करना है:

अपनी संस्थाओं के लिए एक नया इंटरफ़ेस पेश करें, जिसे मैप करने की आवश्यकता है:

public interface MappableEntity {

    public ProductDto map(EntityMapper mapper);
}

आपकी संस्थाओं को इस इंटरफ़ेस को लागू करने की आवश्यकता होगी, उदाहरण के लिए:

public class Book extends Product implements MappableEntity {
//...
@Override
    public ProductDto map(EntityMapper mapper) {
        return mapper.map(this);//This is the magic part. We choose which method to call because the parameter is this, which is a Book!
    }
}

EntityMapper विज़िटर इंटरफ़ेस है:

public interface EntityMapper {

    ProductDto map(Product entity);

    BookDto map(Book entity);

    FurnitureDto map(Furniture entity);

    // Add your next entity here
}

अंत में, आपको MasterMapper की आवश्यकता है:

// Not a class name I'm proud of
public class MasterMapper implements EntityMapper {

    // Inject your mappers here

    @Override
    public ProductDto map(Product product) {
        ProductMapper productMapper = Mappers.getMapper(ProductMapper.class);
        return productMapper.map(product);
    }

    @Override
    public BookDto map(Book product) {
        BookMapper productMapper = Mappers.getMapper(BookMapper.class);
        return productMapper.map(product);
    }

    @Override
    public FurnitureDto map(Furniture product) {
        FurnitureMapper productMapper = Mappers.getMapper(FurnitureMapper.class);
        return productMapper.map(product);
    }

    // Add your next mapper here

}

आपकी सेवा पद्धति तब दिखाई देगी:

MasterMapper mm = new MasterMapper();
List<Product> listOfEntities = productRepository.findAll();
List<ProductDto> listOfProducts = new ArrayList<>(listOfEntities.size());
listOfEntities.forEach(i -> {
        if (i instanceof MappableEntity) {
            MappableEntity me = i;
            ProductDto dto = me.map(mm);
            listOfProducts.add(dto);
        } else {
            // Throw an AssertionError during development (who would run server VMs with -ea ?!?!)
            assert false : "Can't properly map " + i.getClass() + " as it's not implementing MappableEntity";
            // Use default mapper as a fallback
            final ProductDto defaultDto = Mappers.getMapper(ProductMapper.class).map(i);
            listOfProducts.add(defaultDto);
        }
     });
    return listOfProducts;

आप सुरक्षित रूप से अनदेखा कर सकते हैं Mappers.getMapper()कॉल: के बाद से इस मुद्दे को वसंत से संबंधित नहीं है, मैं पर एक काम उदाहरण बनाया GitHub सादगी के लिए MapStruct के कारखानों का उपयोग कर। आप सीडीआई का उपयोग करके सिर्फ अपने मैपर्स को इंजेक्ट करेंगे।

1 AmirSchnell Dec 09 2020 at 16:44

यह ठीक वैसा ही परिदृश्य है जैसा कि इस अंक में वर्णित है ।
दुर्भाग्य से यह मुद्दा अभी भी खुला है और किसी ने भी अब तक समाधान प्रदान नहीं किया है। इसलिए मुझे लगता है कि आप जो चाहते हैं वह संभव नहीं है।

आपकी समस्या को हल करने का एकमात्र तरीका आपकी सेवा के भीतर कोड को बदलना है।

ओपनी बात आप कर सकते हैं, जैसा कि इस मुद्दे में बताया गया है, हर Productवस्तु का परीक्षण करना है instanceof:

@Autowired
BookMapper bookMapper;
@Autowired
FurnitureMapper furnitureMapper;

public List<ProductDto> getAllProducts() {
    List<ProductDto> listOfProducts = new ArrayList<>();
    List<Product> all = productRepository.findAll();
    for(Product product : all) {
        if(product instanceof Book) {
            listOfProducts.add(bookMapper.bookToBookDto((Book)product));
        } else if(product instanceof Furniture) {
            listOfProducts.add(furnitureMapper.furnitureToFurnitureDto((Furniture)product));
        }
    }
    return listOfProducts;
}

आप अपना ProductMapperविस्तार भी कर सकते हैं , BookMapperऔर FurnitureMapperइसलिए आपको उन्हें इंजेक्ट नहीं करना पड़ेगा।