如何使用Hibernate映射組合鍵?

[英]How to map a composite key with Hibernate?


In this code, how to generate a Java class for the composite key (how to composite key in hibernate):

在這段代碼中,如何為組合鍵生成Java類(如何在hibernate中組合鍵):

create table Time (
        levelStation int(15) not null,
        src varchar(100) not null,
        dst varchar(100) not null,
        distance int(15) not null,
        price int(15) not null,
        confPathID int(15) not null,
        constraint ConfPath_fk foreign key(confPathID) references ConfPath(confPathID),
        primary key (levelStation, confPathID)
)ENGINE=InnoDB  DEFAULT CHARSET=utf8;

8 个解决方案

#1


352  

To map a composite key, you can use the EmbeddedId or the IdClass annotations. I know this question is not strictly about JPA but the rules defined by the specification also applies. So here they are:

要映射復合鍵,可以使用EmbeddedId或IdClass注釋。我知道這個問題並不完全是關於JPA的,但是規范定義的規則也適用。這里:

2.1.4 Primary Keys and Entity Identity

...

A composite primary key must correspond to either a single persistent field or property or to a set of such fields or properties as described below. A primary key class must be defined to represent a composite primary key. Composite primary keys typically arise when mapping from legacy databases when the database key is comprised of several columns. The EmbeddedId and IdClass annotations are used to denote composite primary keys. See sections 9.1.14 and 9.1.15.

復合主鍵必須對應於單個持久字段或屬性,或者對應於如下所述的一組此類字段或屬性。必須定義一個主鍵類來表示復合主鍵。當數據庫鍵由幾個列組成時,從遺留數據庫映射時,通常會出現復合主鍵。EmbeddedId和IdClass注釋用於表示復合主鍵。參見第9.1.14和9.1.15節。

...

The following rules apply for composite primary keys:

復合主鍵適用以下規則:

  • The primary key class must be public and must have a public no-arg constructor.
  • 主鍵類必須是公共的,並且必須具有公共的無arg構造函數。
  • If property-based access is used, the properties of the primary key class must be public or protected.
  • 如果使用基於屬性的訪問,則必須公開或保護主鍵類的屬性。
  • The primary key class must be serializable.
  • 主鍵類必須是可序列化的。
  • The primary key class must define equals and hashCode methods. The semantics of value equality for these methods must be consistent with the database equality for the database types to which the key is mapped.
  • 主鍵類必須定義equals和hashCode方法。這些方法的值相等的語義必須與將鍵映射到的數據庫類型的數據庫相等一致。
  • A composite primary key must either be represented and mapped as an embeddable class (see Section 9.1.14, “EmbeddedId Annotation”) or must be represented and mapped to multiple fields or properties of the entity class (see Section 9.1.15, “IdClass Annotation”).
  • 復合主鍵必須被表示並映射為可嵌入的類(參見第9.1.14節“EmbeddedId注釋”),或者必須被表示並映射到實體類的多個字段或屬性(參見第9.1.15節“IdClass注釋”)。
  • If the composite primary key class is mapped to multiple fields or properties of the entity class, the names of primary key fields or properties in the primary key class and those of the entity class must correspond and their types must be the same.
  • 如果復合主鍵類映射到實體類的多個字段或屬性,主鍵類中的主鍵字段或屬性的名稱和實體類的名稱必須對應,它們的類型必須相同。

With an IdClass

The class for the composite primary key could look like (could be a static inner class):

復合主鍵的類可能看起來像(可能是一個靜態內部類):

public class TimePK implements Serializable {
    protected Integer levelStation;
    protected Integer confPathID;

    public TimePK() {}

    public TimePK(Integer levelStation, Integer confPathID) {
        this.levelStation = levelStation;
        this.confPathID = confPathID;
    }
    // equals, hashCode
}

And the entity:

和實體:

@Entity
@IdClass(TimePK.class)
class Time implements Serializable {
    @Id
    private Integer levelStation;
    @Id
    private Integer confPathID;

    private String src;
    private String dst;
    private Integer distance;
    private Integer price;

    // getters, setters
}

The IdClass annotation maps multiple fields to the table PK.

IdClass注釋將多個字段映射到表PK。

With EmbeddedId

The class for the composite primary key could look like (could be a static inner class):

復合主鍵的類可能看起來像(可能是一個靜態內部類):

@Embeddable
public class TimePK implements Serializable {
    protected Integer levelStation;
    protected Integer confPathID;

    public TimePK() {}

    public TimePK(Integer levelStation, Integer confPathID) {
        this.levelStation = levelStation;
        this.confPathID = confPathID;
    }
    // equals, hashCode
}

And the entity:

