【问题标题】:How to generate id based on two foreign keys in JPA / Hibernate?如何根据 JPA / Hibernate 中的两个外键生成 id?
【发布时间】:2010-07-19 11:13:22
【问题描述】:

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

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

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

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

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

有没有一种方法可以根据“Bar”类的 id 和整数“i”的值生成一个非复合的 id(即 int 或 long 类型),因为它是这两个值已经形成唯一约束了吗?

【问题讨论】:

    标签: java hibernate jpa


    【解决方案1】:

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

    您可以将复合键建模为可嵌入

    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 的原因。

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

    祝你好运, J.

    【讨论】:

    • 非常好。谢谢!通过谷歌搜索几乎不可能找到一个像样的例子。显然,除了这个。
    【解决方案2】:

    我认为“效率低下”是如此之小,以至于在 99.99% 的情况下可以忽略它。

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

    【讨论】:

      【解决方案3】:

      我认为你应该在这里重新考虑你的设计;拥有复合键或更好的 Id 可能更有意义。

      从外键生成主键值的理由可能适得其反。这是因为主键不应该被修改——如果其中一个外键值改变了怎么办?是否应该重新生成主键值?结果是否应该修改其他表中的列引用?在任何情况下,JPA 都要求不更改主键,因此最好使用自然键或代理键。

      编写生成器的工作最好花在确保模型正确上。

      【讨论】:

      • 那些外键其实是不能改的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-15
      • 2019-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多