Composite Primary Keys in JPA – JPA中的复合主键

最后修改: 2019年 5月 28日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this tutorial, we’ll learn about Composite Primary Keys and the corresponding annotations in JPA.

在本教程中,我们将学习JPA中的复合主键和相应的注释。

2. Composite Primary Keys

2.复合主键

A composite primary key, also called a composite key, is a combination of two or more columns to form a primary key for a table.

复合主键,也被称为复合键,是由两个或多个列组成的表的主键。

In JPA, we have two options to define the composite keys: the @IdClass and @EmbeddedId annotations.

在JPA中,我们有两个选项来定义复合键:@IdClass@EmbeddedId注释。

In order to define the composite primary keys, we should follow some rules:

为了定义复合主键,我们应该遵循一些规则:

  • The composite primary key class must be public.
  • It must have a no-arg constructor.
  • It must define the equals() and hashCode() methods.
  • It must be Serializable.

3. The IdClass Annotation

3.IdClass注释

Let’s say we have a table called Account and it has two columns, accountNumber and accountType, that form the composite key. Now we have to map it in JPA.

假设我们有一个名为Account的表,它有两列,accountNumberaccountType,构成复合键。现在我们必须在JPA中对其进行映射。

As per the JPA specification, let’s create an AccountId class with these primary key fields:

根据JPA规范,让我们创建一个带有这些主键字段的AccountId类。

public class AccountId implements Serializable {
    private String accountNumber;

    private String accountType;

    // default constructor

    public AccountId(String accountNumber, String accountType) {
        this.accountNumber = accountNumber;
        this.accountType = accountType;
    }

    // equals() and hashCode()
}

Next let’s associate the AccountId class with the entity Account.

接下来让我们将AccountId类与实体Account联系起来。

In order to do that, we need to annotate the entity with the @IdClass annotation. We must also declare the fields from the AccountId class in the entity Account and annotate them with @Id:

为了做到这一点,我们需要用@IdClass 注解来注释该实体。我们还必须在实体Account中声明AccountId类中的字段,并用@Id注释它们。

@Entity
@IdClass(AccountId.class)
public class Account {
    @Id
    private String accountNumber;

    @Id
    private String accountType;

    // other fields, getters and setters
}

4. The EmbeddedId Annotation

4.EmbeddedId注释

@EmbeddedId is an alternative to the @IdClass annotation.

@EmbeddedId@IdClass注解的一个替代。

Let’s consider another example where we have to persist some information of a Book, with title and language as the primary key fields.

让我们考虑另一个例子,我们必须持久化一个的一些信息,标题语言是主键字段。

In this case, the primary key class, BookId, must be annotated with @Embeddable:

在这种情况下,主键类,BookId,必须用@Embeddable来注释。

@Embeddable
public class BookId implements Serializable {
    private String title;
    private String language;

    // default constructor

    public BookId(String title, String language) {
        this.title = title;
        this.language = language;
    }

    // getters, equals() and hashCode() methods
}

Then we need to embed this class in the Book entity using @EmbeddedId:

然后我们需要在Book实体中使用@EmbeddedId嵌入这个类。

@Entity
public class Book {
    @EmbeddedId
    private BookId bookId;

    // constructors, other fields, getters and setters
}

5. @IdClass vs @EmbeddedId

5.@IdClass vs @EmbeddedId

As we can see, the difference on the surface between these two is that with @IdClass we had to specify the columns twice, once in AccountId and again in Account; however, with @EmbeddedId we didn’t.

正如我们所看到的,这两者之间表面上的区别是,使用@IdClass时,我们必须指定两次列,一次在AccountId中,另一次在Account中;然而,使用@EmbeddedId时,我们没有。

There are some other trade offs though.

但也有一些其他的交换条件。

For example, these different structures affect the JPQL queries that we write.

例如,这些不同的结构会影响我们编写的JPQL查询。

With @IdClass, the query is a bit simpler:

有了@IdClass,查询就比较简单了。

SELECT account.accountNumber FROM Account account

With @EmbeddedId, we have to do one extra traversal:

使用@EmbeddedId,我们必须做一个额外的遍历。

SELECT book.bookId.title FROM Book book

Also, @IdClass can be quite useful in places where we are using a composite key class that we can’t modify.

另外,@IdClass在我们使用一个不能修改的复合键类的地方,可以相当有用。

If we’re going to access parts of the composite key individually, we can make use of @IdClass, but in places where we frequently use the complete identifier as an object, @EmbeddedId is preferred.

如果我们要单独访问复合键的部分,我们可以使用@IdClass,但是在我们经常使用完整标识符作为对象的地方,@EmbeddedId是首选。

6. Conclusion

6.结语

In this brief article, we explored composite primary keys in JPA.

在这篇简短的文章中,我们探讨了JPA中的复合主键。

As always, the complete code for this article can be found over on Github.

一如既往,本文的完整代码可以在Github上找到