hiberante-005: @Id

Understanding @Id in Depth

In Java Persistence API (JPA), the @Id annotation is used to mark a field as the primary key of an entity. This primary key uniquely identifies each record in the corresponding database table. Below is a detailed breakdown of the purpose and functionality of @Id.


1. Primary Key in JPA

The primary key is a fundamental concept in relational databases and is crucial for ensuring the integrity and uniqueness of records. In JPA, marking a field with @Id makes it the unique identifier of the entity.

Basic Usage of @Id

When a field is annotated with @Id, JPA recognizes it as the primary key of the entity and uses it for:

  • Identifying entities uniquely
  • Performing lookups (i.e., finding a specific entity in the database)
  • Managing entity persistence and relationships
  • Ensuring that no two entities share the same primary key

Example of a Simple Primary Key:

<span>@Entity</span>
<span>@Table</span><span>(</span><span>name</span> <span>=</span> <span>"customers"</span><span>)</span>
<span>public</span> <span>class</span> <span>Customer</span> <span>{</span>
<span>@Id</span>
<span>@GeneratedValue</span><span>(</span><span>strategy</span> <span>=</span> <span>GenerationType</span><span>.</span><span>IDENTITY</span><span>)</span>
<span>private</span> <span>Long</span> <span>id</span><span>;</span>
<span>private</span> <span>String</span> <span>name</span><span>;</span>
<span>}</span>
<span>@Entity</span>
<span>@Table</span><span>(</span><span>name</span> <span>=</span> <span>"customers"</span><span>)</span>
<span>public</span> <span>class</span> <span>Customer</span> <span>{</span>
    <span>@Id</span>
    <span>@GeneratedValue</span><span>(</span><span>strategy</span> <span>=</span> <span>GenerationType</span><span>.</span><span>IDENTITY</span><span>)</span>
    <span>private</span> <span>Long</span> <span>id</span><span>;</span>

    <span>private</span> <span>String</span> <span>name</span><span>;</span>
<span>}</span>
@Entity @Table(name = "customers") public class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; }

Enter fullscreen mode Exit fullscreen mode

  • Here, id is the primary key of the Customer entity.
  • The @GeneratedValue annotation tells JPA to auto-generate values (e.g., using a database sequence or auto-increment).

2. @Id in Composite Keys

A composite key is when two or more columns together form a unique identifier for an entity. In your provided code, the Payment entity uses a composite key, meaning that a combination of customerNumber and checkNumber uniquely identifies each payment.

Why Use a Composite Key?

  • Some business rules require composite keys (e.g., an order might be uniquely identified by orderId and customerId together).
  • Helps maintain data integrity when no single column can uniquely identify a record.

How @Id Works in Composite Keys

  • Since a composite key consists of multiple fields, each of them must be marked with @Id.
  • The @IdClass(PaymentId.class) annotation specifies an external primary key class (PaymentId) that contains the logic for defining equality and hashing.

Example: Composite Key Implementation

