hiberante-005: @Id

Hunor Vadasz-Perhat - Feb 7 - - Dev Community

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:

@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

@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)

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:

@Embeddable
public class PaymentId implements Serializable {
    private Long customer;
    private String checkNumber;
}
Enter fullscreen mode Exit fullscreen mode

And in the entity:

@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.
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .