JPA:OneToOne 关系所有者

Posted

技术标签:

【中文标题】JPA:OneToOne 关系所有者【英文标题】:JPA: OneToOne relation owner 【发布时间】:2017-05-14 14:05:01 【问题描述】:

我想通过User.idAddress.user_id 列在两个模型之间建立关系。

我创建了两个具有一对一关系的表:

@Entity()
@Table(name = "user")
public class User 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id", referencedColumnName = "user_id")
    private Address address;

    public int getId() 
        return id;
    

    public void setId(Integer id) 
        this.id = id;
    

    public Address getAddress() 
        return address;
    

    public void setAddress(Address address) 
        this.address = address;
    





@Entity
@Table(name = "address")
public class Address extends com.mezoline.domain.common.Entity 

    @Id
    @GeneratedValue
    private int id;

    @OneToOne(mappedBy = "address")
    private User user;

    public int getId() 
        return id;
    

    public void setId(int id) 
        this.id = id;
    

    public User getUser() 
        return user;
    

    public void setUser(User user) 
        this.user = user;
    


这里我已经可以看出问题了:hibernate没有像我预期的那样生成数据库列Address.user_id

我创建Address 实例并将其添加到User

    User user = entityManager.find(User.class, 69);
    Address address = new Address();
    address.setCity("Тест");
    userTransaction.begin();
    user.setAddress(address);
    entityManager.merge(user);
    userTransaction.commit();

在我致电merge(user) 之后。数据成功保存...没有任何关系信息。

更新:

使用下面的配置,JPA 将创建关系列Address.user_id(只是交换了关系所有者)

public class User  
    ...
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
    private Address address;
    ...


public class Address 
    ....
    @OneToOne()
    @JoinColumn(name = "user_id")
    private User user;
    ....

但保存后Address.user_id 为空...(正在填充其他列)

UPD2:

谢谢。第二个配置工作正常,何时设置反向关系字段(如 cmets 中所建议):

Address address = createAddress();
address.setUser(user);
user.setAddress(address);

但我不明白,为什么第一个配置(用户是拥有方)不起作用。

【问题讨论】:

【参考方案1】:

这是因为您对初始配置的期望存在逻辑错误。

您声明User 是关系的拥有方。 Owning 表示表示关系的外键将保存在USER 表中,而不是Address 表中。

同时,您将User.address 注释为@JoinColumn(name = "id", referencedColumnName = "user_id")。基本上,我认为您正试图强制 ADRESS 表保存外键。但在这种情况下,Adress 应该是拥有方

如果您希望初始配置起作用,您应该做的就是将当前的JoinColumn 注释替换为@JoinColumn(name = "address_id")。外键最终会出现在USER 表中。持久化实体时,必须设置User.address;设置Address.user 是可选的。

如果您绝对需要在 ADDRESS 表中拥有密钥,请将 Address 表设为拥有方(然后您只需确保在持久时设置 Address.user;但是,您仍然需要设置User.address 以使级联工作,除非您明确地保留Address 实体)。

【讨论】:

【参考方案2】:

因为是双向One-to-One,所以在持久化父/超实体之前,还需要将反面设置为所谓的父实体。

Address addr =  new Address()
user.setAddress(addr);
addr.setUser(user);
session.save(user);

【讨论】:

【参考方案3】:

我会保留原始配置,其中用户是拥有方。关于您的架构,这对我来说更有意义。

关于地址.. 上的user_id 列,您必须确保在关系的双方都设置了依赖关系。因此,您的交易方法应包含以下内容:

if (user.getAddress() == null) 
    Address address = createAddress();
    address.setUser(user);
    user.setAddress(address);        

【讨论】:

【参考方案4】:

@crizzis 解释得很好,我试图保存一个 Employee 对象和 Address 对象并使 Address 表具有外键,但是 Employee 对象拥有关系,在按照您的指示将所有权更改为 Employee现在外键更新正常。

【讨论】:

请避免发布“谢谢”答案。如果有什么对你有用,请投票赞成:)

以上是关于JPA:OneToOne 关系所有者的主要内容,如果未能解决你的问题,请参考以下文章

Spring JPA 删除 @OneToOne 关系

JPA 孤儿删除不适用于 OneToOne 关系

hibernate/jpa double OneToOne 与一个实体的双向关系

SpringData JPA中一对一关系映射注解@OneToOne应用

jpa hibernate @OneToOne @JoinColumn referencedColumnName 被忽略

在我的 Play 应用程序中使用 Ebean/JPA,如何删除 OneToOne 关系中的对象?