我目前正在建立一个网站,为注册用户提供产品管理。它使用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。
答案 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
添加一个隐藏键,否则它将产品详细信息对象视为临时对象并将其保存为新对象,而不是对其进行更新。