【问题标题】:How to correctly setup bi-directional one-to-many relationship in Hibernate如何在 Hibernate 中正确设置双向一对多关系
【发布时间】:2015-02-14 04:09:20
【问题描述】:

我已经阅读了有关 stackoverflow 的几个 Q/A 以及其他几个在线教程,以找到对于下面描述的问题我到底缺少什么。

背景: 我正在学习在我的 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

}

最后,这是我的 Doctor 实体:

@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"}}]}

我为沿途的帖子道歉,但我认为我已经用尽了所有资源。我绝对崇拜任何决定看一看并指出问题所在的人。在这一点上,我什至不确定我是否正确地测试了这个东西,或者我的期望是否正确。

【问题讨论】:

    标签: spring hibernate jpa one-to-many


    【解决方案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 的保存期间(在第一个测试用例中),医生实体的外键不会在 Patient 表中更新。

    mappedBy 是 xml 格式的 equivalent to specifying inverse=true。 请关注this link,详细了解在一对多映射中指定 inverse=true 或 inverse=false 时执行哪些查询。

    【讨论】:

    • 非常感谢您的回答。只是为了记录,我还必须删除“@JsonManagedReference”和“@JsonBackReference”注释才能让所有测试用例通过。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-13
    • 2011-04-30
    • 2018-10-28
    • 2012-03-27
    • 1970-01-01
    • 2019-11-07
    相关资源
    最近更新 更多