首页 > 解决方案 > 如何在不更新具有多对多关系的子实体的情况下保留父实体?

问题描述

我有一个父实体 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,但不坚持课程和国家表

标签: javahibernaterestspring-bootjpa

解决方案


首先,cascade=ALL与你想要的相反。ALL=PERSIST,MERGE,REFRESH,DETACH,REMOVE, 所以它基本上说'每当我存储、更新或删除 ' 时,Student对所有相关联Courses的 ' 做同样的事情。

对于@ManyToMany关联,PERSIST, MERGE,REMOVE没有多大意义。您可能想保留REFRESHand 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。


推荐阅读