spring JPA中的组合主键和外键

ukqbszuj  于 12个月前  发布在  Spring
关注(0)|答案(1)|浏览(105)

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

package com.example.doctorkom.Entities;

import jakarta.persistence.*;
import lombok.*;

import java.sql.Date;

@Entity
@Table(name = "MedicalNote")
@IdClass(MedicalNoteId.class)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MedicalNote {
    @Column(name = "Diagnosis")
    private String diagnosis;

    @Column(name = "Investigations")
    private String investigations;

    @Column(name = "Prescription")
    private String prescription;

    @Id
    @Column(name = "Date")
    private Date date;

    @Id
    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
    @JoinColumn(name = "PatientId")
    private Patient patient;

    @Id
    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
    @JoinColumn(name = "DoctorId")
    private Doctor doctor;
}

字符串
MedicalNoteId

package com.example.doctorkom.Entities;

import lombok.*;

import java.io.Serializable;
import java.sql.Date;

@Getter
@Setter
@ToString
@EqualsAndHashCode
@NoArgsConstructor
@AllArgsConstructor
public class MedicalNoteId implements Serializable {
    private Patient patient;
    private Doctor doctor;
    private Date date;
}


医生

package com.example.doctorkom.Entities;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.Hibernate;
import org.hibernate.proxy.HibernateProxy;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Entity
@Table(name = "Doctor")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Doctor {
    @Id
    @Column(name = "UserId")
    private Integer id;

    @Column(name = "Title")
    @Enumerated(EnumType.STRING)
    private DoctorTitle title;

    @Column(name = "Specialty")
    @Enumerated(EnumType.STRING)
    private DoctorSpecialty specialty;

    @MapsId
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "UserId")
    private SystemUser systemUser;

    @OneToMany(mappedBy = "doctor", cascade = CascadeType.ALL, orphanRemoval = true)
    @ToString.Exclude
    private List<MedicalNote> medicalNotes;

    @OneToMany(mappedBy = "doctor", cascade = CascadeType.ALL, orphanRemoval = true)
    @ToString.Exclude
    private List<TimeSlot> timeSlots;

    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.DETACH, CascadeType.REFRESH})
    @JoinTable(
            name = "WorksFor",
            joinColumns = @JoinColumn(name = "DoctorId"),
            inverseJoinColumns = @JoinColumn(name = "ClinicId")
    )
    @ToString.Exclude
    private List<Clinic> clinics;

    public void addMedicalNote (MedicalNote medicalNote) {
        if (medicalNotes == null) {
            medicalNotes = new ArrayList<>();
        }

        medicalNotes.add(medicalNote);
        medicalNote.setDoctor(this);
    }

    void deleteMedicalNote (MedicalNote medicalNote) {
        if (medicalNotes != null) {
            medicalNotes.remove(medicalNote);
            medicalNote.setDoctor(null);
        }
    }

    public void addTimeSlot (TimeSlot timeSlot) {
        if (timeSlots == null) {
            timeSlots = new ArrayList<>();
        }

        timeSlots.add(timeSlot);
        timeSlot.setDoctor(this);
    }

    void deleteTimeSlot (TimeSlot timeSlot) {
        if (timeSlots != null) {
            timeSlots.remove(timeSlot);
            timeSlot.setDoctor(null);
        }
    }

    public void addClinic (Clinic clinic) {
        if (clinics == null) {
            clinics = new ArrayList<>();
        }

        clinics.add(clinic);
        clinic.getDoctors().add(this);
    }

    void deleteClinic (Clinic clinic) {
        if (clinics != null) {
            clinics.remove(clinic);
            clinic.getDoctors().remove(this);
        }
    }
}


患者

