如何保持父实体而不更新子实体之间存在多对多关系的子实体?

时间:2019-01-15 13:53:21

标签: java hibernate rest spring-boot jpa

我有一个父实体A,它与映射在A_B表和A_C表中的实体B和实体C有很多对很多的关系。在持久化时,我只想持久化A,A_B和A_c而不持久化B和C。 如果我不要使用 cascadetype.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;

这是我的另一个子实体国家/地区

@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,而不会一直保留在课程表和国家/地区表中

2 个答案:

答案 0 :(得分:0)

您需要从@ManyToMany切换到@OneToMany

然后,您可以将级联放在Student上,并在StudentCourseStudentCountry实体中忽略它。

唯一的缺点是您需要为那些链接表创建中间实体:

学生

@OneToMany(cascade = CascadeType.ALL, mappedBy="student")
private Set<StudentCourse> courses;

学生课程

@ManyToOne
private Student student;

@ManyToOne
private Course course;

课程

@Entity
public class Course {
private String courseName;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int courseId;
@ManyToMany(mappedBy = "courses")
private List<StudentCourse> coursestudents;

您需要相应地更改传入的json对象,以支持c的那些中间实体。

答案 1 :(得分:0)

首先,cascade=ALL的作用与您想要的相反。 ALL=PERSIST,MERGE,REFRESH,DETACH,REMOVE,因此它基本上说:“无论我何时存储,更新或删除Student,对所有关联的Courses都执行相同操作”。

对于@ManyToMany关联,PERSISTMERGEREMOVE没有多大意义。您可能希望保留REFRESHDETACH,但最好将其除去。

第二,删除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方法的主体提取到带有单独注释的@Servicefunction () { var urlPart = document.location.pathname.split('/')[3]; return urlPart; } 的bean中。