java - 如何在不更新具有多对多关系的子实体的情况下保留父实体?
问题描述
我有一个父实体 A,它与实体 B 和实体 C 具有多对多的关系,它们映射在 A_B 表和 A_C 表中。在坚持的同时,我只想坚持 A,A_B 和 A_c 而不坚持 B 和 C 。如果我不使用 cascadetype.all我得到“对象引用了一个未保存的瞬态实例 - 在刷新之前保存瞬态实例”错误。 如果我使用 cascade.all 所有表都会更新。
这是我的父实体学生(getter 和 setter 存在但未显示)
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int studentId;
......
......
@ManyToMany()
@JoinTable(name = "student_preferredCountry",joinColumns = @JoinColumn(name
= "studentId"),inverseJoinColumns = @JoinColumn(name = "countryId"))
private Set<Country> countries;
@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "student_preferred_course",joinColumns = @JoinColumn(name
= "studentId"),inverseJoinColumns = @JoinColumn(name = "courseId"))
private Set<Course> courses;
这是我的孩子实体课程
@Entity
public class Course {
private String courseName;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int courseId;
@ManyToMany(mappedBy = "courses")
private List<Student> coursestudents;
这是我的另一个子实体 Country
@Entity
public class Country {
@Id
private int countryId;
private String countryName;
@ManyToMany(mappedBy = "countries")
private List <Student> students;
这就是我坚持的方式
public boolean studententry(@RequestBody Student stud){
studentRepository.save(stud);
这是示例 json 请求
{
"studentFname": "fdfd",
"studentMname": "dfdf",
"studentLname": "fdf",
"studentFatherName": "fd",
"studentMotherName": "dfd",
"studentEmail": "dfd",
"studentAddress": "df",
"studentPhone": "df",
"countries": [
{
"countryId": "1",
"countryName": "aus"
},
{
"countryId": "2",
"countryName": "newz"
}
],
"courses": [
{
"course_id": "1",
"course_name": "IELTS"
},
{
"course_id": "2",
"course_name": "TOEFL"
}
]
}
基本上我想添加所有学生属性,student_courses,student_country,但不坚持课程和国家表
解决方案
首先,cascade=ALL
与你想要的相反。ALL=PERSIST,MERGE,REFRESH,DETACH,REMOVE
, 所以它基本上说'每当我存储、更新或删除 ' 时,Student
对所有相关联Courses
的 ' 做同样的事情。
对于@ManyToMany
关联,PERSIST
, MERGE
,REMOVE
没有多大意义。您可能想保留REFRESH
and DETACH
,但建议您摆脱其余部分。
其次,删除MERGE
有得到的副作用TransientObjectException
。发生这种情况是因为您试图保存引用其他分离实体的分离实体。您可能正在调用repository.save(student)
,但这只会合并Student
实体,并且引用的Course
实体保持分离状态。
要解决此问题,您需要将分离的实体实例替换为具有相同 id 的托管实体实例:
Set<Courses> managedCourses = new HashSet<>();
for (Course course : stud.getCourses()) {
managedCourses.add(courseRepository.getOne(course.getId()));
}
stud.setCourses(managedCourses);
studentRepository.save(stud); //no TransientObjectException this time!
(注意getOne()
循环中的使用;您可能很想使用findAllById()
,认为它会更高效,但好处getOne()
是它不会从数据源中获取关联的实体状态。由此用例专门getOne()
提供JpaRepository
记住:建立实体之间的关联)
最后,我可以看到stud
用 注释@RequestBody
,表明我们在Controller
这里上课。要使上述方法起作用,您希望使用@Transactional
. 由于事务控制器方法并不是很好的做法,我建议将studententry
方法的主体提取到单独的@Transactional
, @Service
- 注释 bean。
推荐阅读
- swift - 什么是提供的存储桶?
- deep-linking - 打开 Slack 并在消息框中自动填充文本的深层链接
- google-apps-script - 尝试为谷歌工作表的数小时进出脚本创建自动计算
- python - 如何制作墙贴图?
- microsoft-graph-api - 尝试获取非消费者数据时,有没有办法防止消费者帐户出现 404 错误?
- java - JavaFX 任务/线程没有一致地填充表格视图
- twilio - Twilio Studio 未列出服务
- javascript - HTML 内容没有被 innerHTML 清除,只是添加到之前加载的内容的底部
- cumulocity - 您如何反转从 REST API 响应中给出的数据?
- java - TFS SDK 11.0.0 - TFS 2015 Update 3 服务器 - NullPointerException 创建缺陷