spring JPA中的组合主键和外键

ukqbszuj  于 2024-01-05  发布在  Spring
关注(0)|答案(1)|浏览(150)

我有一个引用Doctor和Patient id的MedicalNote,并且有一个date属性,所有这些都构成了MedicalNote的主键。MedicalNote和Doctor和Patient都有一对多的双向关系。我想确定这是否是在Hibernate中实现的正确方法。
医疗笔记

  1. package com.example.doctorkom.Entities;
  2. import jakarta.persistence.*;
  3. import lombok.*;
  4. import java.sql.Date;
  5. @Entity
  6. @Table(name = "MedicalNote")
  7. @IdClass(MedicalNoteId.class)
  8. @Data
  9. @Builder
  10. @NoArgsConstructor
  11. @AllArgsConstructor
  12. public class MedicalNote {
  13. @Column(name = "Diagnosis")
  14. private String diagnosis;
  15. @Column(name = "Investigations")
  16. private String investigations;
  17. @Column(name = "Prescription")
  18. private String prescription;
  19. @Id
  20. @Column(name = "Date")
  21. private Date date;
  22. @Id
  23. @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
  24. @JoinColumn(name = "PatientId")
  25. private Patient patient;
  26. @Id
  27. @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
  28. @JoinColumn(name = "DoctorId")
  29. private Doctor doctor;
  30. }

字符串
MedicalNoteId

  1. package com.example.doctorkom.Entities;
  2. import lombok.*;
  3. import java.io.Serializable;
  4. import java.sql.Date;
  5. @Getter
  6. @Setter
  7. @ToString
  8. @EqualsAndHashCode
  9. @NoArgsConstructor
  10. @AllArgsConstructor
  11. public class MedicalNoteId implements Serializable {
  12. private Patient patient;
  13. private Doctor doctor;
  14. private Date date;
  15. }


医生

  1. package com.example.doctorkom.Entities;
  2. import jakarta.persistence.*;
  3. import lombok.*;
  4. import org.hibernate.Hibernate;
  5. import org.hibernate.proxy.HibernateProxy;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import java.util.Objects;
  9. @Entity
  10. @Table(name = "Doctor")
  11. @Data
  12. @Builder
  13. @NoArgsConstructor
  14. @AllArgsConstructor
  15. public class Doctor {
  16. @Id
  17. @Column(name = "UserId")
  18. private Integer id;
  19. @Column(name = "Title")
  20. @Enumerated(EnumType.STRING)
  21. private DoctorTitle title;
  22. @Column(name = "Specialty")
  23. @Enumerated(EnumType.STRING)
  24. private DoctorSpecialty specialty;
  25. @MapsId
  26. @OneToOne(cascade = CascadeType.ALL)
  27. @JoinColumn(name = "UserId")
  28. private SystemUser systemUser;
  29. @OneToMany(mappedBy = "doctor", cascade = CascadeType.ALL, orphanRemoval = true)
  30. @ToString.Exclude
  31. private List<MedicalNote> medicalNotes;
  32. @OneToMany(mappedBy = "doctor", cascade = CascadeType.ALL, orphanRemoval = true)
  33. @ToString.Exclude
  34. private List<TimeSlot> timeSlots;
  35. @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
  36. @JoinTable(
  37. name = "WorksFor",
  38. joinColumns = @JoinColumn(name = "DoctorId"),
  39. inverseJoinColumns = @JoinColumn(name = "ClinicId")
  40. )
  41. @ToString.Exclude
  42. private List<Clinic> clinics;
  43. public void addMedicalNote (MedicalNote medicalNote) {
  44. if (medicalNotes == null) {
  45. medicalNotes = new ArrayList<>();
  46. }
  47. medicalNotes.add(medicalNote);
  48. medicalNote.setDoctor(this);
  49. }
  50. void deleteMedicalNote (MedicalNote medicalNote) {
  51. if (medicalNotes != null) {
  52. medicalNotes.remove(medicalNote);
  53. medicalNote.setDoctor(null);
  54. }
  55. }
  56. public void addTimeSlot (TimeSlot timeSlot) {
  57. if (timeSlots == null) {
  58. timeSlots = new ArrayList<>();
  59. }
  60. timeSlots.add(timeSlot);
  61. timeSlot.setDoctor(this);
  62. }
  63. void deleteTimeSlot (TimeSlot timeSlot) {
  64. if (timeSlots != null) {
  65. timeSlots.remove(timeSlot);
  66. timeSlot.setDoctor(null);
  67. }
  68. }
  69. public void addClinic (Clinic clinic) {
  70. if (clinics == null) {
  71. clinics = new ArrayList<>();
  72. }
  73. clinics.add(clinic);
  74. clinic.getDoctors().add(this);
  75. }
  76. void deleteClinic (Clinic clinic) {
  77. if (clinics != null) {
  78. clinics.remove(clinic);
  79. clinic.getDoctors().remove(this);
  80. }
  81. }
  82. }


