如何用JPA防止保存子对象?

hel*_*one 7 java jpa

我在学校和学生实体之间建立了OneToMany关系.我想要做的是当我保存学校对象时不保存或更新学生对象.(当然不要删除它们)

当我尝试保存像下面的学校对象时,它也会更新我的学生对象,但我不希望它们更新但只能加入.有什么办法吗?

我删除了Cascade但它仍然无法正常工作.

School school = new School();
school.setStudents(studentList);
repository.save(school);
Run Code Online (Sandbox Code Playgroud)

我的实体;

@OneToMany(fetch = FetchType.EAGER)
@JoinTable(name = "school_student", joinColumns = {@JoinColumn(name = "school_id")},
        inverseJoinColumns = {@JoinColumn(name = "student_id")})
private List<Student> students;
Run Code Online (Sandbox Code Playgroud)

编辑:

  • 当我尝试保存/更新/删除School对象时,请勿保存/更新/删除Student对象.认为我在Student表上没有任何插入/更新/删除权限.

Joh*_*ger 9

当我尝试保存像下面的学校对象时,它也会更新我的学生对象,但我不希望它们更新但只能加入.有什么办法吗?

我认为你的意思是你希望管理协会本身 - 即哪些学生与学校有关 - 而不是学生的细节.出于概念和实际原因,我担心这没有多大意义.

从概念上讲,一对多关系的"拥有"方面总是"很多".当您通过连接表管理关联时,这有点任意(但仍然是真的),但是当通过实体属性直接管理关联时,这很重要.更新关联需要管理拥有方的实体,在这种情况下是您Student的实体.

实际上,从School侧面管理协会只会带来一些困难.例如,如果将 Student内容添加到School?在关联可以持久化之前,该学生需要持久化,但是您希望避免这种情况发生.当你Student从一个移动到另一个时,存在类似但较小的问题School.

现在,你确实可以避免从Schools到它们Student的级联持久性操作,但我希望这样做的结果是无法管理它们与管理Schools 之间的关联.如果这是您想要做的,那么您将省略注释中的任何cascade属性@OneToMany,或者显式指定级联类型的空列表.

但是,请注意,当您提交对持久性上下文的更改时,将保存对所有持久性实体的更改.如果要在Student不将这些更改保存到数据库的情况下修改实体,那么最好的替代方法可能是分离这些实体,或者创建它们的分离副本.

更新:

正如在注释中已经阐明的那样,基本问题是当应用程序没有更新基表的权限时如何修改实体SchoolStudent实体之间的关系Student,但是对表示关系的连接表具有足够的权限.你不能自动结合对Schools的持久更改来做到这一点,因为School它不是 - 也不可能 - 是关系的拥有方.

澄清一下:JPA试图Student在你将它们移动到不同的Schools 时保存你的实体,因为为了它的目的,a Student和他的特定之间的关联School是它的状态的一部分Student.如果Student实体附加到持久性上下文并且是脏的(例如,因为它被分配给不同的实体School),那么在提交对PC的更改时它将被更新.School除了你Student通过将它们移动到不同的Schools' students列表间接修改s 的状态之外,这与级联或状态没有任何关系.

由于您使用的是连接表,因此您可以修改对象模型,以便将每个学生/学校协会本身表示为一个实体.如果这些协会具有自己的属性,例如注册日期,那将是有意义的,否则我不会推荐它.

另一种方法是编写本机查询(通过JPA); 你会在网上找到很多关于它的信息,比如https://blogs.oracle.com/JPQL01/entry/native_query_in_java_persistence.然而,这确实引入了一些问题.特别是,它使EntityManager缓存失效,如果不处理,可能会产生严重的问题.如果您正常执行更新,它会产生实体更改而不会触发将触发的实体生命周期方法,这对您来说也很重要.

对于缓存,解决的办法就是分离受影响StudentSchool(新旧)的实体.你可以用做选择性EntityManager.detach(),或集体使用EntityManager.clear().之后,您需要重新查询或合并您想要继续使用的实体,这可能会产生一些混乱的问题,具体取决于它们的使用范围以及其他代码对它们的假设.

至于生命周期方法,如果你需要确保它们触发,那么你可以自己调用它们.

本文提供了有关您可能需要执行的操作的更多详细信息.


小智 5

也许您可以尝试@JoinColumn:

@OneToMany
@JoinColumn(name = "school_id", updatable = false, insertable = false)
private List<Student> students;
Run Code Online (Sandbox Code Playgroud)