JPA@OnetoOne自引用关系,两列均为非null



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

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

每个学生都有一位既是学生的导师。学生和导师之间有严格的一对一关系。对于id 1,不可能有任何导师,因此它将导师id作为自己的id。id是使用数据库序列生成的。

问题是,在生成id为1的第一条记录时,hibernate并没有分配与导师id相同的id,即使我已经创建了必要的关系。由于列不能为null并且hibernate没有分配mentor_id,所以抛出SQLConstraint nonnull异常。

以下是我如何建立这种关系。

@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,因为其他hibernate尝试从序列中检索2个id,并尝试创建2个不需要的记录。

问题是我如何插入第一张唱片。以下是如何插入的。

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

如果我将关系注释更改为@ManyToOne,因为从技术上讲,mentor_id是1,映射到2个学生,我会得到以下异常

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

编辑1:如果关系类型更改为@ManyToOne并删除级联,则会出现以下错误。

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.

编辑2:将级联类型更改为cascade=CascadeType.PERSIST,hibernate尝试将导师保存为单独的记录。我从日志中验证了它试图检索两个不同的序列id,并创建了两个插入查询,两个mentor_id都为null。

注意:终于找到了根本原因。我在JPA实体中使用了Lombok构建器,它还不支持自引用关系。

我换成了公共主持人,效果很好。有关更多详细信息,请参阅下面的链接https://github.com/rzwitserloot/lombok/issues/2440#event-3270871969

您可以忽略以下解决方案

我不是很自豪的解决方案,但以下是我如何实现它。

1.从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.将映射更改为简单外键字段。

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

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

3.创建了一个手动检索序列的方法,然后将值分配给"id"one_answers"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;
}

最新更新