和實體:

@Entity
class Time implements Serializable {
    @EmbeddedId
    private TimePK timePK;

    private String src;
    private String dst;
    private Integer distance;
    private Integer price;

    //...
}

The @EmbeddedId annotation maps a PK class to table PK.

@EmbeddedId注釋將PK類映射到表PK。

Differences:

  • From the physical model point of view, there are no differences
  • 從物理模型的角度來看,沒有區別
  • @EmbeddedId somehow communicates more clearly that the key is a composite key and IMO makes sense when the combined pk is either a meaningful entity itself or it reused in your code.
  • @EmbeddedId以某種方式更清晰地傳遞了密鑰是復合密鑰的信息,並且當合並的pk本身是有意義的實體或在代碼中重用它時,在IMO上是有意義的。
  • @IdClass is useful to specify that some combination of fields is unique but these do not have a special meaning.
  • @IdClass可以指定字段的某些組合是惟一的,但是這些組合沒有特殊的含義。

They also affect the way you write queries (making them more or less verbose):

它們還會影響您編寫查詢的方式(使它們或多或少有些冗長):

  • with IdClass

    與IdClass

    select t.levelStation from Time t
    
  • with EmbeddedId

    與EmbeddedId

    select t.timePK.levelStation from Time t
    

References

  • JPA 1.0 specification
    • Section 2.1.4 "Primary Keys and Entity Identity"
    • 第2.1.4節“主鍵和實體標識”
    • Section 9.1.14 "EmbeddedId Annotation"
    • 部分9.1.14“EmbeddedId注釋”
    • Section 9.1.15 "IdClass Annotation"
    • 部分9.1.15“IdClass注釋”
  • JPA 1.0規范第2.1.4節“主鍵和實體標識”第9.1.14節“EmbeddedId注釋”第9.1.15節“IdClass注釋”

#2


38  

You need to use @EmbeddedId:

您需要使用@EmbeddedId:

@Entity
class Time {
    @EmbeddedId
    TimeId id;

    String src;
    String dst;
    Integer distance;
    Integer price;
}

@Embeddable
class TimeId implements Serializable {
    Integer levelStation;
    Integer confPathID;
}

#3


7  

As I explained in this article, assuming you have the following database tables:

正如我在本文中解釋的,假設您有以下數據庫表:

enter image description here

First, you need to create the @Embeddable holding the composite identifier:

首先,您需要創建可嵌入的復合標識符:

@Embeddable
public class EmployeeId implements Serializable {

    @Column(name = "company_id")
    private Long companyId;

    @Column(name = "employee_number")
    private Long employeeNumber;

    public EmployeeId() {
    }

    public EmployeeId(Long companyId, Long employeeId) {
        this.companyId = companyId;
        this.employeeNumber = employeeId;
    }

    public Long getCompanyId() {
        return companyId;
    }

    public Long getEmployeeNumber() {
        return employeeNumber;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof EmployeeId)) return false;
        EmployeeId that = (EmployeeId) o;
        return Objects.equals(getCompanyId(), that.getCompanyId()) &&
                Objects.equals(getEmployeeNumber(), that.getEmployeeNumber());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getCompanyId(), getEmployeeNumber());
    }
}

With this in place, we can map the Employee entity which uses the composite identifier by annotating it with @EmbeddedId:

有了這一點,我們可以通過@EmbeddedId對使用復合標識符的員工實體進行注釋:

@Entity(name = "Employee")
@Table(name = "employee")
public class Employee {

    @EmbeddedId
    private EmployeeId id;

    private String name;

    public EmployeeId getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

The Phone entity which has a @ManyToOne association to Employee, needs to reference the composite identifier from the parent class via two @JoinColumnmappings:

與Employee有@ManyToOne關聯的電話實體需要通過兩個@JoinColumnmappings:

@Entity(name = "Phone")
@Table(name = "phone")
public class Phone {

    @Id
    @Column(name = "`number`")
    private String number;

    @ManyToOne
    @JoinColumns({
        @JoinColumn(
            name = "company_id",
            referencedColumnName = "company_id"),
        @JoinColumn(
            name = "employee_number",
            referencedColumnName = "employee_number")
    })
    private Employee employee;

    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }
}

For more details, check out this article.

有關更多細節,請參閱本文。

#4


5  

Looks like you are doing this from scratch. Try using available reverse engineering tools like Netbeans Entities from Database to at least get the basics automated (like embedded ids). This can become a huge headache if you have many tables. I suggest avoid reinventing the wheel and use as many tools available as possible to reduce coding to the minimum and most important part, what you intent to do.

