我有一个父实体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,而不会一直保留在课程表和国家/地区表中
答案 0 :(得分:0)
您需要从@ManyToMany
切换到@OneToMany
。
然后,您可以将级联放在Student
上,并在StudentCourse
和StudentCountry
实体中忽略它。
唯一的缺点是您需要为那些链接表创建中间实体:
学生
@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
关联,PERSIST
,MERGE
,REMOVE
没有多大意义。您可能希望保留REFRESH
和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
,function () {
var urlPart = document.location.pathname.split('/')[3];
return urlPart;
}
的bean中。