我正在尝试使用H2数据库在我自己的项目中复制Hibernate示例from here(使用Flyway迁移进行初始化)和Spring Data。问题是,当我保存父对象BookCategory时,它还保存了子对象(Book对象),但没有保存book_category_id,即“子”对象的“父ID”!
我的SQL看起来像这样:
CREATE SEQUENCE SEQ_IDNUMS START WITH 1;
CREATE TABLE book_category (
id int(11) NOT NULL,
name varchar(255) NOT NULL,
PRIMARY KEY (id,name)
);
CREATE TABLE book (
id int(11) NOT NULL,
name varchar(255) DEFAULT NULL,
book_category_id int(11) DEFAULT NULL
);
alter table book add constraint pk_book_id PRIMARY KEY (id);
alter table book add constraint fk_book_bookcategoryid FOREIGN KEY(book_category_id) REFERENCES book_category(id) ON DELETE CASCADE;
然后,我的Book课:
import javax.persistence.*;
@Entity
public class Book{
private int id;
private String name;
private BookCategory bookCategory;
public Book() { }
public Book(String name) { this.name = name; }
@Id
@SequenceGenerator(name = "seq_idnums", sequenceName = "seq_idnums", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_idnums")
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; }
@ManyToOne
@JoinColumn(name = "book_category_id")
public BookCategory getBookCategory() { return bookCategory; }
public void setBookCategory(BookCategory bookCategory) { this.bookCategory = bookCategory; }
}
还有我的BookCategory类:
import java.util.Set;
import javax.persistence.*;
@Entity
@Table(name = "book_category")
public class BookCategory {
private int id;
private String name;
private Set<Book> books;
public BookCategory(){ }
public BookCategory(String name, Set<Book> books) {
this.name = name;
this.books = books;
}
@Id
@SequenceGenerator(name = "seq_idnums", sequenceName = "seq_idnums", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_idnums")
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; }
@OneToMany(mappedBy = "bookCategory", cascade = CascadeType.ALL)
public Set<Book> getBooks() { return books; }
public void setBooks(Set<Book> books) { this.books = books; }
}
至于节省的部分,我正按照示例中的步骤进行操作:
BookCategory categoryA = new BookCategory("Category A", new HashSet<Book>(){{
add(new Book("Book A1"));
add(new Book("Book A2"));
add(new Book("Book A3"));
}});
BookCategory categoryB = new BookCategory("Category B", new HashSet<Book>(){{
add(new Book("Book B1"));
add(new Book("Book B2"));
add(new Book("Book B3"));
}});
bookCategoryRepository.save(new HashSet<BookCategory>() {{
add(categoryA);
add(categoryB);
}});
最后,由于使用了Spring Data魔术,我的BookCategoryRepository看起来只有这样:
import org.springframework.data.repository.CrudRepository;
public interface BookCategoryRepository extends CrudRepository<BookCategory, Long> {
}
已经问过几次这个问题,例如here。在那里,被接受的答案基本上说带有
@JoinColumn
的一面是应保留的一面。正如您在上面的代码中看到的那样,我正在复制的示例并没有做到这一点,而是坚持到了另一侧,显然对于作者来说还不错。如果我确实尝试保留Book对象,则会出现以下异常:[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : Book.bookCategory -> BookCategory; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : Book.bookCategory -> BookCategory] with root cause
org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : Book.bookCategory -> BookCategory
...这不是解决方案。
现在,另一个问题here及其答案表明,问题在于id列必须可为空。但是,那会导致Flyway错误,而且我真的不想让我的id字段成为可空值:
Invocation of init method failed; nested exception is org.flywaydb.core.internal.dbsupport.FlywaySqlScriptException: Error executing statement at line 14: alter table book add constraint pk_book_id PRIMARY KEY (id)
还有一个问题here建议必须删除“
nullable = false
”,但我首先没有删除它。我完全不知所措。可能有什么问题以及如何解决?
最佳答案
在您当前的映射中,@ManyToOne
是所有者,但使用new Book("Book A1")
创建新的书对象时却未在此新书对象中设置bookCategory字段(所有者)。
您可以设置该字段并尝试保存它吗?
经过以下代码测试,可以直接使用session.save(categoryA)
代替repository
保存。但这应该没有什么不同。
BookCategory categoryA = new BookCategory("Category A");
categoryA.setBooks(new HashSet<Book>(){{
add(new Book("Book A1", categoryA));
add(new Book("Book A2", categoryA));
add(new Book("Book A3", categoryA));
}});
正如您可能已经猜到的那样,为了设置
bookCategory
字段,我在下面的类中添加了一个新的构造函数,以使其更加简单。您也可以使用setter。public BookCategory(String name) {
this.name = name;
}
public Book(String name, BookCategory category) {
this.name = name;
this.bookCategory = category;
}