两列的JPA @OnetoOne自引用关系非null [英] JPA @OnetoOne self reference relationship with both columns non null

查看:342
本文介绍了两列的JPA @OnetoOne自引用关系非null的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个现有的数据库表,例如在T_STUDENTS上,我必须创建一个JPA实体.该表中的所有三列均为NON NULL,并且该表的自引用为mentor_id

I have an existing database table For e.g. T_STUDENTS on top of which I have to create a JPA entity. All three columns in the table are NON NULL and the table has a self-reference as mentor_id

id   | name    | mentor_id  
-----|---------|----------
1    | John    | 1
-----|---------|----------
2    | Marc    | 1
-----|---------|----------
3    | Abby    | 2
-----|---------|----------
4    | Jimy    | 3
-----|---------|----------
5    | Boni    | 4
-----|---------|----------

每个学生都有一位导师,同时也是一名学生.学生和导师之间存在严格的OneToOne关系.对于ID 1,不能有任何指导者,因此它具有指导者ID,因为它是自己的id.这些ID是使用数据库序列生成的.

Each student has a mentor who is also a student. There is a strict OneToOne relationship between the student and the mentor. For id 1, there can't be any mentor, therefore it has the mentor id as it's own id. The ids are generated using a database sequence.

问题在于,即使我创建了必要的关系,在生成ID为1的第一条记录时,hibernate却没有分配与指导者id相同的id.由于列不能为null,并且hibernate没有分配mentor_id,因此将引发SQLConstraint nonnull异常.

The problem is that while generating the first record with id 1, hibernate is not assigning the same id as mentor id even though I have created necessary relationships. Since columns can't be null and hibernate is not assigning mentor_id, SQLConstraint nonnull exception is thrown.

以下是我创建关系的方式.

Following is how I have created the relationship.

@Entity
@Table(name = 'T_STUDENTS')
public class Student implements Serializable {

  @Id
  @SequenceGenerator(name = 'S_STUDENTS_SEQUENCE', allocationSize = 1)
  @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = 'S_STUDENTS_SEQUENCE')
  @Column(name = "id")
  private Long studentId;

  @Column(name = "name", length = 20)
  private String studentName;

  @OneToOne(optional = false, cascade = CascadeType.NONE)
  @JoinColumn(name = "mentor_id")
  private Student mentor;

  // getters and setters

}

我设置了CascadeType.NONE,因为否则冬眠尝试从序列中检索2个ID,并尝试创建2个不希望的记录.

I have set CascadeType.NONE because else hibernate tries to retrieve 2 id's from sequence and tries to create 2 records which are not desirable.

问题是我该如何插入第一条记录.以下是插入的完成方式.

The problem is how can I insert the very first record. Following is how the insert is being done.

Student student = Student.builder()
                        .setName('John')
                        .build();
student = student.toBuilder().setMentor(student).build();
return studentRepository.save(student);

如果由于技术上mentor_id为1映射到2个学生而将关系注释更改为@ManyToOne,则会出现以下异常

If I change the relationship annotation to @ManyToOne since technically mentor_id is 1 is mapped to 2 students, I get the following exception

.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation 

编辑1 :如果将关系类型更改为@ManyToOne并在观察到错误后删除了层叠.

Edit 1: If relationship type changed to @ManyToOne and cascade is removed following error is observed.

org.hibernate.action.internal.UnresolvedEntityInsertActions.logCannotResolveNonNullableTransientDependencies - HHH000437: Attempting to save one or more entities that have a non-nullable association with an unsaved transient entity. The unsaved transient entity must be saved in an operation prior to saving these dependent entities.

将级联类型更改为cascade = CascadeType.PERSIST,并且休眠尝试将导师作为单独的记录进行持久化.我从日志中验证了它尝试检索2个不同的序列ID并创建2个插入查询,并将mentor_id都设置为null.

Edit 2: Changed the cascade type to cascade = CascadeType.PERSIST and hibernate tries to persist the mentor as a separate record. I verified from logs that it tries to retrieve 2 different sequence ids and creates 2 insert queries, with both mentor_id as null.

推荐答案

注意:最终,我找到了根本原因.我在JPA实体中使用了Lombok Builder,它还不支持自引用关系.

NOTE: Finally I found the root cause. I was using Lombok builder in the JPA entity and it does not support the self-reference relationship yet.

我改用公开传销员,但效果很好.有关更多详细信息,请参见下面的链接 https://github.com/rzwitserloot/lombok/issues/2440#event- 3270871969

I switched to public setters and it worked fine. See the link below for more details https://github.com/rzwitserloot/lombok/issues/2440#event-3270871969

您可以忽略以下解决方案.

我对解决方案并不感到骄傲,但是这是我实现该解决方案的方式.

I'm not very proud of the solution, but here is how I achieved it.

1.从ID中删除了自动序列生成.

1.Removed auto sequence generation from the id.

@Id
@SequenceGenerator(name = 'S_STUDENTS_SEQUENCE', allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = 'S_STUDENTS_SEQUENCE')
@Column(name = "id")
private Long studentId

@Id
@Column(name = "id")
private Long studentId; 

2.将映射更改为简单外键字段.

2.Changed the mapping to the simple foreign key field.

@OneToOne(optional = false, cascade = CascadeType.NONE)
@JoinColumn(name = "mentor_id")
private Student mentorId;

@Column(name = "mentor_id")
private Long mentorId;

3.创建一种手动检索序列的方法,然后将值分配给'id'和'mentorId'

3.Created a method to retrieve the sequence manually and then assigned the value to both 'id' and 'mentorId'

@Override
public Student saveExtended(Student student) {
    Object sequence =
        em.createNativeQuery(
                "SELECT NEXT VALUE FOR S_STUDENTS_SEQUENCE AS VALUE FROM SYSIBM.SYSDUMMY1")
            .getSingleResult();
    BigInteger sequenceLong = (BigInteger) sequence;
    student = student.toBuilder().id(sequenceLong.longValue()).mentorId(sequenceLong.longValue()).build();
    em.persist(student);
    em.flush();
    return student;
}

这篇关于两列的JPA @OnetoOne自引用关系非null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