看來你是從零開始。嘗試使用可用的逆向工程工具,比如Netbeans實體,從數據庫中獲取基本的自動化(比如嵌入式id)。如果你有很多桌子,這可能會變成一個巨大的頭痛。我建議避免重新發明輪子,使用盡可能多的工具將代碼減少到最小和最重要的部分,即您打算做什么。

#5


5  

The primary key class must define equals and hashCode methods

主鍵類必須定義equals和hashCode方法。

  1. When implementing equals you should use instanceof to allow comparing with subclasses. If Hibernate lazy loads a one to one or many to one relation, you will have a proxy for the class instead of the plain class. A proxy is a subclass. Comparing the class names would fail.
    More technically: You should follow the Liskows Substitution Principle and ignore symmetricity.
  2. 當實現equals時,您應該使用instanceof來允許與子類進行比較。如果Hibernate惰性加載一個或多個到一個關系,那么您將擁有一個類的代理,而不是普通類。代理是一個子類。比較類名將失敗。更嚴格地說,你應該遵循滑橇替代原則,忽略對稱。
  3. The next pitfall is using something like name.equals(that.name) instead of name.equals(that.getName()). The first will fail, if that is a proxy.
  4. 下一個陷阱是使用name.equals(that.name)而不是name.equals(that.getName())。如果是代理,第一個將失敗。

http://www.laliluna.de/jpa-hibernate-guide/ch06s06.html

http://www.laliluna.de/jpa-hibernate-guide/ch06s06.html

#6


3  

Let's take a simple example. Let's say two tables named test and customer are there described as:

讓我們舉一個簡單的例子。假設有兩個名為test和customer的表描述為:

create table test(
  test_id int(11) not null auto_increment,
  primary key(test_id));

create table customer(
  customer_id int(11) not null auto_increment,
  name varchar(50) not null,
  primary key(customer_id));

One more table is there which keeps the track of tests and customer:

還有一個表格記錄了測試和客戶:

create table tests_purchased(
  customer_id int(11) not null,
  test_id int(11) not null,
  created_date datetime not null,
  primary key(customer_id, test_id));

We can see that in the table tests_purchased the primary key is a composite key, so we will use the <composite-id ...>...</composite-id> tag in the hbm.xml mapping file. So the PurchasedTest.hbm.xml will look like:

我們可以在表tests_bought中看到主鍵是復合鍵,因此我們將使用 …標記在hbm中。xml映射文件。所以PurchasedTest.hbm。xml的樣子:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
  <class name="entities.PurchasedTest" table="tests_purchased">

    <composite-id name="purchasedTestId">
      <key-property name="testId" column="TEST_ID" />
      <key-property name="customerId" column="CUSTOMER_ID" />
    </composite-id>

    <property name="purchaseDate" type="timestamp">
      <column name="created_date" />
    </property>

  </class>
</hibernate-mapping>

But it doesn't end here. In Hibernate we use session.load (entityClass, id_type_object) to find and load the entity using primary key. In case of composite keys, the ID object should be a separate ID class (in above case a PurchasedTestId class) which just declares the primary key attributes like below:

但這還沒完。在Hibernate中,我們使用會話。使用主鍵加載(entityClass、id_type_object)查找和加載實體。對於組合鍵,ID對象應該是一個單獨的ID類(在上面的例子中是一個purchasing睾丸類),它只聲明主鍵屬性,如下所示:

import java.io.Serializable;

public class PurchasedTestId implements Serializable {
  private Long testId;
  private Long customerId;

  // an easy initializing constructor
  public PurchasedTestId(Long testId, Long customerId) {
    this.testId = testId;
    this.customerId = customerId;
  }

  public Long getTestId() {
    return testId;
  }

  public void setTestId(Long testId) {
    this.testId = testId;
  }

  public Long getCustomerId() {
    return customerId;
  }

  public void setCustomerId(Long customerId) {
    this.customerId = customerId;
  }

  @Override
  public boolean equals(Object arg0) {
    if(arg0 == null) return false;
    if(!(arg0 instanceof PurchasedTestId)) return false;
    PurchasedTestId arg1 = (PurchasedTestId) arg0;
    return (this.testId.longValue() == arg1.getTestId().longValue()) &&
           (this.customerId.longValue() == arg1.getCustomerId().longValue());
  }

  @Override
  public int hashCode() {
    int hsCode;
    hsCode = testId.hashCode();
    hsCode = 19 * hsCode+ customerId.hashCode();
    return hsCode;
  }
}

Important point is that we also implement the two functions hashCode() and equals() as Hibernate relies on them.

重要的是,我們還實現了兩個函數hashCode()和equals(),因為Hibernate依賴它們。

#7


2  