<span>@Entity</span>
<span>@Table</span><span>(</span><span>name</span> <span>=</span> <span>"payments"</span><span>)</span>
<span>@IdClass</span><span>(</span><span>PaymentId</span><span>.</span><span>class</span><span>)</span> <span>// Composite Key</span>
<span>public</span> <span>class</span> <span>Payment</span> <span>{</span>
<span>@Id</span>
<span>@ManyToOne</span>
<span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"customerNumber"</span><span>,</span> <span>referencedColumnName</span> <span>=</span> <span>"customerNumber"</span><span>)</span>
<span>private</span> <span>Customer</span> <span>customer</span><span>;</span> <span>// FK to Customers</span>
<span>@Id</span>
<span>@Column</span><span>(</span><span>name</span> <span>=</span> <span>"checkNumber"</span><span>,</span> <span>length</span> <span>=</span> <span>50</span><span>)</span>
<span>private</span> <span>String</span> <span>checkNumber</span><span>;</span>
<span>@Column</span><span>(</span><span>name</span> <span>=</span> <span>"paymentDate"</span><span>,</span> <span>nullable</span> <span>=</span> <span>false</span><span>)</span>
<span>private</span> <span>LocalDate</span> <span>paymentDate</span><span>;</span>
<span>@Column</span><span>(</span><span>name</span> <span>=</span> <span>"amount"</span><span>,</span> <span>precision</span> <span>=</span> <span>10</span><span>,</span> <span>scale</span> <span>=</span> <span>2</span><span>,</span> <span>nullable</span> <span>=</span> <span>false</span><span>)</span>
<span>private</span> <span>BigDecimal</span> <span>amount</span><span>;</span>
<span>}</span>
<span>@Entity</span>
<span>@Table</span><span>(</span><span>name</span> <span>=</span> <span>"payments"</span><span>)</span>
<span>@IdClass</span><span>(</span><span>PaymentId</span><span>.</span><span>class</span><span>)</span>  <span>// Composite Key</span>
<span>public</span> <span>class</span> <span>Payment</span> <span>{</span>
    <span>@Id</span>
    <span>@ManyToOne</span>
    <span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"customerNumber"</span><span>,</span> <span>referencedColumnName</span> <span>=</span> <span>"customerNumber"</span><span>)</span>
    <span>private</span> <span>Customer</span> <span>customer</span><span>;</span>  <span>// FK to Customers</span>

    <span>@Id</span>
    <span>@Column</span><span>(</span><span>name</span> <span>=</span> <span>"checkNumber"</span><span>,</span> <span>length</span> <span>=</span> <span>50</span><span>)</span>
    <span>private</span> <span>String</span> <span>checkNumber</span><span>;</span>

    <span>@Column</span><span>(</span><span>name</span> <span>=</span> <span>"paymentDate"</span><span>,</span> <span>nullable</span> <span>=</span> <span>false</span><span>)</span>
    <span>private</span> <span>LocalDate</span> <span>paymentDate</span><span>;</span>

    <span>@Column</span><span>(</span><span>name</span> <span>=</span> <span>"amount"</span><span>,</span> <span>precision</span> <span>=</span> <span>10</span><span>,</span> <span>scale</span> <span>=</span> <span>2</span><span>,</span> <span>nullable</span> <span>=</span> <span>false</span><span>)</span>
    <span>private</span> <span>BigDecimal</span> <span>amount</span><span>;</span>
<span>}</span>
@Entity @Table(name = "payments") @IdClass(PaymentId.class) // Composite Key public class Payment { @Id @ManyToOne @JoinColumn(name = "customerNumber", referencedColumnName = "customerNumber") private Customer customer; // FK to Customers @Id @Column(name = "checkNumber", length = 50) private String checkNumber; @Column(name = "paymentDate", nullable = false) private LocalDate paymentDate; @Column(name = "amount", precision = 10, scale = 2, nullable = false) private BigDecimal amount; }

Enter fullscreen mode Exit fullscreen mode

  • customerNumber and checkNumber together act as the primary key.
  • The @IdClass(PaymentId.class) annotation tells JPA that this entity uses a composite key defined in a separate class (PaymentId).

The Composite Key Class (PaymentId)

