Hibernate不保存OneToMany子级的父ID

时间:2016-03-15 14:01:13

标签: java hibernate spring-data

我尝试使用H2数据库(使用Flyway迁移初始化)和Spring Data在我自己的项目中复制Hibernate示例from here。问题是,当我保存父对象BookCategory时,它还保存了childen(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表明&#34; nullable = false&#34;必须删除,但我一开始就没有。

我完全不知所措。什么可能有问题以及如何解决?

2 个答案:

答案 0 :(得分:3)

使用当前映射@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;
  }

答案 1 :(得分:0)

我相信你的问题是保存操作不会从Book级联到BookCategory。 BookCategory是一个未保存的瞬态实例,因此您必须在保存书籍之前明确保存它,或者将Cascade选项添加到Book。

试试这个:

#cssmenu > ul > #facebook:hover > a::before,
#facebook li > a:hover::before {
  background-image: url(https://s.w-x.co/facebook.svg?1);
  background-size: 20px 20px;
  background-repeat: no-repeat;
  background-position: center center;
}

#cssmenu > ul > #twitter:hover > a::before,
#twitter li > a:hover::before {
  background-image: url(https://s.w-x.co/twitter.svg?1);
  background-size: 20px 20px;
  background-repeat: no-repeat;
  background-position: center center;
}

编辑:我想我误解了这个问题。请注意:您的复制粘贴不包括测试方法的开头;你是否像原始示例一样在@Transactional方法中?看起来你跟着它写了一切,一切都应该有效。