Another option is to map is as a Map of composite elements in the ConfPath table.

另一種選擇是映射作為ConfPath表中復合元素的映射。

This mapping would benefit from an index on (ConfPathID,levelStation) though.

這個映射將從一個索引(ConfPathID,levelStation)中獲益。

public class ConfPath {
    private Map<Long,Time> timeForLevelStation = new HashMap<Long,Time>();

    public Time getTime(long levelStation) {
        return timeForLevelStation.get(levelStation);
    }

    public void putTime(long levelStation, Time newValue) {
        timeForLevelStation.put(levelStation, newValue);
    }
}

public class Time {
    String src;
    String dst;
    long distance;
    long price;

    public long getDistance() {
        return distance;
    }

    public void setDistance(long distance) {
        this.distance = distance;
    }

    public String getDst() {
        return dst;
    }

    public void setDst(String dst) {
        this.dst = dst;
    }

    public long getPrice() {
        return price;
    }

    public void setPrice(long price) {
        this.price = price;
    }

    public String getSrc() {
        return src;
    }

    public void setSrc(String src) {
        this.src = src;
    }
}

Mapping:

映射:

<class name="ConfPath" table="ConfPath">
    <id column="ID" name="id">
        <generator class="native"/>
    </id>
    <map cascade="all-delete-orphan" name="values" table="example"
            lazy="extra">
        <key column="ConfPathID"/>
        <map-key type="long" column="levelStation"/>
        <composite-element class="Time">
            <property name="src" column="src" type="string" length="100"/>
            <property name="dst" column="dst" type="string" length="100"/>
            <property name="distance" column="distance"/>
            <property name="price" column="price"/>
        </composite-element>
    </map>
</class>

#8


0  

Using hbm.xml

使用hbm.xml

    <composite-id>

        <!--<key-many-to-one name="productId" class="databaselayer.users.UserDB" column="user_name"/>-->
        <key-property name="productId" column="PRODUCT_Product_ID" type="int"/>
        <key-property name="categoryId" column="categories_id" type="int" />
    </composite-id>  

Using Annotation

使用注釋

Composite Key Class

組合鍵類

public  class PK implements Serializable{
    private int PRODUCT_Product_ID ;    
    private int categories_id ;

    public PK(int productId, int categoryId) {
        this.PRODUCT_Product_ID = productId;
        this.categories_id = categoryId;
    }

    public int getPRODUCT_Product_ID() {
        return PRODUCT_Product_ID;
    }

    public void setPRODUCT_Product_ID(int PRODUCT_Product_ID) {
        this.PRODUCT_Product_ID = PRODUCT_Product_ID;
    }

    public int getCategories_id() {
        return categories_id;
    }

    public void setCategories_id(int categories_id) {
        this.categories_id = categories_id;
    }

    private PK() { }

    @Override
    public boolean equals(Object o) {
        if ( this == o ) {
            return true;
        }

        if ( o == null || getClass() != o.getClass() ) {
            return false;
        }

        PK pk = (PK) o;
        return Objects.equals(PRODUCT_Product_ID, pk.PRODUCT_Product_ID ) &&
                Objects.equals(categories_id, pk.categories_id );
    }

    @Override
    public int hashCode() {
        return Objects.hash(PRODUCT_Product_ID, categories_id );
    }
}

Entity Class

實體類

@Entity(name = "product_category")
@IdClass( PK.class )
public  class ProductCategory implements Serializable {
    @Id    
    private int PRODUCT_Product_ID ;   

    @Id 
    private int categories_id ;

    public ProductCategory(int productId, int categoryId) {
        this.PRODUCT_Product_ID = productId ;
        this.categories_id = categoryId;
    }

    public ProductCategory() { }

    public int getPRODUCT_Product_ID() {
        return PRODUCT_Product_ID;
    }

    public void setPRODUCT_Product_ID(int PRODUCT_Product_ID) {
        this.PRODUCT_Product_ID = PRODUCT_Product_ID;
    }

    public int getCategories_id() {
        return categories_id;
    }

    public void setCategories_id(int categories_id) {
        this.categories_id = categories_id;
    }

    public void setId(PK id) {
        this.PRODUCT_Product_ID = id.getPRODUCT_Product_ID();
        this.categories_id = id.getCategories_id();
    }

    public PK getId() {
        return new PK(
            PRODUCT_Product_ID,
            categories_id
        );
    }    
}

注意!

本站翻译的文章,版权归属于本站,未经许可禁止转摘,转摘请注明本文地址:https://www.itdaan.com/blog/2010/08/27/72107cbc661d917575a4adde2d588e09.html



 
粤ICP备14056181号  © 2014-2021 ITdaan.com