【问题标题】:OneToOne PrimaryKeyJoinColumn CascadeOneToOne PrimaryKeyJoinColumn 级联
【发布时间】:2015-02-10 16:00:59
【问题描述】:

我正在努力让 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。它抛出这个错误:

异常描述:必须为 序列号字段。

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

谢谢

【问题讨论】:

    标签: hibernate jpa eclipselink jpa-2.0


    【解决方案1】:

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

    不幸的是,您在第二次尝试中也错误地使用了 @PrimaryKeyJoinColumn 注释。 @PrimaryKeyJoinColumn 用于指定映射中指定的数据库字段由带有@Id 标记的字段控制和设置,本质上使映射可插入/可更新=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 值(持久/刷新后)。

    如果您使用的是 JPA 2.0,则可以在 Notificator 类中使用 @MapsId 而不是指定连接列,以便在持久化对象图时让 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_ID" 字段的值,则至少需要 Notificator.notification来自通知。 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 来访问它。

    【讨论】:

    • 设法使它工作。谢谢你,兄弟。只需通过设置器将通知引用传递给子对象。
    猜你喜欢
    • 2012-02-25
    • 2016-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-08
    • 2011-09-11
    • 2021-01-23
    • 1970-01-01
    相关资源
    最近更新 更多