DEV Community

Hunor Vadasz-Perhat
Hunor Vadasz-Perhat

Posted on

hibernate-003: @IdClass(PaymentId.class)

The annotation @IdClass(PaymentId.class) is used in JPA (Java Persistence API) to define a composite primary key for the Payment entity. A composite key consists of multiple fields instead of a single primary key field.

1. Purpose of @IdClass

@IdClass specifies that the primary key of the Payment entity consists of multiple attributes. Instead of defining a single primary key column, we define multiple fields as the primary key. This is necessary when an entity does not have a single natural unique identifier but instead is uniquely identified by a combination of multiple fields.

In this case:

  • The Payment entity has a composite key consisting of:
    • customer: A reference to the Customer entity (Foreign Key)
    • checkNumber: A String representing the check number

The combination of customer and checkNumber uniquely identifies each payment record.

2. How @IdClass Works

  • @IdClass(PaymentId.class) tells JPA that the Payment entity will use the PaymentId class as its composite key.
  • The PaymentId class must:
    • Be a JavaBean (i.e., have a no-arg constructor and getters/setters).
    • Implement Serializable.
    • Override equals() and hashCode() to ensure correct entity comparison.
    • Have fields matching the primary key fields of the Payment entity.

3. Structure of PaymentId Class

The corresponding PaymentId class should be structured like this:

import java.io.Serializable;
import java.util.Objects;

public class PaymentId implements Serializable {
    private Integer customer;  // Must match the type of Payment.customer (Customer's ID)
    private String checkNumber;

    // Default constructor (required for serialization)
    public PaymentId() {}

    public PaymentId(Integer customer, String checkNumber) {
        this.customer = customer;
        this.checkNumber = checkNumber;
    }

    // Getters and Setters
    public Integer getCustomer() { return customer; }
    public void setCustomer(Integer customer) { this.customer = customer; }

    public String getCheckNumber() { return checkNumber; }
    public void setCheckNumber(String checkNumber) { this.checkNumber = checkNumber; }

    // Override equals() and hashCode() for proper comparison in JPA
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PaymentId that = (PaymentId) o;
        return Objects.equals(customer, that.customer) && Objects.equals(checkNumber, that.checkNumber);
    }

    @Override
    public int hashCode() {
        return Objects.hash(customer, checkNumber);
    }
}
Enter fullscreen mode Exit fullscreen mode

4. How JPA Uses @IdClass

  • When persisting a Payment entity, JPA uses the PaymentId class to represent its primary key.
  • JPA checks whether an entity with the same composite key already exists in the database.
  • When fetching a Payment entity, JPA reconstructs the composite key using the fields defined in PaymentId.

5. Alternative Approach: Using @EmbeddedId

Instead of @IdClass, another way to define a composite key is with @EmbeddedId, which embeds the key fields directly inside the entity:

@Embeddable
public class PaymentId implements Serializable {
    @ManyToOne
    @JoinColumn(name = "customerNumber")
    private Customer customer;

    @Column(name = "checkNumber", length = 50)
    private String checkNumber;

    // Constructors, equals(), hashCode(), Getters, and Setters
}
Enter fullscreen mode Exit fullscreen mode

Then, the Payment entity would use:

@EmbeddedId
private PaymentId id;
Enter fullscreen mode Exit fullscreen mode

The main difference is that @EmbeddedId allows treating the composite key as a single embedded object, while @IdClass keeps the fields separate.

6. Why Use @IdClass?

  • When the composite key fields exist directly in the entity class.
  • When you want to maintain clear separation between the primary key definition and the entity.
  • When working with legacy databases that already have composite keys defined.

Conclusion

@IdClass(PaymentId.class) tells JPA that the primary key consists of multiple fields (customer and checkNumber). The PaymentId class serves as the identifier representation, allowing JPA to correctly handle composite keys in entity operations like persistence, retrieval, and comparison.

Top comments (0)