OneToOne PrimaryKeyJoinColumn 级联

OneToOne PrimaryKeyJoinColumn Cascade

我正在努力让 Eclipselink 级联保持关系:

@Entity
class Notification {
    @Id
    @UuidGenerator(name="UUID_GEN")
    @GeneratedValue(generator="UUID_GEN")
    @Column(name = "NOTIFICATION_ID")
    private UUID id;


    @OneToOne(cascade = ALL, fetch = FetchType.EAGER)
    @JoinFetch(JoinFetchType.INNER)
    @PrimaryKeyJoinColumn(name="NOTIFICATION_ID", referencedColumnName="NOTIFICATION_ID")
    private Notificator notificator;

   ...
}

@Entity
class Notificator {

    @Id
    @Column(name="NOTIFICATION_ID")
    private UUID id;

    ...
}

因此,当我尝试保留一个 Notification 对象时,Eclipselink 未能保留封闭的 Notificator 对象,因为未设置 Notificator.id,因此发生了约束失败。

在第二次尝试中,我尝试使用@MapId 注释:

@Entity
class Notification {
    @Id
    @UuidGenerator(name="UUID_GEN")
    @GeneratedValue(generator="UUID_GEN")
    @Column(name = "NOTIFICATION_ID")
    private UUID id;


    @OneToOne
    @MapsId
    private Notificator notificator;

   ...
}

@Entity
class Notificator {

    @Id
    @Column(name="NOTIFICATION_ID")
    private UUID id;

    @OneToOne
    PrimaryKeyJoinColumn(name="NOTIFICATION_ID", referencedColumnName="NOTIFICATION_ID")
    private Notification notification; // didn't need to have it here, but fine.
    ...
}

这样一来,我连EntityManager都带不上了。它抛出这个错误:

Exception Description: A non-read-only mapping must be defined for the sequence number field.

有没有办法让这个级联起作用,这样我就不需要分别保留这两个实体了?我试图避免这种情况,因为有一些其他实体共享同一个 ID,所以就目前而言,这将是一种肮脏的工作。

谢谢

第一次尝试时,您在尝试用作外键的字段上有排序注释,没有设置 Notificator.id 字段。如果您希望它起作用,您可以创建一个从 Notificator 到 Notification 的映射,以便它的 'ID' 字段用作外键。

不幸的是,您在第二次尝试中也错误地使用了@PrimaryKeyJoinColumn 注释。 @PrimaryKeyJoinColumn用于指定映射中指定的数据库字段由@Id标记的字段控制和设置,本质上使映射insertable/updatable=false。

要匹配您想要的结构,您应该尝试:

@Entity
class Notification {
    @Id
    @UuidGenerator(name="UUID_GEN")
    @GeneratedValue(generator="UUID_GEN")
    @Column(name = "NOTIFICATION_ID")
    private UUID id;


    @OneToOne(mappedby="notification", cascade = ALL, fetch = FetchType.EAGER)
    @JoinFetch(JoinFetchType.INNER)
    private Notificator notificator;

   ...
}

@Entity
class Notificator {

    @Id
    @Column(name="NOTIFICATION_ID")
    private UUID id;

    @OneToOne
    @JoinColumn(name="NOTIFICATION_ID", referencedColumnName="NOTIFICATION_ID", insertable=false, updatable=false)
    private Notification notification; 
    ...
}

这与您想要的不完全一样,因为上面的示例要求您在引用的 Notification.id 中的值可用后自行设置 Notificator.id 值(post persist/flush).

如果您使用的是 JPA 2.0,则可以在通知器中使用 @MapsId class 而不是指定连接列,让 JPA 在托管实体中自动为您设置 Notificator.id 字段值当您的对象图被持久化时:

@Entity
class Notificator {

    @Id
    private UUID id;

    @OneToOne
    @MapsId
    @JoinColumn(name="NOTIFICATION_ID", referencedColumnName="NOTIFICATION_ID")
    private Notification notification; 
    ...
}

正如您所指出的,您实际上并不需要模型中的 Notificator.notification 和 Notificator.id 值,但如果您想要该值,则至少需要 Notificator.notification Notificator."NOTIFICATION_ID" 字段来自通知。 JPA 2.0 允许您删除 Notificator.id 并仅使用 Notificator.notification 关系作为实体 ID 使用:

@Entity
class Notificator {

    @Id
    @OneToOne
    @JoinColumn(name="NOTIFICATION_ID", referencedColumnName="NOTIFICATION_ID")
    private Notification notification; 
    ...
}

这允许您仍然使用 UUID 值进行身份查找。 FetchJoins 等可以确保始终获取关系,但缺点是希望使用 ID 值的 JPQL 需要使用 notificator.notification.id 来访问它。