代码之家  ›  专栏  ›  技术社区  ›  Luciano Greiner

一对一主键联接列级联

  •  0
  • Luciano Greiner  · 技术社区  · 10 年前

    我正在努力让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未能持久化封闭的Notificationcator对象,因为未设置Notificationcator.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,所以从目前来看,这将是一项肮脏的工作。

    非常感谢。

    1 回复  |  直到 10 年前
        1
  •  1
  •   Chris    10 年前

    在第一次尝试中,您在试图用作外键的字段上有了排序注释,但没有设置Notifietor.id字段。如果您希望这样做,可以创建一个从Notifier到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中的值可用时(post-persist/flush)自行设置Notification.id值。

    如果您使用的是JPA 2.0,则可以在Notifier类中使用@MapsId,而不是指定联接列,以便在对象图持久化时,JPA在托管实体中自动为您设置Notifietor.id字段值:

    @Entity
    class Notificator {
    
        @Id
        private UUID id;
    
        @OneToOne
        @MapsId
        @JoinColumn(name="NOTIFICATION_ID", referencedColumnName="NOTIFICATION_ID")
        private Notification notification; 
        ...
    }
    

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

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

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