проблема сопоставления «один ко многим» с ModelMapper - PullRequest
1 голос
/ 13 апреля 2020

Я просто хочу отобразить простой объект, включающий отношение один ко многим, однако он всегда возвращает эту ошибку.

1) Error mapping com.example.ProductDTO to com.example.Product
1 error] with root cause
java.lang.StackOverflowError: null

Я упростил свои классы и запускаю снова и снова, но не могу найти решение. Ниже приведены файлы для весенней загрузки (репозиторий, служба, контроллер, сущности и DTO).

Когда я впервые отправляю запрос POST для создания продукта с функциями (сын внизу сообщения), он создает сущности и сохраняться в БД. Когда я отправляю тот же json для обновления (PUT), он возвращает ошибку StackOverflow.

Есть ли способ сопоставить класс Product с ProductFeatures связанной сущности?

Product.class

package com.example;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Version;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
@Table(name = "product")
public class Product implements Serializable {

    @Id
    @Column(name = "id", columnDefinition = "BINARY(16)")
    private UUID id;

    @Version
    private Long version;

    @Column(length = 64, nullable = false)
    private String name;

    @Column(length = 128)
    private String description;

    @OneToMany(mappedBy = "product", fetch = FetchType.EAGER, orphanRemoval = true, cascade = CascadeType.PERSIST)
    @JsonManagedReference("product_features")
    private Set<ProductFeature> features = new HashSet<>();

    public Product() {
        this.id = UUID.randomUUID();
    }

    @Override
    public String toString() {
        return id.toString();
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Product)) {
            return false;
        }

        Product other = (Product) obj;

        return getId().equals(other.getId());
    }

    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }
}

ProductFeature.class

package com.example;

import com.fasterxml.jackson.annotation.JsonBackReference;
import java.io.Serializable;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Version;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
@Table(name = "productfeatures")
public class ProductFeature implements Serializable {

    @Id
    @Column(name = "id", columnDefinition = "BINARY(16)")
    private UUID id;

    @Version
    private Long version;

    @Column
    private int type;

    @Column
    private int intValue;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "product", nullable = false)
    @JsonBackReference("product_features")
    private Product product;

    public ProductFeature() {
        this.id = UUID.randomUUID();
    }

    @Override
    public String toString() {
        return id.toString();
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof ProductFeature)) {
            return false;
        }

        ProductFeature other = (ProductFeature) obj;

        return getId().equals(other.getId());
    }

    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }
}

ProductDTO.class

package com.example;

import com.fasterxml.jackson.annotation.JsonManagedReference;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ProductDTO implements Serializable {

    private UUID id;
    private String name;
    private String description;

    @JsonManagedReference("product_features")
    private Set<ProductFeatureDTO> features = new HashSet<>();

    public ProductDTO() {
        this.id = UUID.randomUUID();
    }

    @Override
    public String toString() {
        return id.toString();
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof ProductDTO)) {
            return false;
        }

        ProductDTO other = (ProductDTO) obj;

        return getId().equals(other.getId());
    }

    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }
}

ProductFeatureDTO.class

package com.example;

import com.fasterxml.jackson.annotation.JsonBackReference;
import java.io.Serializable;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class ProductFeatureDTO implements Serializable {

    private UUID id;
    private int type;
    private int intValue;

    @JsonBackReference("product_features")
    private ProductDTO product;

    public ProductFeatureDTO() {
        this.id = UUID.randomUUID();
    }

    @Override
    public String toString() {
        return id.toString();
    }

    @Override
    public int hashCode() {
        return id.hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof ProductFeatureDTO)) {
            return false;
        }

        ProductFeatureDTO other = (ProductFeatureDTO) obj;

        return getId().equals(other.getId());
    }

    public UUID getId() {
        return id;
    }

    public void setId(UUID id) {
        this.id = id;
    }
}

ProductRepository.class

package com.example;

import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends JpaRepository<Product, UUID> {

}

ProductRestController.class

package com.example;

import java.security.Principal;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;

@RestController
public class ProductRestController {

    @Autowired
    private ProductService productService;

    @GetMapping("/products")
    public List<ProductDTO> getProducts(Principal principal) {
        return productService.get();
    }

    @GetMapping("/products/{id}")
    public ProductDTO getProduct(@PathVariable UUID id) {
        return productService.get(id);
    }

    @PostMapping("/products")
    public ResponseEntity<?> create(@RequestBody ProductDTO input) {         
        return productService.create(input);
    }

    @PutMapping("/products/{id}")
    public ResponseEntity<?> update(@PathVariable UUID id, @RequestBody ProductDTO input) {
        return productService.update(id, input);
    }
}

ProductService.class

package com.example;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private ModelMapper modelMapper;

    public List<ProductDTO> get() {

        List<Product> products = productRepository.findAll();

        List<ProductDTO> productDTOs = new ArrayList<>();
        for (Product product : products) {
            ProductDTO productDTO = modelMapper.map(product, ProductDTO.class);
            productDTOs.add(productDTO);
        }

        return productDTOs;
    }

    public ProductDTO get(UUID id) {
        Product product = productRepository.findById(id).get();

        ProductDTO productDTO = modelMapper.map(product, ProductDTO.class);

        return productDTO;
    }

    public ResponseEntity<?> create(ProductDTO productDTO) {
        Product product = modelMapper.map(productDTO, Product.class);

        Product p = productRepository.save(product);

        return new ResponseEntity<>(modelMapper.map(p, ProductDTO.class), HttpStatus.OK);
    }

    public ResponseEntity<?> update(UUID id, ProductDTO productDTO) {
        Product foundproduct = productRepository.getOne(id);

        modelMapper.map(productDTO, foundproduct);

        Product p = productRepository.save(foundproduct);

        return new ResponseEntity<>(modelMapper.map(p, ProductDTO.class), HttpStatus.OK);
    }
}

JSON

{
  "id" : "6d1ed14a-d464-415a-9181-a3396ee9cd3e",
  "name" : "Test Product 11",
  "features" : [ {
    "id" : "5103e648-180a-4fb8-bc98-98efc5edf1c1",
    "type" : 13,
    "intValue" : 1
  }, {
    "id" : "e43fdb3e-ce80-4b7b-98db-00117cabf7e2",
    "type" : 11,
    "intValue" : 1
  }, {
    "id" : "4798849a-d849-4987-bfcc-da92adf9f813",
    "type" : 12,
    "intValue" : 1
  }, {
    "id" : "abc6883e-cbd9-4557-ad66-5b4ca6ef4084",
    "type" : 10,
    "intValue" : 1
  }, {
    "id" : "bb594179-80f2-4051-989e-280406a42775",
    "type" : 14,
    "intValue" : 1
  } ]
}
...