【问题标题】:Does Hibernate support one-to-one associations as pkeys?Hibernate 是否支持一对一的关联作为 pkeys?
【发布时间】:2010-04-14 14:37:29
【问题描述】:

谁能告诉我 Hibernate 是否支持关联作为实体的 pkey?我认为这会得到支持,但我在让任何代表这一点的映射工作时遇到了很多麻烦。特别是下面的直接映射:

@Entity
public class EntityBar
{
    @Id
    @OneToOne(optional = false, mappedBy = "bar")
    EntityFoo foo

    // other stuff
}

我收到 org.hibernate.MappingException:“无法确定类型:EntityFoo,在表:ENTITY_BAR,列:[org.hibernate.mapping.Column(foo)]”

深入研究代码,似乎 ID 始终被视为值类型;即“任何通过值而不是通过引用持久化的东西。它本质上是一个 Hibernate 类型,以及零个或多个列。”我可以通过将我的 EntityFoo 声明为可序列化的值类型,但我也不认为这会导致正确的结果。

我原以为 Hibernate 会认为列的类型是整数(或者无论父 ID 的实际类型是什么),就像使用普通的一对一链接一样,但这并不当我也将其声明为 ID 时,它似乎开始起作用。通过尝试将@OneToOne 与@Id 结合起来,我是否超越了可能?如果是这样,如何合理地模拟这种关系?

【问题讨论】:

  • 我不太明白。也许是一个示例映射,在这种情况下,休眠选择另一个实体会有所帮助。 “外国” ID 生成器是什么意思?
  • 好的,我已经改写了我的问题以解决实际的根本问题,这可以说更容易掌握。

标签: java hibernate orm entity-relationship


【解决方案1】:

如果目标是拥有一个共享主键,那该怎么做(灵感来自 Java Persistence With Hibernate 示例并在宠物数据库上进行了测试):

@Entity
public class User {
    @Id
    @GeneratedValue
    private Long id;

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private Address shippingAddress;

    //...
}

这是首先插入并获取生成的 id 的“父”类。 Address 看起来像这样:

@Entity
public class Address implements Serializable {
    @Id @GeneratedValue(generator = "myForeignGenerator")
    @org.hibernate.annotations.GenericGenerator(
        name = "myForeignGenerator",
        strategy = "foreign",
        parameters = @Parameter(name = "property", value = "user")
    )
    @Column(name = "ADDRESS_ID")
    private Long id;

    @OneToOne(mappedBy="shippingAddress")
    @PrimaryKeyJoinColumn
    User user;

    //...
}

对于上述实体,以下行为似乎符合预期:

User newUser = new User();
Address shippingAddress = new Address();
newUser.setShippingAddress(shippingAddress);
shippingAddress.setUser(newUser);            // Bidirectional
session.save(newUser);

保存Address 时,插入的主键值与user 属性引用的User 实例的主键值相同。

加载User 或Address 也可以。

如果我错过了什么,请告诉我。


PS:严格回答问题,照Primary Keys through OneToOne Relationships:

JPA 1.0 不允许在 OneToOne 或 ManyToOne 上使用 @Id,但 JPA 2.0 允许。

但是,符合 JPA 1.0 的 Hibernate 版本

允许在OneToOne 或ManyToOne 映射上使用@Id 注释*。

我无法让它与 Hibernate EM 3.4 一起使用(它与 Hibernate EM 3.5.1 一起使用,即 JPA 2.0 实现)。也许我做错了什么。

无论如何,使用共享主键似乎提供了一个有效的解决方案。

【讨论】:

  • 这并不完全满足我的情况,因为我不能在地址表中单独的列映射到用户(旧模式)。但是我没有在我的原始帖子中指定这一点,所以你已经回答了这个问题并且应该得到赏金。
  • @Andrzej 谢谢。澄清一下,使用上面的映射并没有在 Address 表中产生一个单独的列来映射 User(这是有道理的,因为它们共享相同的值)。在德比上测试。如果你愿意,我可以提供 DDL。
【解决方案2】:

是的,这是可能的。

请看以下示例,使用 Driver 和 DriverId 类作为 Driver 的 id。

@Entity
public class Drivers {

private DriversId id; //The ID which is located in another class

public Drivers() {
}

@EmbeddedId
@AttributeOverrides({
        @AttributeOverride(name = "personId", column = @Column(name = "person_id", nullable = false))})
@NotNull
public DriversId getId() {
    return this.id;
}
   //rest of class
}

这里我们使用 personId 作为 Driver 的 id

还有 DriversId 类:

//composite-id class must implement Serializable
@Embeddable
public class DriversId implements java.io.Serializable {
private static final long serialVersionUID = 462977040679573718L;

private int personId;

public DriversId() {
}

public DriversId(int personId) {
    this.personId = personId;
}

@Column(name = "person_id", nullable = false)
public int getPersonId() {
    return this.personId;
}

public void setPersonId(int personId) {
    this.personId = personId;
}

public boolean equals(Object other) {
    if ((this == other))
        return true;
    if ((other == null))
        return false;
    if (!(other instanceof DriversId))
        return false;
    DriversId castOther = (DriversId) other;

    return (this.getPersonId() == castOther.getPersonId());
}

public int hashCode() {
    int result = 17;

    result = 37 * result + this.getPersonId();
    return result;
}
}

【讨论】:

  • 我认为这不是 OP 所要求的。我的理解是 OP 想要专用表,并且想要将 EntityFoo 和 EntityBar 都映射为实体。
【解决方案3】:

您可以通过在 EntityFoo 和 EntityBar 之间共享一个主键来做到这一点:

@Entity
public class EntityBar
{
    @Id @OneToOne
    @JoinColumn(name = "foo_id")
    EntityFoo foo;

    // other stuff
}

@Entity
public class EntityFoo
{
    @Id @GeneratedValue
    Integer id;

    // other stuff
}

【讨论】:

  • 但是EntityFoo 没有到EntityBar 的映射,所以当从数据库中加载EntityFoo 时,您无法获取关联的EntityBar 对象。
【解决方案4】:

你必须在这里使用@EmbeddedId 而不是@Id。 而EntityFoo 应该是Embeddable。

另一种方法是放入一个整数,一个OneToOne 将updateble 和instertable 设置为false.

【讨论】:

  • 但EntityFoo 不是可嵌入组件,它本身就是一流的实体。 IIRC 无论如何,您不能在实体和组件之间进行 OneToOne 映射。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-26
  • 1970-01-01
  • 2014-03-04
  • 2013-08-20
  • 2017-03-11
  • 2017-09-23
相关资源
最近更新 更多