对于更新方法,将数据从表单对象传输到实体的更简洁方法是什么?

时间:2018-04-21 11:59:38

标签: java spring spring-mvc spring-boot

尝试重构以下控制器的代码:

 @PutMapping("products/{productId}")
    public ProductResponse updateProduct(@PathVariable("productId") Long productId, @RequestBody ProductForm productForm) {
        Optional<Product> foundProductOpt = productRepository.findById(productId);
        Product foundProduct = foundProductOpt.orElseThrow(() -> new EntityNotFoundException("productId" + productId + "not found."));

        //would like to refactor code below!!

        foundProduct.setProductTitle(productForm.getProductTitle());
        foundProduct.setProductPrice(productForm.getProductPrice());
        foundProduct.setProductDescription(productForm.getProductDescription());
        foundProduct.setProductImage(productForm.getProductImage());
        productRepository.save(foundProduct);

        return new ProductResponse(null, "product updated");
    }

我只是将表单对象中的值传递给实体对象。考虑创建方法但不想在实体中编写方法,因此不确定其他解决方案是什么。

根据要求,以下是我的产品和表单对象。我想使用表单对象进行验证,然后将数据传输到Product对象。

Product.java

package com.assignment.restapi.domain;

import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.Min;
import java.util.Date;

@Entity
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long productId;
    private String productImage;
    private String productTitle;
    private String productDescription;
    private Integer productPrice;
    //https://stackoverflow.com/questions/49954812/how-can-you-make-a-created-at-column-generate-the-creation-date-time-automatical/49954965#49954965
    @CreationTimestamp
    private Date createdAt;
    @UpdateTimestamp
    private Date updatedAt;

    // default constructor
    public Product() {

    }
    // parameterized constructor-User enetered value goes here to set the fields of the instantiated object.
    public Product(String productImage, String productTitle, String productDescription, Integer productPrice, Date createdAt) {
        this.productImage = productImage;
        this.productTitle = productTitle;
        this.productDescription = productDescription;
        this.productPrice = productPrice;
        this.createdAt = createdAt;
    }
    // getter methods are used to retrieve a value from an object.
    // setter methods are used to set a new value to an object.
    public Long getProductId() {
        return productId;
    }

    public void setProductId(Long productId) {
        this.productId = productId;
    }

    public String getProductImage() {
        return productImage;
    }

    public void setProductImage(String productImage) {
        this.productImage = productImage;
    }

    public String getProductTitle() {
        return productTitle;
    }

    public void setProductTitle(String productTitle) {
        this.productTitle = productTitle;
    }

    public String getProductDescription() {
        return productDescription;
    }

    public void setProductDescription(String productDescription) {
        this.productDescription = productDescription;
    }

    public Integer getProductPrice() {
        return productPrice;
    }

    public void setProductPrice(@Min(value = 1, message = "値段は0以上の値を設定してください。") Integer productPrice) {
        this.productPrice = productPrice;
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public Date getUpdatedAt() {
        return updatedAt;
    }

    public void setUpdatedAt(Date updatedAt) {
        this.updatedAt = updatedAt;
    }
}

ProductForm.java

package com.assignment.restapi.web.view;

import com.assignment.restapi.domain.Product;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

public class ProductForm {

    String productImage;

    @NotBlank(message = "Product title is necessary")
    @Size(max = 100, message = "Product title has to be less than 100 letters")
    String productTitle;

    @Size(max = 500, message = "Product title has to be less than 500 letters")
    String productDescription;

    @Min(value = 1, message = "price has to have a value larger than 1")
    Integer productPrice;

    public ProductForm() {

    }

    public ProductForm(String productImage, String productTitle, String productDescription, Integer productPrice) {
        this.productImage = productImage;
        this.productTitle = productTitle;
        this.productDescription = productDescription;
        this.productPrice = productPrice;
    }

    public String getProductImage() {
        return productImage;
    }

    public void setProductImage(String productImage) {
        this.productImage = productImage;
    }

    public String getProductTitle() {
        return productTitle;
    }

    public void setProductTitle(String productTitle) {
        this.productTitle = productTitle;
    }

    public String getProductDescription() {
        return productDescription;
    }

    public void setProductDescription(String productDescription) {
        this.productDescription = productDescription;
    }

    public Integer getProductPrice() {
        return productPrice;
    }

    public void setProductPrice(Integer productPrice) {
        this.productPrice = productPrice;
    }

    //turns productForm into Product object.
    public Product convertToProduct() {
        //step by step debug mode, new object constructor function in Product.java gets called.
        //setter methods get called and values of the ProductForm object gets passed and becomes the new value of the Product object.
        Product product = new Product();
        product.setProductTitle(this.productTitle);
        product.setProductImage(this.productImage);
        product.setProductDescription(this.productDescription);
        product.setProductPrice(this.productPrice);
        return product;
    }
}

3 个答案:

答案 0 :(得分:2)

使用Apache commons-beanutils ::

如果两个类中都有相同的字段名称,这将有效。

@PutMapping("products/{productId}")
public ProductResponse updateProduct(@PathVariable("productId") Long productId, @RequestBody ProductForm productForm) {
    Optional<Product> foundProductOpt = productRepository.findById(productId);
    Product foundProduct = foundProductOpt.orElseThrow(() -> new EntityNotFoundException("productId" + productId + "not found."));
    org.apache.commons.beanutils.BeanUtils.copyProperties(foundProduct, productForm); 
    productRepository.save(foundProduct);
    return new ProductResponse(null, "product updated");
}

答案 1 :(得分:0)

您可以使用copyProperties(Object dest, Object orig)

中的 BeanUtils
  

将属性值从原始bean复制到目标bean   所有属性名称相同的情况。

对于您的控制器代码,它看起来像这样(如果对象具有Date属性并且没有值,则需要注意):

BeanUtils.copyProperties(foundProduct,productForm);

您也可以直接在控制器方法中使用Product作为对象参数,但我认为它不符合您的代码设计

答案 2 :(得分:0)

而不是在productId中发送@PathVariable而不是在身体中发送它自己。使用Product中的@RequestBody代替ProductForm

还在@NotNull,@NotBlank等产品实体中添加验证,然后使用@Valid验证产品请求正文。

最后保存Product对象。由于产品具有id(产品ID),因此存储库的save()方法将对数据库执行更新命令。

所以找代码就是这样:

@PutMapping("/products")
public ProductResponse updateProduct(@Valid @RequestBody Product product) {
    Optional<Product> productOptional = productRepository.findById(product.getId());
    Product existingProduct = productOptional
            .orElseThrow(() -> new EntityNotFoundException("productId" + product.getId() + "not found."));

    productRepository.save(product);
    return new ProductResponse(null, "product updated");
}