DEV Community

Hunor Vadasz-Perhat
Hunor Vadasz-Perhat

Posted on

hibernate-004: PaymentId.class

PaymentId Class (Best Practices)

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 by JPA)
    public PaymentId() {}

    // Parameterized constructor for convenience
    public PaymentId(Integer customer, String checkNumber) {
        this.customer = customer;
        this.checkNumber = checkNumber;
    }

    // Getters and Setters (JPA needs these)
    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() to compare objects logically
    @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 hashCode() to ensure correct behavior in HashMap, HashSet, etc.
    @Override
    public int hashCode() {
        return Objects.hash(customer, checkNumber);
    }
}
Enter fullscreen mode Exit fullscreen mode

Detailed Explanation

1. Why Serializable?

  • The PaymentId class must implement Serializable, as JPA uses serialization internally when handling primary keys in @IdClass.
  • Without Serializable, JPA may fail at runtime.

2. Why a No-Arg Constructor?

  • JPA requires a no-argument constructor for entity instantiation.
  • If not explicitly defined, JPA might throw errors when retrieving entities.

3. Why Getters and Setters?

  • Although @IdClass does not require getters and setters explicitly, it's a best practice because:
    • Some frameworks (e.g., Hibernate) may require them for proxies.
    • It ensures compatibility with JavaBeans conventions.

4. Why Override equals() and hashCode()?

  • These methods ensure proper object equality comparison, which is crucial when:
    • Storing objects in a Set (avoid duplicate keys).
    • Fetching entities with composite keys.
    • Avoiding inconsistencies when managing entity relationships.

How JPA Uses PaymentId

  1. When creating a Payment entity, JPA will check if an entity with the same PaymentId already exists before inserting a new one.
  2. When fetching a Payment entity, JPA reconstructs the key using PaymentId and retrieves the correct entry.
  3. When deleting a Payment entity, JPA uses PaymentId to locate the record.

Example: Using PaymentId to Find an Entity

@EntityManager entityManager = ...;
PaymentId paymentId = new PaymentId(103, "CHK123456");  // Composite Key
Payment payment = entityManager.find(Payment.class, paymentId);
Enter fullscreen mode Exit fullscreen mode
  • Here, JPA automatically maps the customer and checkNumber fields from PaymentId to the corresponding @Id fields in Payment.

Alternative Approach: @EmbeddedId

Another way to define a composite key is using @EmbeddedId, which embeds the primary key inside the entity.

With @EmbeddedId:

@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, modify the Payment entity:

@Entity
@Table(name = "payments")
public class Payment {
    @EmbeddedId
    private PaymentId id;
}
Enter fullscreen mode Exit fullscreen mode
  • Key Difference: @EmbeddedId treats the key as an object instead of separate fields in the entity.

Final Thoughts

  • Always override equals() and hashCode() to ensure that entity comparisons work correctly.
  • Consider @EmbeddedId if you prefer treating the composite key as an object instead of separate fields in the entity.

This ensures your Payment entity functions correctly within a Spring Boot + Hibernate environment. 🚀

Top comments (0)