患者

  1. package com.example.doctorkom.Entities;
  2. import jakarta.persistence.*;
  3. import lombok.*;
  4. import org.hibernate.Hibernate;
  5. import org.hibernate.proxy.HibernateProxy;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import java.util.Objects;
  9. @Entity
  10. @Table(name = "Patient")
  11. @Data
  12. @Builder
  13. @NoArgsConstructor
  14. @AllArgsConstructor
  15. public class Patient {
  16. @Id
  17. @Column(name = "UserId")
  18. private Integer id;
  19. @Column(name = "Occupation")
  20. private String occupation;
  21. @Column(name = "MaritalStatus")
  22. private String maritalStatus;
  23. @Column(name = "Insurance")
  24. private String insurance;
  25. @MapsId
  26. @OneToOne(cascade = CascadeType.ALL)
  27. @JoinColumn(name = "UserId")
  28. private SystemUser systemUser;
  29. @OneToMany(mappedBy = "patient", cascade = CascadeType.ALL)
  30. @ToString.Exclude
  31. private List<MedicalNote> medicalNotes;
  32. public void addMedicalNote (MedicalNote medicalNote) {
  33. if (medicalNotes == null) {
  34. medicalNotes = new ArrayList<>();
  35. }
  36. medicalNotes.add(medicalNote);
  37. medicalNote.setPatient(this);
  38. }
  39. }


这种方法在用H2测试时会遇到一些麻烦,

  1. @Test
  2. void whenDeleteByDoctorId_thenDeleteMedicalNote() {
  3. // Given
  4. Account patientAccount = Account.builder().
  5. email("[email protected]").
  6. username("JohnSmith1").
  7. password("JohnyJohny123").
  8. role(Role.PATIENT).
  9. build();
  10. SystemUser patientSystemUser = SystemUser.builder().
  11. firstName("John").
  12. lastName("Smith").
  13. birthdate(Date.valueOf("1985-11-14")).
  14. gender(Gender.MALE).
  15. address("221B Baker Street").
  16. mobilePhone("(555) 555-5555").
  17. landlinePhone("(555) 123-4567").
  18. account(patientAccount).
  19. build();
  20. Patient patient = Patient.builder().
  21. occupation("Engineer").
  22. maritalStatus("Single").
  23. insurance("BOBA").
  24. systemUser(patientSystemUser).
  25. build();
  26. patientRepository.save(patient);
  27. Account doctorAccount = Account.builder().
  28. email("[email protected]").
  29. username("DrSmith1").
  30. password("Medical123").
  31. role(Role.DOCTOR).
  32. build();
  33. SystemUser doctorSystemUser = SystemUser.builder().
  34. firstName("Smith").
  35. lastName("Health").
  36. birthdate(Date.valueOf("1976-10-30")).
  37. gender(Gender.MALE).
  38. address("123 Main Street").
  39. mobilePhone("(666) 666-6666").
  40. landlinePhone("(555) 765-4321").
  41. account(doctorAccount).
  42. build();
  43. Doctor doctor = Doctor.builder().
  44. title(DoctorTitle.PROFESSOR).
  45. specialty(DoctorSpecialty.ONCOLOGIST).
  46. systemUser(doctorSystemUser).
  47. build();
  48. doctorRepository.save(doctor);
  49. MedicalNote medicalNote = MedicalNote.builder().
  50. date(Date.valueOf("2023-04-01")).
  51. patient(patient).
  52. doctor(doctor).
  53. build();
  54. medicalNoteRepository.save(medicalNote);
  55. patient.addMedicalNote(medicalNote);
  56. doctor.addMedicalNote(medicalNote);
  57. patientRepository.save(patient);
  58. doctorRepository.save(doctor);
  59. // When
  60. medicalNoteRepository.deleteByDoctorId(doctor.getId());
  61. // Then
  62. assertTrue(medicalNoteRepository.findByDoctorId(doctor.getId()).isEmpty());
  63. }


