How to load a full graph of 2 entities that are in relationship @OneToMany each other with a Join Table

239 Views Asked by At

I'm using Spring Boot and Spring Data and I have a problem when trying to load entities using JPA and EntityGraph. I have a Patient and Insurance entities. Each Patient can have many Insurances and each Insurance can be assigned to many patients. I decided to use a Join Table PatientInsurance because I need to store extra fields like 'active', and also the relation code (a Patient can be a Member, Spouse, or Child for that specific insurance).

Using Spring Data repositories I annotated the method to find a patient, with an EntityGraph, to have ready the list of PatientInsurances (and Insurances) for that patient in one query.

This is the code (I removed the non-necessary parts in the scope)

Patient class

@Entity
@Table(name = "patient")
public class Patient {

    @NotNull
    @NotEmpty
    @Column(length = 60, nullable = false)
    private String patientFirstName;

    @NotNull
    @NotEmpty
    @Column(length = 60, nullable = false)
    private String patientLastName;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "patient", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
    List<PatientInsurance> patientsInsurances = new ArrayList<>();

    public void addPatientInsurance(PatientInsurance patientIns) {
        if (!patientsInsurances.contains(patientIns)) {
            patientsInsurances.add(patientIns);
        }
    }

    //other properties...

Insurance class


@Entity
@Table(name = "insurance")
public class Insurance {

    @Column(name = "policy_id", length = 20)
    private String policyId;

    @OneToMany(mappedBy = "insurance", fetch = FetchType.LAZY,cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
    private List<PatientInsurance> patientsInsurances = new ArrayList<PatientInsurance>();

    public void addPatientInsurance(PatientInsurance patientIns) {
        if (!patientsInsurances.contains(patientIns)) {
            patientsInsurances.add(patientIns);
        }
    }
    
    //other properties

Entity for the join table between patient and insurance (needed a join table for extra field in this entity like active and relCode

@Entity
@IdClass(PatientInsurance.PatientInsurancePK.class)
@Table(name = "patient_insurance")
public class PatientInsurance implements Serializable {

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "patient_id")
    private Patient patient;

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "insurance_id")
    private Insurance insurance;

    @Column(name = "active")
    private boolean active;

    @Column(length = 1)
    private String relCode;


    public PatientInsurance() {
        insurance = new Insurance();
        patient = new Patient();
    }

    public PatientInsurance(Patient p, Insurance i, boolean active, String relCode) {
        this.patient = p;
        this.insurance = i;
        this.active = active;
        this.relCode = relCode;
        p.addPatientInsurance(this);
        i.addPatientInsurance(this);
    }

    public Patient getPatient() {
        return patient;
    }

    public Insurance getInsurance() {
        return insurance;
    }

    public void setInsurance(Insurance insurance) {
        this.insurance = insurance;
        insurance.addPatientInsurance(this);
    }


    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }

    public void setPatient(Patient patient) {
        this.patient = patient;
        patient.addPatientInsurance(this);
    }

    public String getRelCode() {
        return relCode;
    }

    public void setRelCode(String relCode) {
        this.relCode = relCode;
    }


    static public class PatientInsurancePK implements Serializable {
        protected Patient patient;
        protected Insurance insurance;

        public PatientInsurancePK() {
        }

        public PatientInsurancePK(Patient patient, Insurance insurance) {
            this.patient = patient;
            this.insurance = insurance;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof PatientInsurancePK)) return false;

            PatientInsurancePK that = (PatientInsurancePK) o;

            if (!patient.equals(that.patient)) return false;
            return insurance.equals(that.insurance);
        }

        @Override
        public int hashCode() {
            int result = (patient != null) ? patient.hashCode() : 0;
            result = 31 * result + ((insurance != null) ? insurance.hashCode() : 0);
            return result;
        }
    }
}

Implementation of the PatientService

@Transactional
@Service("patientService")
public class PatientServiceImpl implements PatientService {

    @Autowired
    PatientRepository patientRepository;

    @Override
    public Optional<Patient> findByIdFull(Long id) {
        Optional<Patient> patient = patientRepository.findById(id);      
        return patient;
    }

    //other methods...

Patient Repository

public interface PatientRepository extends JpaRepository<Patient, Long> {

    @EntityGraph(
            attributePaths = {
                    "patientsInsurances",
                    "patientsInsurances.patient",
                    "patientsInsurances.insurance"},
            type = EntityGraph.EntityGraphType.LOAD)
    Optional<Patient> findById(Long id);

A snippet of code that calls the method in PatientService

Optional<Patient> patientOptional = patientService.findByIdFull(p.getId());
if (patientOptional.isPresent()) {
     Patient patient1 = patientOptional.get();
     
     List<PatientInsurance> patientInsurances = patient1.getPatientInsurances();
     PatientInsurances patientInsurance = patientInsurances.get(0);
     Patient patient2 = patientInsurance.getPatient(); //and this is same istance of patient1, it's ok
     Insurance insurance = patientInsurance.getInsurance();
     //here is the problem!!!
     insurance.getPatientInsurances(); 
     //Unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' exception.       

So the problem seems that when I go inside the patient side, I can loop into his Insurances without problems, but when I try to do the same starting from the Insurance instance, I cannot loop into its patients cause they are lazily loaded. So how to make jpa download the full graph in the correct way?

0

There are 0 best solutions below