My first Springboot JPA project and I am trying to understand how to retrieve a Parent with its list of children. In my case a Patient (One) can have (Many) DoctorsVisits.
There are many examples of how to setup the relationships with the OneToMany annotations, but how to retrieve a Patient with a list of Doctors Visits is what I am trying to acheive.
The project is structured with a Patient Service, Patient Repository, Patient Service Impl.
The Patient Entity looks like:
Patient
public class Patient {
@Id @GeneratedValue(strategy = IDENTITY)
private Long id;
@OneToMany (targetEntity = DoctorVisit.class, cascade = CascadeType.ALL)
@JoinColumn(name = "patient_id", referencedColumnName = "id")
private List<DoctorVisit> doctorVisit;
}
Doctor Visit
public class DoctorVisit {
@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
}
Patient Service
public interface PatientService {
Patient create(Patient patient);
Collection<Patient> list();
Patient get(Long id);
Patient update(Patient patient);
Boolean delete(Long id);
}
Patient Repo
public interface PatientRepository extends JpaRepository<Patient, Long> {
}
And finally Patient Service Implementation:
PatientServiceImpl
@RequiredArgsConstructor
@Service
@Transactional
@Slf4j
public class PatientServiceImpl implements PatientService {
private final PatientRepository patientRepository;
@Override
public Patient create(Patient patient) {
log.info("Saving new bird: {}", patient.getName() );
return patientRepository.save(patient);
}
@Override
public Collection<Patient> list() {
log.info("Finding all patientss");
return patientRepository.findAll();
}
@Override
public Patient get(Long id) {
return patientRepository.findById(id).get();
}
@Override
public Patient update(Patient patient) {
return null;
}
@Override
public Boolean delete(Long id) {
return null;
}
}
There is a almost identical set of classes for DoctorVisit.
It seems that the way a parent/child is going to be used determines the best way to set it up (unidirectional or bidirectional) and whether the relationship is really OneToMany or better described as OneToFew.
In this case the useage will be:
- Retrieving a Patient with a list of visits;
- Retrieving a Visit with the patients name;
In most cases the Patient visit relationship should not exceed 1 to 30.
The question:
How do I retrieve a Patient with a List of DoctorVisits ?
Does JPA 'magic' derive the query or do I need to write a query with @query?
Where do I write this query? (I would presume its on a service impl?)
CodePudding user response:
add this in your patient class:
@OneToMany (targetEntity = DoctorVisit.class, cascade = CascadeType.ALL,orphanRemoval=true, fetch = FetchType.EAGER)
The key here is in the fetchType which can be eaguer or lazy:
- To load it together with the rest of the fields (i.e. eagerly), or
- To load it on-demand (i.e. lazily) when you call the university's getStudents() method.
I leave a link here in case you want more information:https://www.baeldung.com/hibernate-lazy-eager-loading
Now every time you retrieve a patient entity you will be able to return the list of doctors of this with:
patient.getDoctorVisit()
Every time you obtain a patient, all the doctors that this patient has will be loaded in this way, so you will not have to make a query in the different service. Finally, keep in mind that this implies more load in the database (since they will load more in the bd)
CodePudding user response:
Using the excellent post from Vlad here I have decided to map the entities with just the @ManyToOne on DoctorVisit (Bidirectional).
So the ORM on the classes looks like:
DoctorVisit
public class DoctorVisit {
@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY, optional=false)
private Patient patient;
// Getters and Setters
}
Patient
public class Patient {
@Id @GeneratedValue(strategy = IDENTITY)
private Long id;
// Getters and Setters
}
Note the optional=false which tells hibernate that there must be a related record for patient - otherwise any loading of a DoctorVisit would EAGER load a Patient despite my LAZY fetching.
Now I hear you ask how would I get the child DoctorVisits associate with a patient?
Just use JPQL
on DoctorVisit Repo class:
@Query("select dv from DoctorVisit where dv.patient.id = :patientId")
Collection<DoctorVisit> findDoctorVisitByPatientId(@Param("patientId") Long id);
There may be a derived way to get this with the JPA magic, but I don't know. The JPQL works.
Then call this query from the DoctorVisitServiceImpl
class to use it.