我遇到@OneToMany的JPA问题,尤其是CascadeType.ALL的功能。 我有两个实体,一个父亲和一个孩子。
我向您展示我所拥有的一个例子: 预订
@Entity
@Table(name = "books")
public class Book implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
@JoinColumn(name="id_page")
private List<Page> pages;
...
...
}
PAGE
@Entity
@Table(name = "pages")
public class Page implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@Column(name = "id_book")
private int idBook;
...
...
}
所以,我只有一本书和几页。保存书籍时,由于使用了CascadeType.ALL,我还保存了其中包含的页面。 可行的方法是,实际上Hibernate尝试保存Book对象,并通过OneToMany意识到也有Page实体要保存,并借助Cascade ALL来启动查询。
我只是这样做:
bookRepository.save(book);
代表我要保存的书的json是这样的:
{
"id": 0,
"page": [
{
"id": 0,
"idBook": 0
}
]
}
但是,如果我在书号上输入0,则会创建它。如果我将idBook上的0放入页面中,则会出现如下错误:键(id_book)=(0)在表“ book”中不存在。
但是,我真正不明白的是:为什么如果我将一个id_book放入数据库中存在的json中,然后完成保存书实体,它会理解id是什么并将其正确插入数据库中?
因此,如果我提供此JSON。
{
"id": 0,
"page": [
{
"id": 0,
"idBook": 1
}
]
}
让我们考虑一下,在数据库上只有一本ID = 1的书。 保存后,会为我生成一行ID = 2的书,以及一个链接到ID = 2的书的页面?
这件事使我发疯。 如果给idBook = 0告诉他必须检索/生成它,他告诉我它不存在。另一方面,如果我给他一个确实存在的idBook(但不正确),他是否甚至没有考虑并正确使用他刚创建的Book的idBook?
我只是不想使用虚假的idBook,告诉他创建图书后必须获取idBook页面,就像他实际上一样。
答案 0 :(得分:1)
根据id_book
实体中Page
列的存在,我得出结论,您想使用双向映射。尝试这样做,让一个孩子成为关系的所有者:
@Entity
@Table(name = "pages")
public class Page implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@ManyToOne(fetch = FetchType.LAZY) // EAGER by default
@JoinColumn(name = "id_book")
private Book book;
...
...
}
@Entity
@Table(name = "books")
public class Book implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@OneToMany(mappedBy = "book", cascade = CascadeType.ALL) // Lazy fetch type by default
private List<Page> pages = new ArrayList<>();
...
...
// obligatory synchronization methods
public void addPage(Page page) {
pages.add(page);
page.setBook(this);
}
public void removePage(Page page) {
pages.remove(this);
page.setBook(null);
}
}
答案 1 :(得分:1)
考虑“我通过一本书,有一页(或多页)”,我也想分享我的解决方案。
与其他答案有点类似,我也在使用双向。我还包括集成测试。
图书实体
@Entity
public class Book {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
@OneToMany(mappedBy = "book", cascade = { CascadeType.PERSIST, CascadeType.MERGE })
private List<Page> pages;
public Book(String id, List<Page> pages) {
this.id = id;
this.pages = pages;
this.pages.stream().forEach(page -> page.attachedTo(this));
}
public String getId() {
return id;
}
public List<Page> getPages() {
return pages;
}
// Required by JPA
protected Book() {
}
}
页面实体
@Entity
public class Page {
@Id
@GeneratedValue(generator = "uuid")
@GenericGenerator(name = "uuid", strategy = "uuid2")
private String id;
private String content;
@ManyToOne
private Book book;
public Page(String content) {
this.content = content;
}
void attachedTo(Book book) {
this.book = book;
}
public String getId() {
return id;
}
public Book getBook() {
return book;
}
// Required by JPA
protected Page() {}
}
集成测试
@Test
@Transactional
public void saveBookWithPages() {
List<Page> firstBookPages = new ArrayList<>();
firstBookPages.add(new Page("content-0001"));
firstBookPages.add(new Page("content-0002"));
firstBookPages.add(new Page("content-0003"));
Book firstBook = new Book("first-book", firstBookPages);
firstBook = bookRepository.save(firstBook);
List<Page> secondBookPages = new ArrayList<>();
secondBookPages.add(new Page("content-0004"));
secondBookPages.add(new Page("content-0005"));
Book secondBook = new Book("second-book", secondBookPages);
secondBook = bookRepository.save(secondBook);
// query the first and second book
firstBook = bookRepository.findById(firstBook.getId()).orElseThrow(() -> new IllegalArgumentException("book id not found."));
secondBook = bookRepository.findById(secondBook.getId()).orElseThrow(() -> new IllegalArgumentException("book id not found"));
Assert.assertEquals(3, firstBook.getPages().size()); // check if first book has 3 pages
Assert.assertEquals(2, secondBook.getPages().size()); // check if second book has 2 pages
// query the first page of second book
Page secondBookFirstPage = pageRepository.findById(secondBook.getPages().get(0).getId()).orElseThrow(() -> new IllegalArgumentException(""));
Assert.assertEquals(secondBook.getId(), secondBookFirstPage.getBook().getId()); // check if the page is correctly associated to the book
}