如何在不更新具有多对多关系的子实体的情况下保留父实体?

Posted

技术标签:

【中文标题】如何在不更新具有多对多关系的子实体的情况下保留父实体?【英文标题】:How do i persist the parent entity without updating the child entity where they have many to many relationship? 【发布时间】:2019-06-09 13:10:10 【问题描述】:

我有一个父实体 A,它与实体 B 和实体 C 有多对多的关系,它们映射在 A_B 表和 A_C 表中。在坚持的同时,我只想坚持 A,A_B 和 A_c 而不坚持 B 和 C 。 如果我不使用 cascadetype.all,我会收到“object references an unsaved transient instance - save the transient instance before flushing”错误。 如果我使用 cascade.all 所有表格都会更新。

这是我的父实体 Student(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,但不保留课程和国家/地区表

【问题讨论】:

Hibernate - How to persist only the parent, keeping the children as they are的可能重复 【参考方案1】:

首先,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@Service- 注释 bean。

【讨论】:

【参考方案2】:

你需要从@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 对象以支持这些中间实体。

【讨论】:

以上是关于如何在不更新具有多对多关系的子实体的情况下保留父实体?的主要内容,如果未能解决你的问题,请参考以下文章

多对多(自相关)特定订单实体框架

Fluent NHibernate:如何在关系表上映射具有附加属性的多对多关系?

如何在不破坏范式的情况下为多对多关系构建连接表

具有多个多对多关系的休眠批处理事务

Entity Framework Core 如何在不使用 Include().ThenInclude() 的情况下从模型中列出一对多从多对多

Django - 使用表单集在不通过表的情况下建立 2 个模型之间的多对多关系