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;
}
- Here,
id
is the primary key of theCustomer
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
andcustomerId
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;
}
-
customerNumber
andcheckNumber
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);
}
}
- This class must implement
Serializable
. -
equals()
andhashCode()
ensure that JPA can properly compare composite keys.
3. How @Id
Affects Persistence
JPA and Hibernate use the @Id
field(s) in several ways:
- Uniqueness Enforcement: Ensures that each record in the table has a unique primary key.
-
Retrieving Entities: Used to fetch entities with
EntityManager.find()
orSpring Data JPA findById()
. - Updating & Deleting: JPA uses the primary key for identifying rows to update or delete.
-
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()
andhashCode()
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;
}
And in the entity:
@EmbeddedId
private PaymentId id;
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 implementSerializable
and properly defineequals()
andhashCode()
. -
@EmbeddedId
is an alternative approach for composite keys.