我在学校和学生实体之间建立了OneToMany关系。我想要做的是当我保存学校对象时不保存或更新学生对象。(当然不要删除它们)
当我尝试保存下面的学校对象时,它也会更新我的学生对象,但我不希望它们更新但只能加入。有什么办法吗?
我删除了Cascade,但它仍无效。
School school = new School();
school.setStudents(studentList);
repository.save(school);
我的实体;
@OneToMany(fetch = FetchType.EAGER)
@JoinTable(name = "school_student", joinColumns = {@JoinColumn(name = "school_id")},
inverseJoinColumns = {@JoinColumn(name = "student_id")})
private List<Student> students;
编辑:
答案 0 :(得分:8)
当我尝试保存下面的学校对象时,它也会更新我的学生对象,但我不希望它们更新但只能加入。有什么办法吗?
我认为您的意思是您希望管理协会本身 - 即哪些学生与学校相关 - 而不是学生的详细信息。出于概念和实际原因,我担心,这没有多大意义。
从概念上讲,一对多关系的“拥有”方面总是“很多”。当您通过连接表管理关联时,这有点任意(但仍然是真的),但是当通过实体属性直接管理关联时,这很重要。更新关联需要管理拥有方的实体,在这种情况下是Student
s。
实际上,从School
方面管理协会只会带来一些困难。例如,如果将新 Student
添加到School
,该怎么办?在关联可以持久化之前,该学生需要持久化,但是您希望避免这种情况发生。将Student
从School
移动到另一个School
时,会出现类似但较小的问题。
现在,您确实可以避免将持久性操作从Student
级联到School
,但我希望这样做的结果是无法管理它们之间的关联以及管理cascade
秒。如果这是您想要做的,那么您将省略@OneToMany
注释中的任何Student
属性,或者显式指定级联类型的空列表。
但是,请注意,当您提交对持久性上下文的更改时,将保存对所有持久性实体的更改。如果要修改School
实体而不将这些更改保存到数据库中,那么最好的替代方法可能是分离这些实体,或者制作它们的分离副本。
<强>更新强>:
正如评论中所阐明的,当应用程序无权更新Student
的基表时,基本问题是如何修改Student
和School
实体之间的关系,但是对表示关系的连接表有足够的权限。你不能自动对School
的持续更改自动执行此操作,因为Student
不是 - 也不可能 - 是关系的拥有方。
澄清一下:JPA在您将School
实体移动到其他Student
时尝试保存它们,因为它的目的是School
与其特定{{1}之间的关联}}是Student
状态的一部分。如果Student
实体附加到持久性上下文并且是脏的(例如,因为它被分配给不同的School
),则在提交对PC的更改时将更新它。这与级联或School
的状态没有任何关系,除了您通过将Student
s的状态移动到不同的School
s students
来间接修改EntityManager
的状态。 1}}列表。
由于您使用的是联接表,因此您可以修改对象模型,以便将每个学生/学校协会表示为一个实体。如果这些协会具有自己的属性,例如注册日期,那将是有意义的,否则我不会推荐它。
另一种方法是编写本机查询(通过JPA);你可以在网上找到很多关于它的信息,例如https://blogs.oracle.com/JPQL01/entry/native_query_in_java_persistence。然而,这确实引入了一些问题。特别是,它使Student
的缓存无效,如果不处理,可能会产生严重的问题。如果您正常执行更新,它会产生实体更改而不会触发将触发的实体生命周期方法,这对您来说也很重要。
对于缓存,解决方案是分离受影响的School
和EntityManager.detach()
(旧的和新的)实体。您可以选择性地使用EntityManager.clear()
,或 en masse {{1}}。之后,您需要重新查询或合并您想要继续使用的实体,这可能会产生一些混乱的问题,具体取决于它们的使用范围以及其他代码对它们的假设。
至于生命周期方法,如果你需要确保它们开火,那么你可以自己调用它们。
This article提供了有关您可能需要执行的操作的更多详细信息。
答案 1 :(得分:4)
也许你可以试试@JoinColumn:
@OneToMany
@JoinColumn(name = "school_id", updatable = false, insertable = false)
private List<Student> students;
答案 2 :(得分:2)
您向我们展示的结构不是您列出的一对多关系。这是多对多的。
@ManyToMany(cascade = {}, targetEntity = Student.class)
@JoinTable(name = "school_student", joinColumns = {
// references column "id" of "school"
@JoinColumn(name = "school_id", referencedColumnName = "id")
}, inverseJoinColumns = {
// references column "id" of "student"
@JoinColumn(name = "student_id", referencedColumnName = "id")
})
private List<Student> students;
真的那么简单。
答案 3 :(得分:1)
删除这些注释
@OneToMany(fetch = FetchType.EAGER)
@JoinTable(name = "school_student", joinColumns = {@JoinColumn(name = "school_id")},
inverseJoinColumns = {@JoinColumn(name = "student_id")})
因此,hibernate实体管理器可能不会将此对象与自动更新相关联
因此,您必须使用set方法向学生编写手动查询,或者将学生列表作为代码逻辑中的参数来设置学生
private List<Student> students;
然后它将按预期工作
答案 4 :(得分:1)
我建议删除school.setStudents(studentList)
因为你告诉hibernate编辑这些学生的many2one字段。尝试session.clear()如果它不起作用,除了删除声明,并确保你从学生方面这样做。
我的意思是为学生定义一个不相反的学校。
package main;
import org.hibernate.Session;
import hibernate.ConnectionFactory;
import hibernate.models.Car;
import hibernate.models.Client;
public class MainClass {
public static void main(String[] args) {
try {
/*
* if you remove cascading configuration from both side
* hibernate will not save unsaved transient instance
*/
if(ConnectionFactory.buildSessionFactory()){
Session session = ConnectionFactory.getSessionFactory().openSession();
session.beginTransaction();
// save a client instance
Client client = new Client();
client.setName("client");
session.save(client);
session.getTransaction().commit();
session.close();
session = ConnectionFactory.getSessionFactory().openSession();
session.beginTransaction();
Car car = new Car();
car.setName("Audi");
client = (Client) session.get(Client.class, 1);
client.setName("client1");
car.setClient_id(client);
/* inset statement
* to prevent hibernate from updating client instance just clear
* the session and save only the wanted instance
*/
session.clear();
session.save(car);
session.getTransaction().commit();
session.close();
session = ConnectionFactory.getSessionFactory().openSession();
session.beginTransaction();
car = (Car) session.get(Car.class, 1);
car.setName("Audi1");
client = (Client) session.get(Client.class, 1);
client.setName("Charif1");
car.setClient_id(client);
/* update statement
* to prevent hibernate from saving other instance just clear
* the session and update only the wanted instance
*/
session.clear();
session.saveOrUpdate(car);
session.getTransaction().commit();
session.close();
/**
* but you you are doing from the wrong side
* you are trying to tell that this client have
* this cars but Hibernate will try to link this
* car by editing the many2one field ??!!
* so remove the statement
* school.setStudents(studentList);
*
*
*/
}
} catch (Exception e) {
// TODO: handle exception
}
try {
ConnectionFactory.getSessionFactory().close();
} catch (Exception e) {e.printStackTrace();}
}
}