<span>import</span> <span>java.io.Serializable</span><span>;</span>
<span>import</span> <span>java.util.Objects</span><span>;</span>
<span>public</span> <span>class</span> <span>PaymentId</span> <span>implements</span> <span>Serializable</span> <span>{</span>
<span>private</span> <span>Long</span> <span>customer</span><span>;</span> <span>// Must match entity field types</span>
<span>private</span> <span>String</span> <span>checkNumber</span><span>;</span>
<span>public</span> <span>PaymentId</span><span>()</span> <span>{}</span>
<span>public</span> <span>PaymentId</span><span>(</span><span>Long</span> <span>customer</span><span>,</span> <span>String</span> <span>checkNumber</span><span>)</span> <span>{</span>
<span>this</span><span>.</span><span>customer</span> <span>=</span> <span>customer</span><span>;</span>
<span>this</span><span>.</span><span>checkNumber</span> <span>=</span> <span>checkNumber</span><span>;</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>boolean</span> <span>equals</span><span>(</span><span>Object</span> <span>o</span><span>)</span> <span>{</span>
<span>if</span> <span>(</span><span>this</span> <span>==</span> <span>o</span><span>)</span> <span>return</span> <span>true</span><span>;</span>
<span>if</span> <span>(</span><span>o</span> <span>==</span> <span>null</span> <span>||</span> <span>getClass</span><span>()</span> <span>!=</span> <span>o</span><span>.</span><span>getClass</span><span>())</span> <span>return</span> <span>false</span><span>;</span>
<span>PaymentId</span> <span>paymentId</span> <span>=</span> <span>(</span><span>PaymentId</span><span>)</span> <span>o</span><span>;</span>
<span>return</span> <span>Objects</span><span>.</span><span>equals</span><span>(</span><span>customer</span><span>,</span> <span>paymentId</span><span>.</span><span>customer</span><span>)</span> <span>&&</span>
<span>Objects</span><span>.</span><span>equals</span><span>(</span><span>checkNumber</span><span>,</span> <span>paymentId</span><span>.</span><span>checkNumber</span><span>);</span>
<span>}</span>
<span>@Override</span>
<span>public</span> <span>int</span> <span>hashCode</span><span>()</span> <span>{</span>
<span>return</span> <span>Objects</span><span>.</span><span>hash</span><span>(</span><span>customer</span><span>,</span> <span>checkNumber</span><span>);</span>
<span>}</span>
<span>}</span>
<span>import</span> <span>java.io.Serializable</span><span>;</span>
<span>import</span> <span>java.util.Objects</span><span>;</span>

<span>public</span> <span>class</span> <span>PaymentId</span> <span>implements</span> <span>Serializable</span> <span>{</span>
    <span>private</span> <span>Long</span> <span>customer</span><span>;</span>  <span>// Must match entity field types</span>
    <span>private</span> <span>String</span> <span>checkNumber</span><span>;</span>

    <span>public</span> <span>PaymentId</span><span>()</span> <span>{}</span>

    <span>public</span> <span>PaymentId</span><span>(</span><span>Long</span> <span>customer</span><span>,</span> <span>String</span> <span>checkNumber</span><span>)</span> <span>{</span>
        <span>this</span><span>.</span><span>customer</span> <span>=</span> <span>customer</span><span>;</span>
        <span>this</span><span>.</span><span>checkNumber</span> <span>=</span> <span>checkNumber</span><span>;</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>boolean</span> <span>equals</span><span>(</span><span>Object</span> <span>o</span><span>)</span> <span>{</span>
        <span>if</span> <span>(</span><span>this</span> <span>==</span> <span>o</span><span>)</span> <span>return</span> <span>true</span><span>;</span>
        <span>if</span> <span>(</span><span>o</span> <span>==</span> <span>null</span> <span>||</span> <span>getClass</span><span>()</span> <span>!=</span> <span>o</span><span>.</span><span>getClass</span><span>())</span> <span>return</span> <span>false</span><span>;</span>
        <span>PaymentId</span> <span>paymentId</span> <span>=</span> <span>(</span><span>PaymentId</span><span>)</span> <span>o</span><span>;</span>
        <span>return</span> <span>Objects</span><span>.</span><span>equals</span><span>(</span><span>customer</span><span>,</span> <span>paymentId</span><span>.</span><span>customer</span><span>)</span> <span>&&</span>
               <span>Objects</span><span>.</span><span>equals</span><span>(</span><span>checkNumber</span><span>,</span> <span>paymentId</span><span>.</span><span>checkNumber</span><span>);</span>
    <span>}</span>

    <span>@Override</span>
    <span>public</span> <span>int</span> <span>hashCode</span><span>()</span> <span>{</span>
        <span>return</span> <span>Objects</span><span>.</span><span>hash</span><span>(</span><span>customer</span><span>,</span> <span>checkNumber</span><span>);</span>
    <span>}</span>
<span>}</span>
import java.io.Serializable; import java.util.Objects; public class PaymentId implements Serializable { private Long customer; // Must match entity field types private String checkNumber; public PaymentId() {} public PaymentId(Long customer, String checkNumber) { this.customer = customer; this.checkNumber = checkNumber; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PaymentId paymentId = (PaymentId) o; return Objects.equals(customer, paymentId.customer) && Objects.equals(checkNumber, paymentId.checkNumber); } @Override public int hashCode() { return Objects.hash(customer, checkNumber); } }

Enter fullscreen mode Exit fullscreen mode

  • This class must implement Serializable.
  • equals() and hashCode() ensure that JPA can properly compare composite keys.

3. How @Id Affects Persistence

JPA and Hibernate use the @Id field(s) in several ways:

  1. Uniqueness Enforcement: Ensures that each record in the table has a unique primary key.
  2. Retrieving Entities: Used to fetch entities with EntityManager.find() or Spring Data JPA findById().
  3. Updating & Deleting: JPA uses the primary key for identifying rows to update or delete.
  4. Relationships & Foreign Keys: In many-to-one relationships, the @Id field(s) can serve as foreign keys.

4. Best Practices When Using @Id

  • Prefer a Single Primary Key When Possible: Composite keys add complexity and can make querying more difficult.
  • Ensure @IdClass Matches Entity Fields: The @IdClass fields must match those in the entity (same name and type).
  • Implement equals() and hashCode() Correctly: Composite key classes must override these methods properly.
  • Use @GeneratedValue for Auto-Generation (when applicable): Helps avoid manually setting IDs for new records.
  • Consider Using @EmbeddedId Instead of @IdClass: @EmbeddedId is another way to define composite keys.

Example:

<span>@Embeddable</span>
<span>public</span> <span>class</span> <span>PaymentId</span> <span>implements</span> <span>Serializable</span> <span>{</span>
<span>private</span> <span>Long</span> <span>customer</span><span>;</span>
<span>private</span> <span>String</span> <span>checkNumber</span><span>;</span>
<span>}</span>
<span>@Embeddable</span>
<span>public</span> <span>class</span> <span>PaymentId</span> <span>implements</span> <span>Serializable</span> <span>{</span>
    <span>private</span> <span>Long</span> <span>customer</span><span>;</span>
    <span>private</span> <span>String</span> <span>checkNumber</span><span>;</span>
<span>}</span>
@Embeddable public class PaymentId implements Serializable { private Long customer; private String checkNumber; }

Enter fullscreen mode Exit fullscreen mode

And in the entity:

<span>@EmbeddedId</span>
<span>private</span> <span>PaymentId</span> <span>id</span><span>;</span>
<span>@EmbeddedId</span>
<span>private</span> <span>PaymentId</span> <span>id</span><span>;</span>
@EmbeddedId private PaymentId id;

Enter fullscreen mode Exit fullscreen mode

This avoids having to use @IdClass.


Summary

  • @Id marks a field as the primary key of an entity.
  • It ensures uniqueness and is used for querying, updating, and deleting records.
  • When using composite keys, multiple fields are marked with @Id, and an external key class (@IdClass) is required.
  • @IdClass must implement Serializable and properly define equals() and hashCode().
  • @EmbeddedId is an alternative approach for composite keys.

原文链接:hiberante-005: @Id

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
Pain changes people. However, love will finally guide them back.
伤痛会改变一个人,但爱最终总会让你找回最初的自己
评论 抢沙发

请登录后发表评论

    暂无评论内容