抛出当前异常

  1. Hibernate: insert into Account (Email,Enabled,Password,Role,Username,Id) values (?,?,?,?,?,default)
  2. Hibernate: insert into SystemUser (Address,Birthdate,FirstName,Gender,LandlinePhone,LastName,MobilePhone,AccountId) values (?,?,?,?,?,?,?,?)
  3. Hibernate: insert into Patient (Insurance,MaritalStatus,Occupation,UserId) values (?,?,?,?)
  4. Hibernate: insert into Account (Email,Enabled,Password,Role,Username,Id) values (?,?,?,?,?,default)
  5. Hibernate: select
  6. m1_0.Date,m1_0.DoctorId,d1_0.UserId,d1_0.Specialty,s1_0.AccountId,a1_0.Id,a1_0.Email,a1_0.Enabled,a1_0.Password,a1_0.Role,a1_0.Username,s1_0.Address,s1_0.Birthdate,s1_0.FirstName,s1_0.Gender,s1_0.LandlinePhone,s1_0.LastName,s1_0.MobilePhone,d1_0.Title,c1_0.DoctorId,c1_1.ClinicId,c1_1.Address,a2_0.AccountId,a3_0.Id,a3_0.Email,a3_0.Enabled,a3_0.Password,a3_0.Role,a3_0.Username,c1_1.Email,c1_1.LandlinePhone,c1_1.MobilePhone,c1_1.Name,m1_0.PatientId,p1_0.UserId,p1_0.Insurance,p1_0.MaritalStatus,p1_0.Occupation,s2_0.AccountId,a4_0.Id,a4_0.Email,a4_0.Enabled,a4_0.Password,a4_0.Role,a4_0.Username,s2_0.Address,s2_0.Birthdate,s2_0.FirstName,s2_0.Gender,s2_0.LandlinePhone,s2_0.LastName,s2_0.MobilePhone,m1_0.Diagnosis,m1_0.Investigations,m1_0.Prescription from MedicalNote m1_0 join Doctor d1_0 on d1_0.UserId=m1_0.DoctorId left join SystemUser s1_0 on s1_0.AccountId=d1_0.UserId left join Account a1_0 on a1_0.Id=s1_0.AccountId left join (WorksFor c1_0 join Clinic c1_1 on c1_1.ClinicId=c1_0.ClinicId) on d1_0.UserId=c1_0.DoctorId left join ClinicAdmin a2_0 on a2_0.AccountId=c1_1.ClinicId left join Account a3_0 on a3_0.Id=a2_0.AccountId join Patient p1_0 on p1_0.UserId=m1_0.PatientId left join SystemUser s2_0 on s2_0.AccountId=p1_0.UserId left join Account a4_0 on a4_0.Id=s2_0.AccountId where (m1_0.Date,m1_0.DoctorId,m1_0.PatientId) in ((?,?,?))
  7. Hibernate: insert into SystemUser (Address,Birthdate,FirstName,Gender,LandlinePhone,LastName,MobilePhone,AccountId) values (?,?,?,?,?,?,?,?)
  8. Hibernate: insert into Doctor (Specialty,Title,UserId) values (?,?,?)
  9. Hibernate: insert into MedicalNote (Diagnosis,Investigations,Prescription,Date,DoctorId,PatientId) values (?,?,?,?,?,?)
  10. Hibernate: select m1_0.Date,m1_0.DoctorId,m1_0.PatientId,m1_0.Diagnosis,m1_0.Investigations,m1_0.Prescription from MedicalNote m1_0 where m1_0.DoctorId=?
  11. org.opentest4j.AssertionFailedError:
  12. Expected :true
  13. Actual :false

fcg9iug3

fcg9iug31#

据我所知,问题是您的测试在一个事务中运行,并且您为ParentDoctormedicalNotes字段设置了cascade = CascadeType.ALL。因此deleteByDoctorId标记了MedicalNote实体以进行删除,但在事务结束时,Hibernate再次从doctor或parent传播持久状态。
如果您删除cascade = CascadeType.ALL或在“When”和“Then”的单独事务中运行“Given”测试块-测试将正确完成。

相关问题