Hibernate 4.2,双向@OneToOne和@Id

时间:2013-08-17 15:24:56

标签: hibernate orm jpa-2.0 one-to-one

我正在尝试使用OneToOne关系将可选数据(ExtraData)添加到主类(MainItem)。 ExtraData的所有实例都应链接到MainItem的实例,但并非MainItem的所有实例都需要ExtraData的实例。

(我主要对单向关系感兴趣,但似乎我需要双向关系才能将MainItem的更新和删除级联到ExtraData。)

我在使用@Id@OneToOne@JoinColumn以及双向关系时遇到问题。

类如下(单向):

@Entity
@Table(name = "main_item")
public class MainItem implements Serializable {
    @Id
    @Column(name = "id")
    private int id;

    @Column(name = "name")
    private String name;

    // + getters, setters, toString, ...
}

@Entity
@Table(name = "extra_data")
public class ExtraData implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @OneToOne
    @JoinColumn(name = "item_id")
    private MainItem item;

    @Column(name = "extra")
    private String extra;

    // + getters, setters, toString, ...
}

我正在使用hbm2ddl.auto=update,但我还直接插入了以下数据:

INSERT INTO main_item(id, name) VALUES (1, 'Test A');
INSERT INTO main_item(id, name) VALUES (2, 'Test B');

INSERT INTO extra_data(item_id, extra) VALUES (1, 'Extra A');

通过这种单向关系,获取额外数据及其主要项目可以正常工作:

Collection<ExtraData> extras = session.createCriteria(ExtraData.class)
        .list();
for (ExtraData extra : extras) {
    System.out.println(String.format("%s: %s", extra.getItem(), extra));
}

这是输出:

Hibernate: select this_.item_id as item2_0_0_, this_.extra as extra1_0_0_ from extra_data this_
Hibernate: select mainitem0_.id as id1_1_0_, mainitem0_.name as name2_1_0_ from main_item mainitem0_ where mainitem0_.id=?
Test A: Extra A

当我添加其他@OneToOne关系以使关系成为双向关系时,同一个查询在获取MainItem个实例时不再获取ExtraData个实例。

以下是添加到MainItem(+ get / set)的代码:

@OneToOne(mappedBy = "item", cascade = CascadeType.ALL)
private ExtraData extraData;

现在,上一代码的输出是:

Hibernate: select this_.item_id as item2_0_0_, this_.extra as extra1_0_0_ from extra_data this_
null: Extra A

我真的不明白为什么添加其他反转关系会阻止检索MainItem实例。 这似乎与在@Id中一起使用@OneToOne@JoinColumnExtraData有关。 我意识到这个was not possible with older version,但是我正在使用Hibernate 4.2,它应该支持这个(我不确定是否有特定的配置设置要激活)。

将相同的列用于成员使其有效,但我不确定这不会导致其他冲突:

@Id
@Column(name = "item_id")
private int itemId;

@OneToOne
@JoinColumn(name = "item_id")
private MainItem item;

我没有设法在不使用@Id成员中的单独@OneToOne的情况下获取变体,但以下变体似乎有效(至少对于该测试查询)。 使用Hibernate 4.2执行此操作的正确方法是什么?

  • 使用@PrimaryKeyJoinColumn

    @Id
    @Column(name = "item_id")
    private int itemId;
    
    @OneToOne
    @PrimaryKeyJoinColumn
    private MainItem item;
    
  • 使用@MapsId

    @Id
    @Column(name = "item_id")
    private int itemId;
    
    @OneToOne
    @MapsId
    private MainItem item;
    

编辑:

为了澄清一下,我并没有真正完成MainItemExtraData之间的完全双向关系,但我试图尽可能地分离这两组数据。

在纯SQL中,我会做这样的事情:

CREATE TABLE main_item (
    id INTEGER PRIMARY KEY,
    name TEXT
);
CREATE TABLE extra_data (
    item_id INTEGER PRIMARY KEY
        REFERENCES main_item(id) ON UPDATE CASCADE ON DELETE CASCADE,
    extra TEXT
);

此处,main_item中的问题不必了解extra_data。无论与extra_data有什么关系都可以由其他人编码和处理,可能在以后。

是的,如果有人删除main_item中另一行引用的extra_data中的行,则extra_data中的行也将被删除。

使用Hibernate,因为级联没有在生成的外键约束中声明,并且因为它似乎是从另一方的角度声明的,所以我似乎需要双向关系,至少要@OneToOne(mappedBy = "item", cascade = CascadeType.ALL) private ExtraData extraData;MainItem中,以便级联删除(否则删除MainData实例将失败,因为外键约束在数据库中。)

理想情况下,我希望与ExtraData相关的代码和数据模型依赖MainItem中的代码,但原始MainItem无需了解{{1}无论它带来什么新成员。

1 个答案:

答案 0 :(得分:1)

可选@SecondaryTable @Table是一个选项吗?

@Entity
@Table(name="MAIN_DATA")
@SecondaryTable(name="EXTRA_DATA")
[@org.hibernate.annotations.Table(
   appliesTo="EXTRA_DATA",
   fetch=FetchMode.SELECT,
   optional=true)

你可以在MainItem中拥有

class MainItem {
  @Column(name="extra", table="EXTRA_DATA")
  private String extra;
  // ...and others field

  public void setExtraData(ExtraData extra) {
    this.extra = extra.getExtra();
    // etc...  
  }

  public ExtraData getExtraData() {
    ExtraData extraData = new ExtraData();

    extraData.setExtra(this.extra);
    // etc...  
  }
}

我希望它可以提供帮助