【问题标题】:Efficent updating entity in HibernateHibernate 中的高效更新实体
【发布时间】:2015-11-07 02:57:56
【问题描述】:

我想这可能是一个新手问题,但我仍然想知道一些答案。

假设有实体:医院和医生(多对多)。假设在我的控制器类中,我必须获取所有现有的医生和医院,然后在特定医院雇用一名医生

@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);
    }
  ..
}

这当然行不通,因为 - 据我了解 - 我已经在我的控制器中获取了所有医生和医院,然后在hiredDoctor 方法中我们打开了传递常规 Java 对象的事务,这些对象不在会话中。

我知道,我可以再次获取具有特定 ID 的 Hospital,以及具有特定 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);
}

但它看起来很垃圾。

那么 - 这样的更新应该如何最有效?

【问题讨论】:

  • 一个明显的解决方法是使用 SQLQuery 并向医生医院表中插入一行。但我仍然对类似 Hibernate 的解决方案感到好奇 - 猜测二级缓存可能会有所帮助。

标签: java hibernate spring-mvc


【解决方案1】:

有一种很好且优雅的方法可以做到这一点。它依赖于使用 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;
}

现在,将DoctorHospital 与仅一个插入语句关联,而无需任何额外的数据库往返:

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 中添加如下内容:

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 extra lazy collections 以避免在将Doctor 添加到Hospital 时加载doctors 集合,反之亦然(我假设您的问题的主要关注点)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多