【问题标题】:OneToOne bidirectional mapping foreign key auto fillOneToOne 双向映射外键自动填充
【发布时间】:2015-07-20 13:37:26
【问题描述】:

我在两个表之间建立了一对一的关系,但外键在我不需要映射的那个上,DBA 这样做是为了支持未来的更改。

假设我们有 User 和 Address,今天每个用户只有一个地址,并且会以这种方式映射,但 DBA 相信将来它可能是一对多映射,所以 user 的外键是 on地址,但应用程序有用户实例,这对于自动获取地址很重要。

我们做对了,如下:

@Entity
@Table(name = "user")
class User {
    @Id
    @Column(name = "user_id")
    private Long id;

    //...

    @OneToOne(cascade = CascadeType.MERGE, mappedBy = "user")
    private Address address; // this attribute is crucial
}

@Entity
@Table(name = "address")
class Address {
    @Id
    @Column(name = "address_id")
    private Long id;

    @OneToOne
    @JoinColumn(name = "user_id")
    private User user; // this attribute is not needed for the business at all, but mappedBy requires it

    //...

}

数据库:

-- SQL:
CREATE TABLE user
(
    user_id INT NOT NULL,
    -- ...
    CONSTRAINT user_pk PRIMARY KEY (user_id)
);

CREATE TABLE address
(
    address_id INT NOT NULL,
    user_id INT NOT NULL,
    -- ...
    CONSTRAINT address_pk PRIMARY KEY (address_id),
    CONSTRAINT address_user_id_fk FOREIGN KEY (user_id) REFERENCES user (user_id),
    CONSTRAINT address_user_id_uk UNIQUE (user_id) -- it says it's a one to one relation for now, if in the future it won't be anymore, just remove this constraint
);

问题是当用一个新的地址实例保存一个用户实例时,地址的用户属性是null,所以我期待Hibernate足够聪明,可以将它设置为来自用户实例的值。

我已经找了几天的解决方案,但仍然没有找到如何解决这个问题,同时我正在手动设置值,但我希望我不需要这样做。

【问题讨论】:

    标签: java hibernate orm one-to-one bidirectional


    【解决方案1】:

    标准解决方案是正确更新bidirectional association 的双方(尽管只需要更新拥有方才能将关联保存到数据库中)。添加到User 类中的Address 设置器:

    public void setAddress(Address address) {
       this.address = address;
       address.setUser(this);
    }
    

    此外,您可能希望扩展 address 属性的级联选项以包括 PERSIST,以便它始终与其用户一起持久化:

     @OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "user")
     private Address address;
    

    然后你可以为一个用户设置一个地址并保持两者:

    user.setAddress(address);
    session.persist(user);
    

    【讨论】:

    • 我明白了,我想知道 Hibernate 是否可以足够聪明地为我做这件事,但这个解决方案比设置用户创建地址的每段代码要好。
    【解决方案2】:

    如果不需要“私人用户用户”,请将其删除并删除“用户”实体中的 mappedBy。使用单向关系。

    在您的示例中,mappedBy 表示关联的所有者是“地址”实体,因此请保存 Adress 的实例而不是 User。喜欢:

    adress.setUser(user);
    session.save(adress);
    

    【讨论】:

    • 如果地址上有外键,我该怎么做?
    • 因为外键在地址上,所以需要持久化地址。因此,外键值将是用户实例的主键。
    • 不幸的是,它抛出了一个错误:Missing column: address_address_id in public.user 这就是我所说的,当我说需要 mappedBy 来指示外键在另一个实体上时。
    【解决方案3】:

    您需要添加 CasecadeType.PERSIST 以使创建地址与创建用户级联。

    在你的java代码中,你需要做:

    user.setAddress(address);
    address.setUser(user);
    session.persist(user);
    

    然后你的用户将被创建一个地址。

    如果您只想读取新创建用户的地址,那么您需要这样做:

    // your code to persist a new User and Address
    session.flush();
    session.refresh(user);
    

    如果不是您想要的,那么您需要分享您自己的 Java 代码并详细说明您的期望。

    【讨论】:

    • 我可以省略address.setUser(user);这行吗?如果可以的话,这就是答案。
    • 不幸的是它抛出了一个异常:ERROR: null value in column "user_id" violates not-null constraint
    猜你喜欢
    • 1970-01-01
    • 2014-07-15
    • 2015-08-24
    • 1970-01-01
    • 1970-01-01
    • 2014-10-12
    • 1970-01-01
    • 2014-05-17
    • 1970-01-01
    相关资源
    最近更新 更多