Hibernate saveOrUpdate方法创建新的条目/行,而不是更新现有的条目/行

时间:2019-05-04 17:19:55

标签: java spring hibernate spring-mvc one-to-one

我目前正在建立一个网站,为注册用户提供产品管理。它使用spring + hibernate + mysql + jsp。休眠的saveOrUpdate方法总是为ProductDetail-entity创建一个新的条目/行到数据库,而不是更新已经存在的条目/行。我已经按照那里的教程进行了映射,但是我不明白是什么原因导致创建新行的,因为在使用之前,我已经在Controller层中建立了与Product-和ProductDetail-entity(OneToOne)的关系。有人将我从这场斗争中解救了出来,我已经在这里呆了3个多月了...在下面,我将提供有关实体,控制者和DAO的图片。

产品实体(nvm注释注释)

@Entity
@Table(name = "product")
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;

    @NotNull(message = "Product name is required")
    @Size(min = 1, message = "is required")
    @Column(name = "product_name")
    private String productName;

    @DecimalMin(value = "0.01", inclusive = true, message = "Price must be a minimum of 0.01$")
    @Digits(integer = 6, fraction = 2, message = "Price out of bounds, limit <6 digits>.<2 digits>")
    @Column(name = "price")
    private float price;

    @NotNull(message = "Quantity is required")
    @Min(value = 1, message = "Must be greater than zero")
    @Column(name = "qty")
    private Integer quantity;

    @NotNull(message = "Email is required")
    @Email(message = "Provide a valid email address")
    @Pattern(regexp = ".+@.+\\..+", message = "Provide a valid email address")
    @Column(name = "added_by")
    private String addedBy;

    @Column(name = "creation_datetime")
    private Date createDateTime;


    //@Version
    @Column(name = "last_updated")
    private Date updateDateTime;


    //@Valid
    @OneToOne(mappedBy = "product", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    private ProductDetail productDetail;

    @Column(name = "value_in_stock")
    private float valueInStock;

    public Product() {
        this.createDateTime = new Date();
        this.updateDateTime = this.createDateTime;
    }

    public Product(String productName, float price, Integer quantity, String addedBy) {
        this.productName = productName;
        this.price = price;
        this.quantity = quantity;
        this.addedBy = addedBy;
        this.valueInStock = this.price * this.quantity;
    }

        public void setProductDetail(ProductDetail productDetail) {
        if (productDetail == null) {
            if (this.productDetail != null) {
                this.productDetail.setProduct(null);
            }
        } else {
            productDetail.setProduct(this);
        }
        this.productDetail = productDetail;
    }
// getters and setters

ProductDetail实体

@Entity
@Table(name = "product_detail")
public class ProductDetail {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;

    @NotNull(message = "A descriptionis required")
    @Column(name = "description")
    private String description;

    @NotNull(message = "Category is required")
    @Column(name = "category")
    private String category;

    @DecimalMin(value = "0.001", inclusive = true, message = "Must a minimum of 0.001g")
    @Digits(integer = 7, fraction = 2, message = "Weight out of bounds, limit <7 digits>.<2 digits>")
    @Column(name = "weight_g")
    private float weight;

    @NotNull(message = "Manufacturer is required")
    @Column(name = "manufacturer")
    private String manufacturer;

    @NotNull(message = "Provide a country")
    @Column(name = "made_in_country")
    private String countryMadeIn;

    @OneToOne(fetch=FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinColumn(name = "product_id")
    private Product product;

    public ProductDetail() {

    }

    public ProductDetail(String description, String category, String manufacturer, String madeIn) {
        this.description = description;
        this.category = category;
        this.manufacturer = manufacturer;
        this.countryMadeIn = madeIn;
    }
// Getters and setters...

ProductController

获取表单以添加新产品的方法(GET请求)

@GetMapping("/add")
public String getAddForm(Model model) {

        // create model attribute to bind all form data
        Product product = new Product();
        ProductDetail productDetail = new ProductDetail();

        // associating product and product details
        product.setProductDetail(productDetail);
        product.getProductDetail());

        model.addAttribute("categoryMap", categoryOptions);
        model.addAttribute("countryMap", countryOptions);
        model.addAttribute("product", product);

        return "product-form";
    }

通过id从productService获取产品的方法(将数据获取委托给productDAO)(获取请求)

@GetMapping("/updateProduct")
public String getUpdateForm(@RequestParam("productId") int productId, Model model) {

        // get product from db
        Product product = productService.getProduct(productId);
        product.getProductDetail());

        // set product as a model to pre-populate the form
        model.addAttribute("categoryMap", categoryOptions);
        model.addAttribute("countryMap", countryOptions);
        model.addAttribute("product", product);

        return "product-form";
    }

用于保存/更新产品及其ProductDetail(POST请求)的方法

@PostMapping("/save")
public String saveOrUpdate(@Valid @ModelAttribute("product") Product product, BindingResult bindingResult,
            Model model) {

        // if result set has errors, return to product form with errors
        if (bindingResult.hasErrors()) {
            model.addAttribute("categoryMap", categoryOptions);
            model.addAttribute("countryMap", countryOptions);
            return "product-form";
        } else {

            // calculate value in stock to product before saving
            product.setValueInStock();

            productService.saveProduct(product);

            return "redirect:/";
        }

    }

ProductDAOImpl

保存或更新给定产品的方法

@Override
    public void saveProduct(Product product) {

        // get current hibernate session
        Session session = sessionFactory.getCurrentSession();

        // save or update given product
        session.saveOrUpdate(product);
    }

通过产品ID获取产品的方法

@Override
    public Product getProduct(int id) {

        // get current hibernate session
        Session session = sessionFactory.getCurrentSession();

        Query<Product> query = 
                session.createQuery("SELECT p FROM Product p "
                        + "JOIN FETCH p.productDetail "
                        + "WHERE p.id=:productId",
                        Product.class);

        // set parameters in query
        query.setParameter("productId", id);

        // execute and get product
        Product product = query.getSingleResult();

        return product;
    }

最后是JSP表单本身

<form:form action="save" modelAttribute="product" method="POST">

                <!-- associate data with product id -->
                <form:hidden path="id" />

                <div class="form-group row">
                    <label for="nameInput" class="col-sm-2 col-form-label">Product name *:</label>
                    <div class="col-sm-10">
                        <form:input path="productName" cssClass="form-control" id="nameInput" placeholder="Enter name" />
                        <form:errors path="productName" cssClass="errors" />
                    </div>
                </div>

                <div class="form-group row">
                    <label for="priceInput" class="col-sm-2 col-form-label">Price($) *:</label>
                    <div class="col-sm-10">
                        <form:input path="price" cssClass="form-control" id="priceInput" placeholder="Enter price" />
                        <form:errors path="price" cssClass="errors" />
                    </div>
                </div>

                <div class="form-group row">
                    <label for="quantityInput" class="col-sm-2 col-form-label">Quantity *:</label>
                    <div class="col-sm-10">
                        <form:input path="quantity" cssClass="form-control" id="quantityInput" placeholder="Enter qty" />
                        <form:errors path="quantity" cssClass="errors" />
                    </div>
                </div>


                <div class="form-group row">
                    <label for="emailInput" class="col-sm-2 col-form-label">Added by(email) *:</label>
                    <div class="col-sm-10">
                        <form:input path="addedBy" cssClass="form-control" id="emailInput" placeholder="example.address@email.com" />
                        <form:errors path="addedBy" cssClass="errors"  />
                    </div>
                </div>

                <div id="separator" > </div>


                <h5 id="header" >Product Details (Can be updated later)</h5>

                <div class="form-group row">
                    <label for="categoryInput" class="col-sm-2 col-form-label">Category *:</label>
                    <div class="col-sm-3">
                        <form:select path="productDetail.category" id="categoryInput" cssClass="form-control">
                            <form:option value="" label="Select Product Category" />
                            <form:options items="${categoryMap}"/>
                        </form:select>
                        <form:errors path="productDetail.category" cssClass="errors"  />
                    </div>
                </div>

                <div class="form-group row">
                    <label for="manufacturerInput" class="col-sm-2 col-form-label">Manufacturer *:</label>
                    <div class="col-sm-4">
                        <form:input path="productDetail.manufacturer" cssClass="form-control" id="manufacturerInput" placeholder="Enter manufacturer" />
                        <form:errors path="productDetail.manufacturer" cssClass="errors" />
                    </div>

                    <label for="madeInInput" class="col-sm-2 col-form-label">Country *:</label>
                    <div class="col-sm-3">
                        <form:select path="productDetail.countryMadeIn" id="madeInInput" cssClass="form-control">
                            <form:option value="" label="Country manufactured in" />
                            <form:options items="${countryMap}"/>
                        </form:select>
                        <form:errors path="productDetail.countryMadeIn" cssClass="errors"  />
                    </div>
                </div>


                <div class="form-group row">
                    <label for="descriptionInput" class="col-sm-2 col-form-label">Description *:</label>
                    <div class="col-sm-10">
                        <form:textarea path="productDetail.description" cssClass="form-control" id="descriptionInput" placeholder="Short description of product..." />
                        <form:errors path="productDetail.description" cssClass="errors"  />
                    </div>
                </div>


                <div class="form-group row">
                    <label for="weightInput" class="col-sm-2 col-form-label">Weight(g):</label>
                    <div class="col-sm-10">
                        <form:input path="productDetail.weight" cssClass="form-control" id="weightInput" placeholder="Enter weight" />
                    </div>
                </div>

                <input type="submit" value="Add" class="btn btn-primary" />

</form:form>

因此,用户应该能够添加和更新产品。目前,这只是添加,当用户想要更新产品时,它只是创建一个新的ProductDetail-entity而不是在获取的Product-entity上更新ProductDetail。

2 个答案:

答案 0 :(得分:0)

这是因为您使用的是原始数据类型int。将其更新为Integer,它应该可以正常工作。

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private int id;

更新为

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

注意:为该字段重新生成相应的getter / setter。

答案 1 :(得分:0)

您需要为productDetail.id添加一个隐藏键,否则它将产品详细信息对象视为临时对象并将其保存为新对象,而不是对其进行更新。