更新@OneToMany 关系(Spring 引导,Spring 数据)
update @OneToMany relationship (Spring Boot, Spring Data)
我有一个 OneToMany 关系(两个表是双向的)。
当我保存医生的专业时,它确实有效,但是当我删除任何专业并更新医生时,它不起作用。
医生
@Entity
@Table(name = "doctors")
public class Doctor implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer doctorId;
@Column(length = 20)
private String doctorName;
@Column(length = 9)
private String doctorPhoneNo;
@Column(length = 30)
private String doctorEmailAddress;
private String doctorProfileImage;
@Enumerated(EnumType.STRING)
private Status status;
@Column(length = 6)
private String doctorCmp;
@OneToMany(mappedBy = "doctor", cascade = CascadeType.ALL)
// @JsonIgnore
private Set<DoctorSpecialties> doctorSpecialties;
public Doctor() {
this.doctorSpecialties = new HashSet<>();
}
public Doctor(Integer id){
this();
this.doctorId = id;
}
// getters y setters
}
专业
@Entity
@Table(name = "specialties")
public class Specialty implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer specialtyId;
private String specialtyName;
@OneToMany(mappedBy = "specialty")
@JsonIgnore
private Set<DoctorSpecialties> doctorSpecialties;
public Specialty() {
}
public Specialty(Integer id) {
this.specialtyId = id;
}
// getters and setters
}
专科医生
@Entity
@Table(name = "doctor_specialties")
public class DoctorSpecialties implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "doctor_id")
private Doctor doctor;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "specialty_id")
private Specialty specialty;
@OneToMany
@JoinColumn(name = "doctor_specialties_id")
private Set<Appointment> appointments;
@OneToMany
@JoinColumn(name = "doctor_specialties_id")
private Set<DoctorSchedule> schedules;
public DoctorSpecialties(){
}
public DoctorSpecialties(Specialty specialty, Doctor doctor){
this.specialty = specialty;
this.doctor = doctor;
}
getters / setters
}
控制器
@PostMapping(value = "/saveSpecialties/{id}")
public String saveDoctorSpecialties(@RequestParam(required = false) String[] specialtiesId,
@PathVariable Integer id, RedirectAttributes message) {
if (id != null && id > 0) {
Doctor doctor = doctorService.findOne(id);
if (doctor != null) {
// It does not work
doctor.getDoctorSpecialties().forEach(ds -> doctorSpecialtiesService.delete(ds.getId()));
doctor.getDoctorSpecialties().clear();
if (specialtiesId != null) {
for (String specialtyId : specialtiesId) {
DoctorSpecialties ds = new DoctorSpecialties();
ds.setSpecialty(new Specialty(Integer.parseInt(specialtyId)));
ds.setDoctor(doctor);
doctor.getDoctorSpecialties()
.add(ds);
}
}
doctorService.update(doctor);
message.addFlashAttribute("success", "Specialties successfully saved.");
return "redirect:/doctors/profile/{id}/specialties";
}
}
// specialtiesId = new String[]{};
message.addFlashAttribute("error", "Doctor doesn't exists");
return "redirect:/doctors/list";
}
控制台:
2021-10-30 21:19:13.330 DEBUG 44504 --- [nio-8080-exec-7] org.hibernate.SQL : select doctor0_.doctor_id as doctor_i1_3_0_, doctor0_.doctor_cmp as doctor_c2_3_0_, doctor0_.doctor_email_address as doctor_e3_3_0_, doctor0_.doctor_name as doctor_n4_3_0_, doctor0_.doctor_phone_no as doctor_p5_3_0_, doctor0_.doctor_profile_image as doctor_p6_3_0_, doctor0_.status as status7_3_0_ from doctors doctor0_ where doctor0_.doctor_id=?
2021-10-30 21:19:13.339 DEBUG 44504 --- [nio-8080-exec-7] org.hibernate.SQL : select doctorspec0_.doctor_id as doctor_i2_2_0_, doctorspec0_.id as id1_2_0_, doctorspec0_.id as id1_2_1_, doctorspec0_.doctor_id as doctor_i2_2_1_, doctorspec0_.specialty_id as specialt3_2_1_ from doctor_specialties doctorspec0_ where doctorspec0_.doctor_id=?
2021-10-30 21:19:13.401 DEBUG 44504 --- [nio-8080-exec-8] org.hibernate.SQL : select doctor0_.doctor_id as doctor_i1_3_0_, doctor0_.doctor_cmp as doctor_c2_3_0_, doctor0_.doctor_email_address as doctor_e3_3_0_, doctor0_.doctor_name as doctor_n4_3_0_, doctor0_.doctor_phone_no as doctor_p5_3_0_, doctor0_.doctor_profile_image as doctor_p6_3_0_, doctor0_.status as status7_3_0_ from doctors doctor0_ where doctor0_.doctor_id=?
2021-10-30 21:19:13.404 DEBUG 44504 --- [nio-8080-exec-8] org.hibernate.SQL : select specialty0_.specialty_id as specialt1_7_0_, doctorspec1_.id as id1_2_1_, doctor2_.doctor_id as doctor_i1_3_2_, specialty0_.specialty_name as specialt2_7_0_, doctorspec1_.doctor_id as doctor_i2_2_1_, doctorspec1_.specialty_id as specialt3_2_1_, doctorspec1_.specialty_id as specialt3_2_0__, doctorspec1_.id as id1_2_0__, doctor2_.doctor_cmp as doctor_c2_3_2_, doctor2_.doctor_email_address as doctor_e3_3_2_, doctor2_.doctor_name as doctor_n4_3_2_, doctor2_.doctor_phone_no as doctor_p5_3_2_, doctor2_.doctor_profile_image as doctor_p6_3_2_, doctor2_.status as status7_3_2_ from specialties specialty0_ inner join doctor_specialties doctorspec1_ on specialty0_.specialty_id=doctorspec1_.specialty_id inner join doctors doctor2_ on doctorspec1_.doctor_id=doctor2_.doctor_id where doctor2_.doctor_id=?
2021-10-30 21:19:13.565 DEBUG 44504 --- [nio-8080-exec-4] org.hibernate.SQL : select specialty0_.specialty_id as specialt1_7_0_, doctorspec1_.id as id1_2_1_, doctor2_.doctor_id as doctor_i1_3_2_, specialty0_.specialty_name as specialt2_7_0_, doctorspec1_.doctor_id as doctor_i2_2_1_, doctorspec1_.specialty_id as specialt3_2_1_, doctorspec1_.specialty_id as specialt3_2_0__, doctorspec1_.id as id1_2_0__, doctor2_.doctor_cmp as doctor_c2_3_2_, doctor2_.doctor_email_address as doctor_e3_3_2_, doctor2_.doctor_name as doctor_n4_3_2_, doctor2_.doctor_phone_no as doctor_p5_3_2_, doctor2_.doctor_profile_image as doctor_p6_3_2_, doctor2_.status as status7_3_2_ from specialties specialty0_ inner join doctor_specialties doctorspec1_ on specialty0_.specialty_id=doctorspec1_.specialty_id inner join doctors doctor2_ on doctorspec1_.doctor_id=doctor2_.doctor_id where doctor2_.doctor_id=?
没有删除语句...
------------------------
编辑 1
----------------------
Doctor find = doctorRepository.findById(1).get();
DoctorSpecialties ds1 = new DoctorSpecialties();
ds1.setSpecialty(specialtyRepository.findById(1).get());
ds1.setDoctor(find);
DoctorSpecialties ds2 = new DoctorSpecialties();
ds2.setSpecialty(specialtyRepository.findById(2).get());
ds2.setDoctor(find);
find.getDoctorSpecialties().add(ds1);
find.getDoctorSpecialties().add(ds2);
doctorRepository.save(find);
我做了一些测试,我不能完全理解。我做到了,它只在我实际添加两个对象时保存一次。
insert into doctor_specialties (id, doctor_id, specialty_id) values (null, ?, ?)
------------------------
编辑 2
----------------------
DoctorSpecialties(修改构造函数)
@Entity
@Table(name = "doctor_specialties")
public class DoctorSpecialties implements Serializable {
public DoctorSpecialties(Integer specialtyId, Doctor doctor) {
this.specialty = new Specialty(specialtyId);
this.doctor = doctor;
}
}
控制器
@PostMapping(value = "/saveSpecialties/{id}")
public String saveDoctorSpecialties(@RequestParam(required = false) String[] specialtiesId,
@PathVariable Integer id, RedirectAttributes message) {
if (id != null && id > 0) {
doctorService.saveDelete(id);
Doctor doctor = doctorService.findOne(id);
if (specialtiesId != null && specialtiesId.length > 0) {
for(String idSpecialty : specialtiesId){
doctorSpecialtiesService.save(new DoctorSpecialties(Integer.parseInt(idSpecialty), doctor));
}
}
message.addFlashAttribute("success", "Specialties successfully saved.");
return "redirect:/doctors/profile/{id}/specialties";
}
message.addFlashAttribute("error", "Doctor doesn't exists");
return "redirect:/doctors/list";
}
服务
@Override
@Transactional
public void saveDelete(Integer doctorId) {
Doctor doctor = this.doctorRepository
.findById(doctorId).get();
doctor.getDoctorSpecialties().clear();
}
控制台:
select doctor0_.doctor_id as doctor_i1_3_0_, doctor0_.doctor_cmp as doctor_c2_3_0_, doctor0_.doctor_email_address as doctor_e3_3_0_, doctor0_.doctor_name as doctor_n4_3_0_, doctor0_.doctor_phone_no as doctor_p5_3_0_, doctor0_.doctor_profile_image as doctor_p6_3_0_, doctor0_.status as status7_3_0_ from doctors doctor0_ where doctor0_.doctor_id=?
select doctorspec0_.doctor_id as doctor_i2_2_0_, doctorspec0_.id as id1_2_0_, doctorspec0_.id as id1_2_1_, doctorspec0_.doctor_id as doctor_i2_2_1_, doctorspec0_.specialty_id as specialt3_2_1_ from doctor_specialties doctorspec0_ where doctorspec0_.doctor_id=?
update appointments set doctor_specialties_id=null where
doctor_specialties_id=?
update doctor_schedules set
doctor_specialties_id=null where doctor_specialties_id=?
delete from doctor_specialties where id=?
有mappedBy
的一方为拥有方
This means something like: "modifications on this side of the relation
are already Mapped By the other side of the relation
, so no need to track it here separately in an extra
table."
查看此 url 了解更多信息 What is the "owning side" in an ORM mapping?
对于事务中的一对多关系,一旦你得到父项(Doctor
)并遍历其子项(DoctorSpecialties
)[换句话说,一旦您将整个父项及其子项加载到 Persistent 状态],您将无法通过其 repository
.
直接删除您的 DoctorSpecialties
您可以尝试类似下面的示例来查看它:
@Transactional
public void removeLine(Long doctorId, Long specId) {
Doctor doctor = this.doctorRepository // (1)
.findById(doctorId)
.orElseThrow(IllegalArgumentException::new);
this.doctorSpecialtiesRepository.deleteById(specId); // (2)
}
在 (1) 处,我们将 doctor
加载到持久状态。所以在这里,如果 fetch = FetchType.EAGER
,这意味着它将加载 doctor
及其所有 doctorSpecialties
到持久状态,并且这个原因 (2) 不会给你任何影响。
否则,如果fetch = FetchType.LAZY
,则只加载doctor
进入持久化状态,在(2)处删除成功
你的情况很相似,虽然 fetch = FetchType.LAZY
,但你通过使用 forEach
遍历子项并将 doctorSpecialties
加载到持久状态。这就是为什么你不能删除它们。
建议:在您的父实体中使用 orphanRemoval = true
@OneToMany(mappedBy = "doctor", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<DoctorSpecialties> dss;
只需简单地在您的方法中清除它的子项(在@Transactional 方法中)
doctor.getDoctorSpecialties().clear();
我有一个 OneToMany 关系(两个表是双向的)。 当我保存医生的专业时,它确实有效,但是当我删除任何专业并更新医生时,它不起作用。
医生
@Entity
@Table(name = "doctors")
public class Doctor implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer doctorId;
@Column(length = 20)
private String doctorName;
@Column(length = 9)
private String doctorPhoneNo;
@Column(length = 30)
private String doctorEmailAddress;
private String doctorProfileImage;
@Enumerated(EnumType.STRING)
private Status status;
@Column(length = 6)
private String doctorCmp;
@OneToMany(mappedBy = "doctor", cascade = CascadeType.ALL)
// @JsonIgnore
private Set<DoctorSpecialties> doctorSpecialties;
public Doctor() {
this.doctorSpecialties = new HashSet<>();
}
public Doctor(Integer id){
this();
this.doctorId = id;
}
// getters y setters
}
专业
@Entity
@Table(name = "specialties")
public class Specialty implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer specialtyId;
private String specialtyName;
@OneToMany(mappedBy = "specialty")
@JsonIgnore
private Set<DoctorSpecialties> doctorSpecialties;
public Specialty() {
}
public Specialty(Integer id) {
this.specialtyId = id;
}
// getters and setters
}
专科医生
@Entity
@Table(name = "doctor_specialties")
public class DoctorSpecialties implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "doctor_id")
private Doctor doctor;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "specialty_id")
private Specialty specialty;
@OneToMany
@JoinColumn(name = "doctor_specialties_id")
private Set<Appointment> appointments;
@OneToMany
@JoinColumn(name = "doctor_specialties_id")
private Set<DoctorSchedule> schedules;
public DoctorSpecialties(){
}
public DoctorSpecialties(Specialty specialty, Doctor doctor){
this.specialty = specialty;
this.doctor = doctor;
}
getters / setters
}
控制器
@PostMapping(value = "/saveSpecialties/{id}")
public String saveDoctorSpecialties(@RequestParam(required = false) String[] specialtiesId,
@PathVariable Integer id, RedirectAttributes message) {
if (id != null && id > 0) {
Doctor doctor = doctorService.findOne(id);
if (doctor != null) {
// It does not work
doctor.getDoctorSpecialties().forEach(ds -> doctorSpecialtiesService.delete(ds.getId()));
doctor.getDoctorSpecialties().clear();
if (specialtiesId != null) {
for (String specialtyId : specialtiesId) {
DoctorSpecialties ds = new DoctorSpecialties();
ds.setSpecialty(new Specialty(Integer.parseInt(specialtyId)));
ds.setDoctor(doctor);
doctor.getDoctorSpecialties()
.add(ds);
}
}
doctorService.update(doctor);
message.addFlashAttribute("success", "Specialties successfully saved.");
return "redirect:/doctors/profile/{id}/specialties";
}
}
// specialtiesId = new String[]{};
message.addFlashAttribute("error", "Doctor doesn't exists");
return "redirect:/doctors/list";
}
控制台:
2021-10-30 21:19:13.330 DEBUG 44504 --- [nio-8080-exec-7] org.hibernate.SQL : select doctor0_.doctor_id as doctor_i1_3_0_, doctor0_.doctor_cmp as doctor_c2_3_0_, doctor0_.doctor_email_address as doctor_e3_3_0_, doctor0_.doctor_name as doctor_n4_3_0_, doctor0_.doctor_phone_no as doctor_p5_3_0_, doctor0_.doctor_profile_image as doctor_p6_3_0_, doctor0_.status as status7_3_0_ from doctors doctor0_ where doctor0_.doctor_id=? 2021-10-30 21:19:13.339 DEBUG 44504 --- [nio-8080-exec-7] org.hibernate.SQL : select doctorspec0_.doctor_id as doctor_i2_2_0_, doctorspec0_.id as id1_2_0_, doctorspec0_.id as id1_2_1_, doctorspec0_.doctor_id as doctor_i2_2_1_, doctorspec0_.specialty_id as specialt3_2_1_ from doctor_specialties doctorspec0_ where doctorspec0_.doctor_id=? 2021-10-30 21:19:13.401 DEBUG 44504 --- [nio-8080-exec-8] org.hibernate.SQL : select doctor0_.doctor_id as doctor_i1_3_0_, doctor0_.doctor_cmp as doctor_c2_3_0_, doctor0_.doctor_email_address as doctor_e3_3_0_, doctor0_.doctor_name as doctor_n4_3_0_, doctor0_.doctor_phone_no as doctor_p5_3_0_, doctor0_.doctor_profile_image as doctor_p6_3_0_, doctor0_.status as status7_3_0_ from doctors doctor0_ where doctor0_.doctor_id=? 2021-10-30 21:19:13.404 DEBUG 44504 --- [nio-8080-exec-8] org.hibernate.SQL : select specialty0_.specialty_id as specialt1_7_0_, doctorspec1_.id as id1_2_1_, doctor2_.doctor_id as doctor_i1_3_2_, specialty0_.specialty_name as specialt2_7_0_, doctorspec1_.doctor_id as doctor_i2_2_1_, doctorspec1_.specialty_id as specialt3_2_1_, doctorspec1_.specialty_id as specialt3_2_0__, doctorspec1_.id as id1_2_0__, doctor2_.doctor_cmp as doctor_c2_3_2_, doctor2_.doctor_email_address as doctor_e3_3_2_, doctor2_.doctor_name as doctor_n4_3_2_, doctor2_.doctor_phone_no as doctor_p5_3_2_, doctor2_.doctor_profile_image as doctor_p6_3_2_, doctor2_.status as status7_3_2_ from specialties specialty0_ inner join doctor_specialties doctorspec1_ on specialty0_.specialty_id=doctorspec1_.specialty_id inner join doctors doctor2_ on doctorspec1_.doctor_id=doctor2_.doctor_id where doctor2_.doctor_id=? 2021-10-30 21:19:13.565 DEBUG 44504 --- [nio-8080-exec-4] org.hibernate.SQL : select specialty0_.specialty_id as specialt1_7_0_, doctorspec1_.id as id1_2_1_, doctor2_.doctor_id as doctor_i1_3_2_, specialty0_.specialty_name as specialt2_7_0_, doctorspec1_.doctor_id as doctor_i2_2_1_, doctorspec1_.specialty_id as specialt3_2_1_, doctorspec1_.specialty_id as specialt3_2_0__, doctorspec1_.id as id1_2_0__, doctor2_.doctor_cmp as doctor_c2_3_2_, doctor2_.doctor_email_address as doctor_e3_3_2_, doctor2_.doctor_name as doctor_n4_3_2_, doctor2_.doctor_phone_no as doctor_p5_3_2_, doctor2_.doctor_profile_image as doctor_p6_3_2_, doctor2_.status as status7_3_2_ from specialties specialty0_ inner join doctor_specialties doctorspec1_ on specialty0_.specialty_id=doctorspec1_.specialty_id inner join doctors doctor2_ on doctorspec1_.doctor_id=doctor2_.doctor_id where doctor2_.doctor_id=?
没有删除语句...
------------------------ 编辑 1 ----------------------
Doctor find = doctorRepository.findById(1).get();
DoctorSpecialties ds1 = new DoctorSpecialties();
ds1.setSpecialty(specialtyRepository.findById(1).get());
ds1.setDoctor(find);
DoctorSpecialties ds2 = new DoctorSpecialties();
ds2.setSpecialty(specialtyRepository.findById(2).get());
ds2.setDoctor(find);
find.getDoctorSpecialties().add(ds1);
find.getDoctorSpecialties().add(ds2);
doctorRepository.save(find);
我做了一些测试,我不能完全理解。我做到了,它只在我实际添加两个对象时保存一次。
insert into doctor_specialties (id, doctor_id, specialty_id) values (null, ?, ?)
------------------------ 编辑 2 ----------------------
DoctorSpecialties(修改构造函数)
@Entity
@Table(name = "doctor_specialties")
public class DoctorSpecialties implements Serializable {
public DoctorSpecialties(Integer specialtyId, Doctor doctor) {
this.specialty = new Specialty(specialtyId);
this.doctor = doctor;
}
}
控制器
@PostMapping(value = "/saveSpecialties/{id}")
public String saveDoctorSpecialties(@RequestParam(required = false) String[] specialtiesId,
@PathVariable Integer id, RedirectAttributes message) {
if (id != null && id > 0) {
doctorService.saveDelete(id);
Doctor doctor = doctorService.findOne(id);
if (specialtiesId != null && specialtiesId.length > 0) {
for(String idSpecialty : specialtiesId){
doctorSpecialtiesService.save(new DoctorSpecialties(Integer.parseInt(idSpecialty), doctor));
}
}
message.addFlashAttribute("success", "Specialties successfully saved.");
return "redirect:/doctors/profile/{id}/specialties";
}
message.addFlashAttribute("error", "Doctor doesn't exists");
return "redirect:/doctors/list";
}
服务
@Override
@Transactional
public void saveDelete(Integer doctorId) {
Doctor doctor = this.doctorRepository
.findById(doctorId).get();
doctor.getDoctorSpecialties().clear();
}
控制台:
select doctor0_.doctor_id as doctor_i1_3_0_, doctor0_.doctor_cmp as doctor_c2_3_0_, doctor0_.doctor_email_address as doctor_e3_3_0_, doctor0_.doctor_name as doctor_n4_3_0_, doctor0_.doctor_phone_no as doctor_p5_3_0_, doctor0_.doctor_profile_image as doctor_p6_3_0_, doctor0_.status as status7_3_0_ from doctors doctor0_ where doctor0_.doctor_id=? select doctorspec0_.doctor_id as doctor_i2_2_0_, doctorspec0_.id as id1_2_0_, doctorspec0_.id as id1_2_1_, doctorspec0_.doctor_id as doctor_i2_2_1_, doctorspec0_.specialty_id as specialt3_2_1_ from doctor_specialties doctorspec0_ where doctorspec0_.doctor_id=?
update appointments set doctor_specialties_id=null where doctor_specialties_id=?
update doctor_schedules set doctor_specialties_id=null where doctor_specialties_id=?
delete from doctor_specialties where id=?
有mappedBy
的一方为拥有方
This means something like: "modifications on this side of the relation are already Mapped By the other side of the relation , so no need to track it here separately in an extra table."
查看此 url 了解更多信息 What is the "owning side" in an ORM mapping?
对于事务中的一对多关系,一旦你得到父项(Doctor
)并遍历其子项(DoctorSpecialties
)[换句话说,一旦您将整个父项及其子项加载到 Persistent 状态],您将无法通过其 repository
.
DoctorSpecialties
您可以尝试类似下面的示例来查看它:
@Transactional
public void removeLine(Long doctorId, Long specId) {
Doctor doctor = this.doctorRepository // (1)
.findById(doctorId)
.orElseThrow(IllegalArgumentException::new);
this.doctorSpecialtiesRepository.deleteById(specId); // (2)
}
在 (1) 处,我们将 doctor
加载到持久状态。所以在这里,如果 fetch = FetchType.EAGER
,这意味着它将加载 doctor
及其所有 doctorSpecialties
到持久状态,并且这个原因 (2) 不会给你任何影响。
否则,如果fetch = FetchType.LAZY
,则只加载doctor
进入持久化状态,在(2)处删除成功
你的情况很相似,虽然 fetch = FetchType.LAZY
,但你通过使用 forEach
遍历子项并将 doctorSpecialties
加载到持久状态。这就是为什么你不能删除它们。
建议:在您的父实体中使用 orphanRemoval = true
@OneToMany(mappedBy = "doctor", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<DoctorSpecialties> dss;
只需简单地在您的方法中清除它的子项(在@Transactional 方法中)
doctor.getDoctorSpecialties().clear();