Java JPA - Как поставить объект со списком и обновить список объектов, а также с CRUD? - PullRequest
0 голосов
/ 24 февраля 2019

У меня есть объект, который содержит список других объектов, которые я не могу корректно обновить.Я могу создать объект (продукт) со списком объектов (ProductItemQuantity) без проблем.Я также могу сделать PUT со списком объектов, но он создает новый список объектов все, что я делаю PUT.Я хочу, чтобы список объектов, которые я предоставляю, обновлялся, вместо того, чтобы он создавал новый список каждый раз, когда я делаю сдачу родительского объекта.

Если я добавляю ID в ProductItemQuantity, я получаю исключение:

detached entity passed to persist

Вот мои уроки:

Product.java

    @Entity 
public class Product {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    private Long id;
    @ManyToOne
    private Organization org;
    private String barCode;
    private String name;
    @ManyToOne(cascade=CascadeType.MERGE)
    private Status status;
    @ManyToMany(cascade=CascadeType.MERGE)
    private List<Fee> fees;
    @ManyToMany(cascade=CascadeType.ALL)
    private List<Note> notes;
    @ManyToMany(cascade=CascadeType.ALL)
    private List<ProductItemQuantity> productItems;
    private Integer stock;
    private BigDecimal msrp;
    @CreationTimestamp
    private LocalDateTime createdOn;
    @UpdateTimestamp
    private LocalDateTime updatedOn;

    // Getter & Setters

ProductItemQuantity.java

@Entity 
public class ProductItemQuantity {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    private Long id;
    private Integer count;
    @ManyToOne
    private ProductItem productItem;
    @CreationTimestamp
    private LocalDateTime createdOn;
    @UpdateTimestamp
    private LocalDateTime updatedOn;

// Getters / setters

ProductItem.java

@Entity 
public class ProductItem {

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY) 
    private Long id;
    @ManyToOne
    private Organization org;
    @ManyToOne
    private Supplier supplier;
    private String barCode;
    private String description;
    private String name;
    private Integer stock;
    private Integer caseQty;
    private BigDecimal caseCost;
    @ManyToMany(cascade=CascadeType.ALL)
    private List<Note> notes;
    @CreationTimestamp
    private LocalDateTime createdOn;
    @UpdateTimestamp
    private LocalDateTime updatedOn;

ProductController.java

@PutMapping("/{id}")
public Product update(@RequestBody Product product, @PathVariable long id) {


    Product savedProduct = productService.save(product);


    return savedProduct;
}

Рабочий запрос CRUD PUT:http://localhost:8080/product/1

{
    "barcode":"12347163",
    "name":"Product 1",
    "stock": 12,
    "msrp": 29.99,
    "org": {
        "id":1
    },
    "status":{
        "id":1
    },
    "productItems":[{
        "count":30
    },{
        "count":30
    }
        ],
        "fees":[{
            "id":1

        },{
            "id":2

        }],
    "notes":[{
        "title":"Product Created",
        "description":"Note created by user X on 12/16/2019 11:00PM"
    },{
        "title":"Product Updated",
        "description":"Product updated stock by user X on 12/16/2019 11:00PM"
    }]
}

Сломанный запрос CRUD PUT: http://localhost:8080/product/1

{
    "barcode":"12347163",
    "name":"Product 1",
    "stock": 12,
    "msrp": 29.99,
    "org": {
        "id":1
    },
    "status":{
        "id":1
    },
    "productItems":[{
        "id":1,
        "count":30
    },{
        "id":2,
        "count":30
    }
        ],
        "fees":[{
            "id":1

        },{
            "id":2

        }],
    "notes":[{
        "title":"Product Created",
        "description":"Note created by user X on 12/16/2019 11:00PM"
    },{
        "title":"Product Updated",
        "description":"Product updated stock by user X on 12/16/2019 11:00PM"
    }]
}

Ответы [ 2 ]

0 голосов
/ 28 февраля 2019

Ответ состоял в том, чтобы обновить свойство до этого:

@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"}) 
private List<ProductItemQuantity> productItemQuantities;

, а затем установить его вручную в пут

    List<ProductItemQuantity> piqs = product.getProductItemQuantities();

    if (piqs != null) {
        List<ProductItemQuantity> piiList = new ArrayList<ProductItemQuantity>();
        for (ProductItemQuantity pii : piqs) {
            // Check for ID in here?
            ProductItemQuantity curPII = piqService.getOne(pii.getId());
            curPII.setCount(pii.getCount());
            piiList.add(curPII);
        }
        originalProduct.setProductItemQuantities(piiList);
    }
0 голосов
/ 24 февраля 2019

Ваши объекты отделены, потому что отношения (OneToMany, ManyToMany) установлены только с одного направления.Чтобы сохранить их, вы должны установить двусторонние отношения.Ваше отношение является однонаправленным, потому что парсер (Джексон) сделает следующие объекты:

Product product = new Product();
Fee fee = new Fee();
fee.setId(1);
product.setFees(Arrays.asList(fee));

В двунаправленном отношении должны быть установлены обе стороны:

product.getFees().forEach(fee-> fee.getProducts().add(product));

Это хорошопрактикуйтесь в том, чтобы отделить объект постоянства от объекта контроллера, потому что объект постоянства также имеет дело с отношениями.

По моему опыту, если вы хотите использовать GeneratedValue, вам сначала нужно выбрать объект из базы данных ипосле того, как изменить его.Не имеет смысла создавать новый объект и устанавливать для него идентификатор, если вы ожидаете, что Hibernate сгенерирует его.

Итак, вам может понадобиться сначала сделать выбор:

Список сборов= // выберите все сборы с идентификатором в списке сборов продукта

и после:

product.setFees(fees);
fees.forEach(fee -> fee.getProducts().add(product));

Ваш метод представляет собой PUT, поэтому вам не следует напрямую сохранять объект продукта (он создастновая запись в базе данных).

@PutMapping("/{id}")
public Product update(@RequestBody Product product, @PathVariable long id) {
    Optional<Product> originalProductOptional = productRepository.findById(id);
    // you should add a check here : if originalProduct is not found, return 404
    Product originalProduct = originalProductOptional.get();
    originalProduct.setName(product.getName());
    // here update all fields and relations

    productRepository.save(originalProduct);
    return originalProduct;
 }
...