如何在JPA / Hibernate中基于两个外键生成id?

时间:2010-07-19 11:13:23

标签: java hibernate jpa

我有一个关于JPA中实体声明的简单问题。 我有一个带有2个外键的实体,它们不是null并且形成uniqueConstraint。首先,我在考虑一个由两个外键组成的复合键,但我听说这是一个遗留设计,而不是设计新表的推荐方法。

所以我感兴趣的是Hibernate / JPA能否根据两个外键自动生成id。假设我有以下实体:

@Entity
public class Foo {
  @ManyToOne
  private Bar bar;
  private int i;
}

(我省略了not null和uniqueConstraint标签,以使代码更具可读性)

我知道我可以简单地使用GeneratedValue添加一个id字段,并让我的数据库生成密钥(在我的示例MySQL中使用auto_increment),但这对我来说似乎效率低,因为它涉及查询数据库并要求它生成唯一的id值。

有没有一种生成id的方法,它不是复合的(即int或long类型),基于“Bar”类的id和整数“i”的值,因为它是那两个值已形成一个独特的约束?

3 个答案:

答案 0 :(得分:5)

您可能需要查看“Java Persistence with Hibernate”的第7章。

您可以将复合键建模为 Embeddable

import javax.persistence.*;
import java.io.Serializable;

@Entity
public class Foo {

    @Embeddable
    public static class Id implements Serializable {
        @Column(name = "bar_id_col")
        private Long barId;

        @Column(name = "i_col")
        private int i;

        public Id() {
        }

        public Id(Long barId, int i) {
            this.barId = barId;
            this.i = i;
        }

        @Override
        public boolean equals(final Object o) {
            if (this == o) {
                return true;
            }
            if (!(o instanceof Id)) {
                return false;
            }

            final Id id = (Id) o;

            if (i != id.i) {
                return false;
            }
            if (barId != null ? !barId.equals(id.barId) : id.barId != null) {
                return false;
            }

            return true;
        }

        @Override
        public int hashCode() {
            int result = barId != null ? barId.hashCode() : 0;
            result = 31 * result + i;
            return result;
        }
    }

    @EmbeddedId
    private Id id = new Id();

    @ManyToOne
    @JoinColumn(name = "bar_id_col", insertable = false, updatable = false)
    private Bar bar;

    private int i;

    public Foo() {
    }

    public Foo(Bar bar, int i) {
        // set fields
        this.Bar = bar;
        this.i=i;
        // set identifier values
        this.id.barId = bar.getId();
        this.id.i = i;
    }

}

这里我假设Bar看起来像:

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class Bar {

    @Id
    Long id;

    public Long getId() {
        return id;
    }

    public void setId(final Long id) {
        this.id = id;
    }
}

请注意,这会将 bar_id_col 映射两次。这是第二个引用中 insertable = false,updatable = false 的原因。

这很棘手,但如果你真的想这样做,那就有可能。

祝你好运, 学家

答案 1 :(得分:1)

我认为'效率低下'非常小,99.99%的情况下可以忽略它。

对于支持自动增量列的数据库,要求它生成ID没有额外的往返行程。对于不支持自动增量列的数据库(例如Oracle),Hibernate进行了一些优化以减少ID生成的数据库访问(例如,获取序列值,乘以50并使用结果中的后50个值作为新实体的ID )

答案 2 :(得分:1)

我认为你应该重新考虑你的设计;拥有一个复合键可能更有意义,或者更好的是Id。

从外键生成主键值的基本原理可能适得其反。这是因为主键不是要修改的 - 如果其中一个外键值发生变化会怎样?是否应重新生成主键值?并且应该修改其他表中的列引用吗?在任何情况下,JPA都要求不更改主键,因此最好使用自然键或代理键。

编写生成器的努力最好用于确保模型正确。