两列的JPA @OnetoOne自引用关系非null [英] JPA @OnetoOne self reference relationship with both columns non 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屋!