我想这可能像一个新手问题,但我仍然想知道一些答案。
假设有实体:医院和医生( 多对多 )。假设在我的控制者班级中,我必须先找所有现有的医生和医院,然后再在特定的医院雇用一名医生
@Controller public class HospitalController { ... @RequestMapping("/hireDoctor") public String (HttpServletRequest request, Model model) { List<Hospital> hospitals = hospitalService.findAllHospitals(); List<Doctor> doctors = doctorService.findAllDoctors(); //some logic, in the end we choose just one doctor and one hospital Doctor doctor = doctors.get(0); Hospital hospital = hospitals.get(0); hospitalService.hireDoctor(hospital, doctor); } ... @Service public class HospitalService { .. @Transactional public void hireDoctor(Hospital hospital, Doctor doctor) { //ideally List<Doctor> doctors = hospital.getDoctors(); doctors.add(doctor); this.em.merge(hospital); } .. }
它当然不起作用,因为-据我了解- 我已经在控制器中获取了所有医生和医院,然后在hireDoctor方法中,我们通过常规Java对象(不在会话中)打开了事务处理。
我知道,我可以再次获取带有特定ID的医院,以及具有特定ID的Doctor,然后将其保存
public void hireDoctor(Hospital hospital, Doctor doctor) { Hospital h = hospitalRepo.getHospitalById(hospital.getId()); Doctor d = hospitalRepo.getDoctorById(doctor.getId()); List<Doctor> doctors = h.getDoctors(); doctors.add(d); }
但这看起来只是垃圾。
那么-这样的更新看起来如何最有效?
有一个很好的方法可以做到这一点。它依赖于结合使用Hibernate代理和提取与一个单独实体的多对多关系,例如:
@Entity public class HospitalToDoctor implements Serializable { @Id @ManyToOne private Hospital hospital; @Id @ManyToOne private Doctor doctor; } @Entity public class Hospital { @OneToMany(mappedBy = "hospital") private Collection<HospitalToDoctor> doctors; } @Entity public class Doctor { @OneToMany(mappedBy = "doctor") private Collection<HospitalToDoctor> hospitals; }
现在,仅使用一个insert语句来关联a Doctor和a Hospital,而无需进行任何其他数据库往返操作:
Doctor
Hospital
HospitalToDoctor hospitalToDoctor = new HospitalToDoctor(); hospitalToDoctor.setHospital(entityManager.getReference(Hospital.class, hospitalId)); hospitalToDoctor.setDoctor(entityManager.getReference(Doctor.class, doctorId)); entityManager.persist(hospitalToDoctor);
这里的关键点是使用EntityManager.getReference:
获取一个实例,其状态可能会延迟获取。
Hibernate只会根据提供的ID创建代理,而无需从数据库中获取实体。
在其他用例中,您可以封装HospitalToDoctor实体,以便仍然使用多对多关联。例如,您可以添加Hopsital如下内容:
HospitalToDoctor
Hopsital
public Collection<Doctor> getDoctors() { Collection<Doctor> result = new ArrayList<>(doctors.size()); for (HospitalToDoctor hospitalToDoctor : doctors) { result.add(hospitalToDoctor.getDoctor()); } return result; }
引入的另一个好处HospitalToDoctor是,如果需要,您可以轻松地在其中存储其他属性(例如,当医生开始在医院工作时等)。
但是,如果您仍然不想引入单独的实体,而是想要使用干净的多对多Hibernate,仍然可以从代理中受益。您可以将Doctor代理添加到已加载的代理中Hospital(反之亦然)。您可能还希望查看Hibernate额外的惰性集合,以避免doctors在将a添加Doctor到a时Hospital反之亦然(反之亦然)(我想这是您问题的主要关注点),以避免加载该集合。
doctors