package com.example.doctorkom.Entities;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.Hibernate;
import org.hibernate.proxy.HibernateProxy;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Entity
@Table(name = "Patient")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Patient {
    @Id
    @Column(name = "UserId")
    private Integer id;

    @Column(name = "Occupation")
    private String occupation;

    @Column(name = "MaritalStatus")
    private String maritalStatus;

    @Column(name = "Insurance")
    private String insurance;

    @MapsId
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "UserId")
    private SystemUser systemUser;

    @OneToMany(mappedBy = "patient", cascade = CascadeType.ALL)
    @ToString.Exclude
    private List<MedicalNote> medicalNotes;

    public void addMedicalNote (MedicalNote medicalNote) {
        if (medicalNotes == null) {
            medicalNotes = new ArrayList<>();
        }

        medicalNotes.add(medicalNote);
        medicalNote.setPatient(this);
    }
}


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

@Test
    void whenDeleteByDoctorId_thenDeleteMedicalNote() {
        // Given
        Account patientAccount = Account.builder().
                email("[email protected]").
                username("JohnSmith1").
                password("JohnyJohny123").
                role(Role.PATIENT).
                build();

        SystemUser patientSystemUser = SystemUser.builder().
                firstName("John").
                lastName("Smith").
                birthdate(Date.valueOf("1985-11-14")).
                gender(Gender.MALE).
                address("221B Baker Street").
                mobilePhone("(555) 555-5555").
                landlinePhone("(555) 123-4567").
                account(patientAccount).
                build();

        Patient patient = Patient.builder().
                occupation("Engineer").
                maritalStatus("Single").
                insurance("BOBA").
                systemUser(patientSystemUser).
                build();

        patientRepository.save(patient);

        Account doctorAccount = Account.builder().
                email("[email protected]").
                username("DrSmith1").
                password("Medical123").
                role(Role.DOCTOR).
                build();

        SystemUser doctorSystemUser = SystemUser.builder().
                firstName("Smith").
                lastName("Health").
                birthdate(Date.valueOf("1976-10-30")).
                gender(Gender.MALE).
                address("123 Main Street").
                mobilePhone("(666) 666-6666").
                landlinePhone("(555) 765-4321").
                account(doctorAccount).
                build();

        Doctor doctor = Doctor.builder().
                title(DoctorTitle.PROFESSOR).
                specialty(DoctorSpecialty.ONCOLOGIST).
                systemUser(doctorSystemUser).
                build();

        doctorRepository.save(doctor);

        MedicalNote medicalNote = MedicalNote.builder().
                date(Date.valueOf("2023-04-01")).
                patient(patient).
                doctor(doctor).
                build();

        medicalNoteRepository.save(medicalNote);

        patient.addMedicalNote(medicalNote);
        doctor.addMedicalNote(medicalNote);

        patientRepository.save(patient);
        doctorRepository.save(doctor);

        // When
        medicalNoteRepository.deleteByDoctorId(doctor.getId());

        // Then
        assertTrue(medicalNoteRepository.findByDoctorId(doctor.getId()).isEmpty());
    }


抛出当前异常

Hibernate: insert into Account (Email,Enabled,Password,Role,Username,Id) values (?,?,?,?,?,default)
Hibernate: insert into SystemUser (Address,Birthdate,FirstName,Gender,LandlinePhone,LastName,MobilePhone,AccountId) values (?,?,?,?,?,?,?,?)
Hibernate: insert into Patient (Insurance,MaritalStatus,Occupation,UserId) values (?,?,?,?)
Hibernate: insert into Account (Email,Enabled,Password,Role,Username,Id) values (?,?,?,?,?,default)
Hibernate: select 
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 ((?,?,?))
Hibernate: insert into SystemUser (Address,Birthdate,FirstName,Gender,LandlinePhone,LastName,MobilePhone,AccountId) values (?,?,?,?,?,?,?,?)
Hibernate: insert into Doctor (Specialty,Title,UserId) values (?,?,?)
Hibernate: insert into MedicalNote (Diagnosis,Investigations,Prescription,Date,DoctorId,PatientId) values (?,?,?,?,?,?)
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=?

org.opentest4j.AssertionFailedError: 
Expected :true
Actual   :false

fcg9iug3

fcg9iug31#

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

相关问题