Java Spring数据Jpa从子级到父级级联

Java Spring数据Jpa从子级到父级级联,java,hibernate,jpa,spring-data-jpa,Java,Hibernate,Jpa,Spring Data Jpa,假设我有一个应用程序来处理一系列的书 我的应用程序允许向库中添加新书。创建书籍时,用户可以在列表中选择作者,如果作者还不存在,则可以将其添加到列表中,并在表单字段中提供其姓名。 填写表单时,数据被发送到WS,类似于 { "name" : "The Book name" "author" : { "name" : "author's name" } } 然后我将json映射到我的实体中 书籍: @Entity @Table(name = "book") public c

假设我有一个应用程序来处理一系列的书

我的应用程序允许向库中添加新书。创建书籍时,用户可以在列表中选择作者,如果作者还不存在,则可以将其添加到列表中,并在表单字段中提供其姓名。 填写表单时,数据被发送到WS,类似于

{ 
  "name" : "The Book name"
  "author" : {
     "name" : "author's name"
   }
}
然后我将json映射到我的实体中

书籍:

@Entity
@Table(name = "book")
public class Book{
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Author author;
}
作者

@Entity
@Table(name = "author")
public class Author{
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @OneToMany(mappedBy = "author", cascade = { CascadeType.ALL })
    private List<Book> books;
}
@实体
@表(name=“author”)
公共类作者{
@身份证
@列(name=“id”)
@GeneratedValue(策略=GenerationType.IDENTITY)
私人长id;
@列(name=“name”)
私有字符串名称;
@OneToMany(mappedBy=“author”,cascade={CascadeType.ALL})
私人书目;
}
这不会像用户尝试添加新作者那样工作,当我尝试添加新作者时。save()我将得到一个错误:

org.hibernate.TransientPropertyValueException:对象引用 未保存的瞬态实例

有没有办法用Spring数据Jpa处理这个问题,或者我必须手动检查json中是否有作者id,如果没有,也就是说这是一个新作者,手动运行作者创建,然后保存新书


谢谢

正如您所猜测的,正如Javadoc所说,
级联
操作必须级联到关联的目标“。但是,请确保您了解,
mappedBy
定义了关系的所有者实体。拥有实体是实际执行持久化操作的实体,除非被级联设置覆盖。在这种情况下,
Child
是所属实体

@Entity
public class Parent {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy="parent")
    private Set<Child> children;
您将通过持久化子对象来持久化父对象和子对象

tx.begin();
Parent p = new Parent();
Child c = new Child(); 
c.setParent(p);
em.persist(c);
tx.commit();
tx.begin();
Child cFound = em.find(Child.class, 1L);
em.remove(cFound);
tx.commit();
em.clear();
当您删除子项时,它将同时删除父项和子项

tx.begin();
Parent p = new Parent();
Child c = new Child(); 
c.setParent(p);
em.persist(c);
tx.commit();
tx.begin();
Child cFound = em.find(Child.class, 1L);
em.remove(cFound);
tx.commit();
em.clear();
这就是你的问题所在。如果你有一个以上的孩子会怎么样

em.clear();
tx.begin();
p = new Parent();
Child c1 = new Child(); 
Child c2 = new Child(); 
c1.setParent(p);
c2.setParent(p);
em.persist(c1);
em.persist(c2);
tx.commit();
在删除其中一个
子项之前,一切都很好

em.clear();
tx.begin();
cFound = em.find(Child.class, 2L);
em.remove(cFound);
tx.commit();
然后,当级联传播到父级
时,您将得到一个
完整性约束冲突
,但数据库中还有第二个
子级
。当然,你可以通过在一次提交中删除所有子项来解决问题,但这会变得有点混乱,不是吗

从概念上讲,人们倾向于认为传播是从父代到子代的,因此如果不是这样的话,这是非常违反直觉的。此外,如果你不想仅仅因为书店出售了作者所有的书就删除他或她的书,那情况又如何呢?在这种情况下,您可能是混合级联,有时是从子级到父级,有时是从父级到子级


一般来说,我认为最好在数据库代码中非常精确。阅读、理解和维护先保存父级然后再保存子级的代码要比在其他地方添加注释容易得多,我可能知道也可能不知道这些注释正在隐式地执行其他数据库操作

没有理由保存
Author
不起作用。您是否试图使用未保存的
作者
保存
书籍
?如果我尝试单独保存作者,然后再保存书籍,是的,它会工作,但这不是我的问题:)确实,我希望能够调用bookRepository。使用新的未保存作者保存(新书),我想知道我能不能用JPA,用一种级联的方式,或者别的什么方法来做到这一点?谢谢你的精确回答,为了你的时间,我将明确地创建作者,然后是这本书,非常感谢:)