如何在Hibernate中正确设置双向一对多关系

时间:2014-12-15 22:51:55

标签: spring hibernate jpa one-to-many

我已经在stackoverflow和其他几个在线教程中经历了几个Q / As,以找到我在下面描述的问题中完全没有的内容。

背景: 我正在学习在我的Android应用程序中使用restful API,因此,我编写了一个简单的医患管理应用程序。医生和他的病人之间存在一对多的关系。即一位医生可以有很多病人。

问题: 我正在使用一个用户表来维护所有用户信息,即医生和病人。此表中维护了基本信息,此表还用于确定尝试登录的用户类型,以便显示适当的屏幕。以下是该表的实体的样子:

@Entity
public class ConcreteUser implements User{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name= "USER_ID")
    private long id;

    private String name;
    private String email;
    private int age;
    private SEX sex;
    private String accessLevel;

    public ConcreteUser() {
    }
 // gettersand setters here
} 

此实体与维护医生和患者实体的表具有一对一的关系。如前所述,医生和患者实体之间存在一对一的关系。以下是这两个实体的外观:

@Entity
public class PatientEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "PATIENT_RECORD_ID")
    private long recordId;

    // specify this as a foreign key from ConcreteUser entity
    @OneToOne(cascade = CascadeType.ALL) /*CascadeType.ALL should not be required according to almost all the tutorials I have seen - But I always get unsaved transient object error if I don't do this and try to save a patient entity */
    @JoinColumn(name="USER_ID")
    private ConcreteUser patient;

    @ManyToOne(cascade = {CascadeType.ALL})
    @JoinColumn(name = "DOCTOR_RECORD_ID")
    @JsonBackReference
    private DoctorEntity doctor;


    public PatientEntity() {
    }

    public void setDoctor(DoctorEntity doctor) {
        this.doctor = doctor;
        //if(!doctor.getPatients().contains(this)){
        //  doctor.addPatient(this);
        //}
     /* Commented out code always leads to stack overflow error */
     /* although, according to tutorial in the link below, this code is necessary */
     /* http://en.wikibooks.org/wiki/Java_Persistence/OneToMany */
    }

 // getters and setters are not shown

}

最后,这是我的医生实体:

@Entity
public class DoctorEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "DOCTOR_RECORD_ID")
    private long recordId;

    // specify this as a foreign key from ConcreteUser entity
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="USER_ID")
    private ConcreteUser doctor;

    @OneToMany(mappedBy = "doctor", cascade = CascadeType.ALL)
    @JsonManagedReference
    private Collection<PatientEntity> patients = new ArrayList<PatientEntity>();

    public DoctorEntity() {
    }

    public boolean addPatient(PatientEntity p) {
        boolean status = false;

        status = patients.add(p);
        //if (p.getDoctor() != this) {
        //  p.setDoctor(this);
        //}
        return status;
    }

    public boolean removePatient(PatientEntity p) {
        boolean status = false;

        status = patients.remove(p);
        //if (p.getDoctor() != this) {
        //  p.setDoctor(null);
        //}
        return status;
    }

// getters and setters are not shown. Same problem with the commented out code as described above
}

现在测试这样一个事实:当PatientEntity的POJO对象可以保存并且它保留信息时,我使用以下测试用例:

@Test
public void TestPatientDoctorManyToOne() throws Exception{
    PatientEntity p1 = TestData.getPatientEntity(patient1);
    DoctorEntity d = TestData.getDoctorEntity(doctor);

    p1.setDoctor(d);

    p1 = patientService.addPatient(p1);
    assertNotNull(p1);

    PatientEntity p2 = patientService.getPatientById(p1.getRecordId());
    assertNotNull(p2);
    assertNotNull(p2.getDoctor());

    assertEquals(p1.getRecordId(), p2.getRecordId());
    assertEquals(p1.getDoctor().getRecordId(), p2.getDoctor().getRecordId());
    assertEquals(p1.getDoctor().getDoctor().getEmail(), p2.getDoctor().getDoctor().getEmail());

}

在上述测试案例中,assertNotNull(p2.getDoctor());断言失败,因为返回的患者实体根本不包含医生对象。

这是日志:

