2015-07-20 71 views
2

我在两个表之间有一对一的关系,但外键在我不需要映射的那个上,DBA这样做是为了支持未来的更改。让我们想象我们有用户和地址,今天每个用户只有一个地址,并且将以这种方式映射,但DBA相信未来它可能是一对多映射,所以用户的外键是在地址上,但应用程序有用户实例,这对于自动获取地址很重要。OneToOne双向映射外键自动填充

我们这样做是正确的,如下:

@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 
); 

问题是,当保存的用户实例与地址的新实例,地址的用户的属性所以我期望Hibernate能够很聪明地将它设置为来自用户实例的值。

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

回答

3

标准的解决方案是正确更新bidirectional association的两边(虽然只有拥有方需要更新以便将关联保存到数据库中)。添加到Address二传手在User类:

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); 
+0

我看到,我想知道Hibernate可以聪明地为我做,但这个解决方案是比设置用户创建地址的每一段代码更好。 –

0

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

在你的例子中,mappedBy意味着关联的所有者是'Address'实体,所以保存Adress的实例而不是User。像:

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

我怎么能这样做,如果外键是地址? –

+0

因为外键在地址上,所以你需要坚持地址。因此,外键值将是用户实例的主键。 –

+0

不幸的是它会抛出一个错误:'在public.user'中缺少列:address_address_id,这就是我所说的当我说mappedBy需要指示外键在另一个实体上。 –

0

您需要添加CasecadeType.PERSIST以创建用户创建地址casecade。

在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代码,并给详细描述你的期望。

+0

我可以省略'address.setUser(user)'这行吗?如果我能做到这一点,就是答案。 –

+0

试试吧!对我来说,它应该工作 – Qianlong

+0

不幸的是,它引发了一个异常:'错误:列'user_id'中的空值违反了非空约束' –