问题:我的Web MVC应用程序正在插入成分表。我不想这样做,因为我已经在该表中填充了1000种不同的洗发精成分。
我确实尝试弄乱“ cascade = CascadeType.ALL”设置,但通常会收到“对象引用了一个未保存的瞬态实例-在刷新之前保存该瞬态实例:com.app.entity.Ingredient”。但是我真的不确定那是否可以解决任何问题。
Image of Console
DB DIAGRAM
在提交结果用户之后并且将其插入数据库之前,输出toString():
Shampoo [id=0, name=sas, company=Company [id=0, name=asassa], ingredients=[Ingredient [id=0, name=1-Dodecene, shampoos=null], Ingredient [id=0, name=Acetylated, shampoos=null]]]
冬眠的东西:
Hibernate: insert into company (company_name) values (?)
Hibernate: insert into shampoo (shampoo_company, shampoo_name) values (?, ?)
Hibernate: insert into ingredient (ingredient_name) values (?)
Hibernate: insert into ingredient (ingredient_name) values (?)
我不希望将其插入成分表,因为我已经在表格中填充了1000个成分名称。实际上,我只想用给定成分的键插入shampoo_ingredients中。
Hibernate: insert into shampoo_ingredients (fk_shampoo, fk_ingredient) values (?, ?)
Hibernate: insert into shampoo_ingredients (fk_shampoo, fk_ingredient) values (?, ?)
洗发水实体
@Entity
@Table(name="shampoo")
public class Shampoo {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="shampoo_id")
private int id;
@Column(name="shampoo_name")
private String name;
@OneToOne(cascade=CascadeType.ALL)
@JoinColumn(name="shampoo_company")
private Company company;
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(
name="shampoo_ingredients",
joinColumns=@JoinColumn(name="fk_shampoo"),
inverseJoinColumns=@JoinColumn(name="fk_ingredient")
)
private List<Ingredient> ingredients;
public Shampoo() {
}
public Shampoo(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public List<Ingredient> getIngredients() {
return ingredients;
}
public void setIngredients(List<Ingredient> ingredients) {
this.ingredients = ingredients;
}
@Override
public String toString() {
return "Shampoo [id=" + id + ", name=" + name + ", company=" + company + ", ingredients=" + ingredients + "]";
}
}
成分实体
@Entity
@Table(name="ingredient")
public class Ingredient {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="ingredient_id")
private int id;
@Column(name="ingredient_name")
private String name;
@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(
name="shampoo_ingredients",
joinColumns=@JoinColumn(name="fk_ingredient"),
inverseJoinColumns=@JoinColumn(name="fk_shampoo")
)
private List<Shampoo> shampoos;
public Ingredient() {
}
public Ingredient(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Shampoo> getShampoos() {
return shampoos;
}
public void setShampoos(List<Shampoo> shampoos) {
this.shampoos = shampoos;
}
@Override
public String toString() {
return "Ingredient [id=" + id + ", name=" + name + ", shampoos=" + shampoos + "]";
}
}
洗发水控制器
@Controller
@RequestMapping("/shampoo")
public class ShampooController {
//inject DAO into controller
@Autowired
private ShampooDAO shampooDAO;
@Autowired
private CompanyDAO companyDAO;
@Autowired
private IngredientDAO ingredientDAO;
@Autowired
private ConversionService cs;
@RequestMapping("/list")
public String listShampoos(Model theModel) {
//get shampoo from DAO
List<Shampoo> theShampoos = shampooDAO.getShampoos();
//add shampoo to model
theModel.addAttribute("shampoos", theShampoos);
return "list-shampoos";
}
@GetMapping("/showFormForAdd")
public String showFormForAdd(Model theModel, ModelMap modelMap) {
//create model attribute to bind form data
Shampoo theShampoo = new Shampoo();
modelMap.addAttribute("shampoo", theShampoo);
List<Ingredient> theIngredients = ingredientDAO.getIngredients();
modelMap.addAttribute("ingredient", theIngredients);
return "shampoo-form";
}
@PostMapping("/saveShampoo")
public String saveShampoo(@ModelAttribute("shampoo") Shampoo theShampoo) {
System.out.println(theShampoo.getIngredients());
System.out.println(theShampoo.toString());
shampooDAO.saveShampoo(theShampoo);
return "redirect:/shampoo/list";
}
}
洗发精
@Repository
public class ShampooDAOImpl implements ShampooDAO {
@Autowired
private SessionFactory sessionFactory;
@Transactional
public List<Shampoo> getShampoos() {
//get current hibernate session
Session currentSession = sessionFactory.getCurrentSession();
//create query
Query<Shampoo> theQuery = currentSession.createQuery("from Shampoo", Shampoo.class);
//execute query and get result list
List<Shampoo> shampoos = theQuery.getResultList();
//return list of shampoo
return shampoos;
}
@Transactional
public void saveShampoo(Shampoo theShampoo) {
Session currentSession = sessionFactory.getCurrentSession();
currentSession.save(theShampoo);
}
}
洗发水形式
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<title>Add New Shampoo</title>
<script src="${pageContext.request.contextPath}/resources/jquery-3.3.1.min.js"></script>
<script src="${pageContext.request.contextPath}/resources/chosen.jquery.js"></script>
<link type="text/css" href="<%=request.getContextPath() %>/resources/chosen.css" rel="stylesheet"/>
<script>
$(document).ready(function(e){
// first initialize the Chosen select
$('.test').chosen();
});
</script>
</head>
<body>
<form:form cssClass="form" action="saveShampoo" modelAttribute="shampoo" method="POST">
<table>
<tbody>
<tr>
<td><label>Name:</label></td>
<td><form:input path="name" /></td>
</tr>
<tr>
<td><label>Company:</label></td>
<td><form:input path="company" /></td>
</tr>
<tr>
<td><label>Ingredients:</label></td>
<td><form:select cssClass="test" multiple="true" path="Ingredients" >
<form:options items="${ingredient}" itemValue="name" itemLabel="name" />
</form:select></td>
</tr>
</tbody>
</table>
<input id= "submit" type="submit" value="Save"/>
</form:form>
</body>
</html>
最佳答案
关键是,当您尝试加入实体香波和成分时,必须从数据库中获取它们。
假设您创建了一种新的洗发水。你可以做:
Shampoo s = new Shampoo();
//All your own stuffs about Shampoo object
现在,您想加入洗发水和成分;并假设您想加入的配料名称是“ IngrA”和“ IngrB”,您不能这样做:
Ingredient a = new Ingredient();
a.setName("IngrA");
Ingredient b = new Ingredient();
b.setName("IngrB");
List<Ingredient> ingrs = new ArrayList<>(2);
ingrs.add(a);
ingrs.add(b);
s.setIngredients(ingrs);
repository.save(s); //Error will be thrown here; If you have CascadeType.All an insert will be done
为什么会有错误?因为根据Hibernate,
a
和b
是“新”对象,所以您从未保存过。因此,休眠告诉您:“在加入他们之前,您必须保存他们”。另一方面,由于您有
CascadeType.All
休眠,休眠会尝试插入这些“新”对象您必须做的是在数据库中搜索成分,然后进行设置。
我的意思是这样的:
Ingredient a = ingredientDao.findByName("IngrA");
Ingredient b = ingredientDao.findByName("IngrB");
List<Ingredient> ingrs = new ArrayList<>(2);
if(a!=null)
ingrs.add(a);
if(b!=null)
ingrs.add(b);
s.setIngredients(ingrs);
repository.save(s); //No error will be thrown here
为什么没有错误?因为您从DB检索了成分,现在它们不再分离了。
我希望它有用
安杰洛