Outgoing:
"{"recordId":0,"patient":{"id":0,"name":"Patient-0ee1407e-2d2b-4c6c-a57b-e2fad24fafa5","email":"0ee1407e-2d2b-4c6c-a57b-e2fad24fafa5","age":50,"sex":"MALE","accessLevel":"patient"},"doctor":{"recordId":0,"doctor":{"id":0,"name":"Doctor-f025c8ce-8c31-4681-b673-a9e322dccf5a","email":"f025c8ce-8c31-4681-b673-a9e322dccf5a","age":50,"sex":"MALE","accessLevel":"doctor"},"patients":[]}}"

Incoming:
{"recordId":16,"patient":{"id":33,"name":"Patient-0ee1407e-2d2b-4c6c-a57b-e2fad24fafa5","email":"0ee1407e-2d2b-4c6c-a57b-e2fad24fafa5","age":50,"sex":"MALE","accessLevel":"patient"}}

如您所见,返回的对象根本没有Doctor实体。

但是,当我尝试将医生实体与其中的患者一起保存时,可以毫无问题地保存。即以下测试用例通过:

@Test
public void testDoctorPatientOneToMany() throws Exception {

    PatientEntity p1 = TestData.getPatientEntity(patient1);
    PatientEntity p2 = TestData.getPatientEntity(patient2);
    DoctorEntity d = TestData.getDoctorEntity(doctor);

    d.addPatient(p1);
    d.addPatient(p2);

    d = doctorService.addDoctor(d);

    DoctorEntity d2 = doctorService.getDoctorById(d.getRecordId());
    assertNotNull(d2);
    assertEquals(d2.getRecordId(), d.getRecordId());
    assertEquals(d2.getDoctor().getEmail(), d.getDoctor().getEmail());


}

上述测试用例的交易: 传出:

"{"recordId":17,"doctor":{"id":43,"name":"Doctor-e4baeee7-eaaa-443e-8845-e0b12d7be82f","email":"e4baeee7-eaaa-443e-8845-e0b12d7be82f","age":50,"sex":"MALE","accessLevel":"doctor"},"patients":[{"recordId":21,"patient":{"id":44,"name":"Patient-d8aab5ad-d3d9-4442-b8de-678de9e3b1ce","email":"d8aab5ad-d3d9-4442-b8de-678de9e3b1ce","age":50,"sex":"MALE","accessLevel":"patient"}},{"recordId":22,"patient":{"id":45,"name":"Patient-5c9cfa3c-ee79-4aea-a193-4d8762f58431","email":"5c9cfa3c-ee79-4aea-a193-4d8762f58431","age":50,"sex":"MALE","accessLevel":"patient"}}]}[\r][\n]"

Incoming:
{"recordId":17,"doctor":{"id":43,"name":"Doctor-e4baeee7-eaaa-443e-8845-e0b12d7be82f","email":"e4baeee7-eaaa-443e-8845-e0b12d7be82f","age":50,"sex":"MALE","accessLevel":"doctor"},"patients":[{"recordId":21,"patient":{"id":44,"name":"Patient-d8aab5ad-d3d9-4442-b8de-678de9e3b1ce","email":"d8aab5ad-d3d9-4442-b8de-678de9e3b1ce","age":50,"sex":"MALE","accessLevel":"patient"}},{"recordId":22,"patient":{"id":45,"name":"Patient-5c9cfa3c-ee79-4aea-a193-4d8762f58431","email":"5c9cfa3c-ee79-4aea-a193-4d8762f58431","age":50,"sex":"MALE","accessLevel":"patient"}}]}

我为帖子道歉,但我想我已经耗尽了所有的资源。我绝对崇拜那些决定看一眼并指出问题所在的人。在这一点上,我甚至不确定我是否正确测试这件事,或者我的期望是正确的。

1 个答案:

答案 0 :(得分:1)

这是因为在DoctorEntity类中指定了mappedBy =“doctor”

@Entity
public class DoctorEntity {

    @OneToMany(mappedBy = "doctor", cascade = CascadeType.ALL)
    @JsonManagedReference
    private Collection<PatientEntity> patients = new ArrayList<PatientEntity>();

    public DoctorEntity() {
    }
}

你说DoctorEntity不再是一对多关系的拥有者。 PatientEntity是所有者。因此,在保存PatientEntity期间(在第一个测试案例中),医生实体的外键未在患者表中更新。

mappedBy是xml格式的equivalent to specifying inverse=true。 关注在一对多映射中指定inverse = true或inverse = false时执行查询的详细说明。