简体   繁体   English

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

[英]How to generate id based on two foreign keys in JPA / Hibernate?

I have a simple question regarding Entity declaration in JPA. 关于JPA中的实体声明,我有一个简单的问题。 I have an entity with 2 foreign keys, which are not null and form an uniqueConstraint. 我有一个带有2个外键的实体,这些外键不为null,并形成uniqueConstraint。 First I was thinking about a composite key, composed of the two foreign keys, but I heard that this is a legacy design, and not the recommended way of designing new tables. 首先,我正在考虑一个由两个外键组成的复合键,但是我听说这是一个遗留设计,而不是推荐的设计新表的方式。

So I am interested if Hibernate/JPA can automatically generate id, based on the two foreign keys. 因此,我很感兴趣Hibernate / JPA是否可以基于两个外键自动生成id。 Let's say I have the following Entity: 假设我有以下实体:

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

(I omitted not null and uniqueConstraint tags to make the code more readable) (我省略了不是null和uniqueConstraint标记,以使代码更具可读性)

I know I can simply add an id field, with GeneratedValue, and let my DB generate the key (in my example MySQL with auto_increment), but this seems inefficient to me as it involves querying the database, and asking it to generate the unique id value. 我知道我可以简单地添加一个带有GeneratedValue的id字段,并让我的数据库生成密钥(在我的示例中为MySQL,使用auto_increment),但这对我来说似乎效率不高,因为它涉及查询数据库并要求其生成唯一ID值。

Is there a way of generating an id, which is not composite (ie of type int or long), based on the id of the "Bar" class, and value of the integer "i", since it those two values already form a unique constraint? 有没有一种方法可以基于“ Bar”类的ID和整数“ i”的值来生成非复合ID(即int或long类型),因为这两个值已经形成了独特的约束?

You may want to check out Chapter7 of " Java Persistence with Hibernate ". 您可能需要查看“ 带有Hibernate的Java持久性 ”的第7章。

You can model the composite key as an Embeddable : 您可以将组合键建模为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;
    }

}

Here I assume Bar looks like: 在这里,我假设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;
    }
}

Notice that this maps the bar_id_col twice. 注意,这两次映射了bar_id_col This is the reason for insertable = false, updatable = false in the second reference. 这就是第二个引用中的insertable = false,可更新= false的原因。

It's tricky, but if you really want to do it like this, it's possible. 这很棘手,但是如果您真的想这样做,那就有可能。

Good luck, J. 祝你好运,J。

I think the 'ineffeciency' is so minor that in 99.99% of case it can be ignored. 我认为“效率低下”如此之小,在99.99%的情况下可以忽略不计。

For DB supporting auto increment column, there is no extra round trip for asking it to generate the ID. 对于支持自动增量列的数据库,没有额外的往返请求。 For DB that does not support auto increment column (eg Oracle), Hibernate have some optimization done to reduce DB access for ID generation (eg get the sequence value, multiply by 50 and use the next 50 values from the result as new entities' ID) 对于不支持自动递增列的数据库(例如Oracle),Hibernate进行了一些优化,以减少ID生成的数据库访问(例如,获取序列值,乘以50,并将结果中的下50个值用作新实体的ID) )

I think you should rethink your design here; 我认为您应该在这里重新考虑您的设计; it probably makes more sense to have a composite key, or better an Id. 拥有一个组合键,或者更好地使用一个ID可能更有意义。

The rationale of generating primary key values out of foreign keys can backfire. 从外键中生成主键值的原理可能适得其反。 This is because primary keys are not meant to be modified - what if one of the foreign key values change? 这是因为不打算修改主键-如果其中一个外键值发生更改怎么办? Should the primary key value be regenerated? 是否应该重新生成主键值? And should column references in other tables be modified as a result?. 并且是否应该修改其他表中的列引用? In any case, JPA requires that primary keys not be changed, so you're better off having a natural or a surrogate key. 无论如何,JPA都要求不要更改主键,因此最好不要使用自然键或代理键。

The effort in writing a generator, is better spent in ensuring that the model is correct. 编写生成器的工作最好用于确保模型正确。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM