我发现,当一对多关系中的父表更新时,子表上依赖数据的外键被设置为空,在子表上留下孤儿记录。
我有两个用Hibernate标记注释的Java类。父表为:
@Entity
@Table(name = "PERSON")
public class Person implements Serializable {
// Attributes.
@Id
@Column(name="PERSON_ID", unique=true, nullable=false)
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer personId;
@Column(name="NAME", nullable=false, length=50)
private String name;
@Column(name="ADDRESS", nullable=false, length=100)
private String address;
@Column(name="TELEPHONE", nullable=false, length=10)
private String telephone;
@Column(name="EMAIL", nullable=false, length=50)
private String email;
@OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name="PERSON_ID")
private List<Book> books;
子表为:
Entity
@Table(name = "BOOK")
public class Book implements Serializable {
// Attributes.
@Id
@Column(name="BOOK_ID", unique=true, nullable=false)
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer bookId;
@Column(name="AUTHOR", nullable=false, length=50)
private String author;
@Column(name="TITLE", nullable=false, length=50)
private String title;
@Column(name="DESCRIPTION", nullable=false, length=500)
private String description;
@Column(name="ONLOAN", nullable=false, length=5)
private String onLoan;
// @ManyToOne
// private Person person;
但是当在Person上发布更新时,与父类相关的任何Books记录都被设置为null。
Book表是:
CREATE TABLE BOOK (
BOOK_ID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
AUTHOR VARCHAR(50) NOT NULL,
TITLE VARCHAR(100) NOT NULL,
DESCRIPTION VARCHAR(500) NOT NULL,
ONLOAN VARCHAR(5) NOT NULL,
PERSON_ID INTEGER,
CONSTRAINT PRIMARY_KEY_BOOK PRIMARY KEY(BOOK_ID),
CONSTRAINT FOREIGN_KEY_BOOK FOREIGN KEY(PERSON_ID) REFERENCES PERSON(PERSON_ID))
Person controller
中的update
方法为:
@RequestMapping(value = "/profile", method = RequestMethod.POST)
public String postProfile(@ModelAttribute("person") Person person,
BindingResult bindingResult,
Model model) {
logger.info(PersonController.class.getName() + ".postProfile() method called.");
personValidator.validate(person, bindingResult);
if (bindingResult.hasErrors()) {
return "view/profile";
}
else {
personService.update(person);
model.addAttribute("person", person);
return "view/options";
}
}
实际DAO
水平方法为:
@Override
public void update(Person person) {
logger.info(PersonDAOImpl.class.getName() + ".update() method called.");
Session session = sessionFactory.openSession();
Transaction transaction = session.getTransaction();
try {
transaction.begin();
session.update(person);
transaction.commit();
}
catch(RuntimeException e) {
Utils.printStackTrace(e);
transaction.rollback();
throw e;
}
finally {
session.close();
}
}
所以我假设update
是问题的原因,但为什么?
我已经尝试了merge
, persist
和saveOrUpdate
方法作为替代方案,但无济于事。
关于我的Book表没有@ManyToOne
的注释这一事实,禁用这个标记是我可以使LAZY
抓取工作的唯一方法。
这种情况看起来也与Hibernate的一对多非常相似:"many"side record's的外键自动更新为null, Hibernate Many to one将外键更新为null,但是如果我对自己的表采用这些问题中对类指定的更改,我的应用程序甚至拒绝编译,似乎是因为在Person表中使用mappedBy
的问题。
欢迎提出建议。
控制器方法改为:
// Validates and updates changes made by a Person on profile.jap
@RequestMapping(value = "/profile", method = RequestMethod.POST)
public String postProfile(@ModelAttribute("person") Person person,
BindingResult bindingResult,
Model model) {
logger.info(PersonController.class.getName() + ".postProfile() method called.");
// Validate Person.
personValidator.validate(person, bindingResult);
if (bindingResult.hasErrors()) {
return "view/profile";
}
else {
// Get current Person.
Person currPerson = personService.get(person.getPersonId());
// Set Books to updated Person.
person.setBooks(currPerson.getBooks());
personService.update(person);
model.addAttribute("person", person);
return "view/options";
}
}
它成功了
我假设postProfile()
方法接收一个Person实例,该实例只包含由web表单发布的人的ID、姓名、地址等,但其图书列表为null或空。
你告诉Hibernate去救那个人。因此,你实际上是在告诉Hibernate,这个由给定ID标识的人有一个新名字、新地址、新电子邮件……和一个新的图书列表,碰巧是空的,这应该被保存到数据库中。
Hibernate做你让它做的事情:它保存这个人的新状态。由于新人没有任何书,他以前拥有的所有书都不属于任何人。
您必须从数据库中获得实际的、持久的Person实体,并将应该实际修改的字段从新的Person复制到持久的Person。
或者您必须从数据库中预加载持久person,并让Spring填充这个持久person,而不是每次创建一个新实例。看到http://docs.spring.io/spring/docs/4.0.x/spring-framework-reference/htmlsingle/mvc-ann-modelattrib-methods .
您也可以尝试从@OneToMany
中删除@JoinColumn
,而使用mappedBy
。
参见:Hibernate多对一更新外键为null