PaymentId
Class (Best Practices)
<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>Integer</span> <span>customer</span><span>;</span> <span>// Must match the type of Payment.customer (Customer's ID)</span><span>private</span> <span>String</span> <span>checkNumber</span><span>;</span><span>// Default constructor (required by JPA)</span><span>public</span> <span>PaymentId</span><span>()</span> <span>{}</span><span>// Parameterized constructor for convenience</span><span>public</span> <span>PaymentId</span><span>(</span><span>Integer</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>// Getters and Setters (JPA needs these)</span><span>public</span> <span>Integer</span> <span>getCustomer</span><span>()</span> <span>{</span> <span>return</span> <span>customer</span><span>;</span> <span>}</span><span>public</span> <span>void</span> <span>setCustomer</span><span>(</span><span>Integer</span> <span>customer</span><span>)</span> <span>{</span> <span>this</span><span>.</span><span>customer</span> <span>=</span> <span>customer</span><span>;</span> <span>}</span><span>public</span> <span>String</span> <span>getCheckNumber</span><span>()</span> <span>{</span> <span>return</span> <span>checkNumber</span><span>;</span> <span>}</span><span>public</span> <span>void</span> <span>setCheckNumber</span><span>(</span><span>String</span> <span>checkNumber</span><span>)</span> <span>{</span> <span>this</span><span>.</span><span>checkNumber</span> <span>=</span> <span>checkNumber</span><span>;</span> <span>}</span><span>// Override equals() to compare objects logically</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>that</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>that</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>that</span><span>.</span><span>checkNumber</span><span>);</span><span>}</span><span>// Override hashCode() to ensure correct behavior in HashMap, HashSet, etc.</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>Integer</span> <span>customer</span><span>;</span> <span>// Must match the type of Payment.customer (Customer's ID)</span> <span>private</span> <span>String</span> <span>checkNumber</span><span>;</span> <span>// Default constructor (required by JPA)</span> <span>public</span> <span>PaymentId</span><span>()</span> <span>{}</span> <span>// Parameterized constructor for convenience</span> <span>public</span> <span>PaymentId</span><span>(</span><span>Integer</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>// Getters and Setters (JPA needs these)</span> <span>public</span> <span>Integer</span> <span>getCustomer</span><span>()</span> <span>{</span> <span>return</span> <span>customer</span><span>;</span> <span>}</span> <span>public</span> <span>void</span> <span>setCustomer</span><span>(</span><span>Integer</span> <span>customer</span><span>)</span> <span>{</span> <span>this</span><span>.</span><span>customer</span> <span>=</span> <span>customer</span><span>;</span> <span>}</span> <span>public</span> <span>String</span> <span>getCheckNumber</span><span>()</span> <span>{</span> <span>return</span> <span>checkNumber</span><span>;</span> <span>}</span> <span>public</span> <span>void</span> <span>setCheckNumber</span><span>(</span><span>String</span> <span>checkNumber</span><span>)</span> <span>{</span> <span>this</span><span>.</span><span>checkNumber</span> <span>=</span> <span>checkNumber</span><span>;</span> <span>}</span> <span>// Override equals() to compare objects logically</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>that</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>that</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>that</span><span>.</span><span>checkNumber</span><span>);</span> <span>}</span> <span>// Override hashCode() to ensure correct behavior in HashMap, HashSet, etc.</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 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 implementSerializable
, 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.
- Storing objects in a
How JPA Uses PaymentId
- When creating a
Payment
entity, JPA will check if an entity with the samePaymentId
already exists before inserting a new one. - When fetching a
Payment
entity, JPA reconstructs the key usingPaymentId
and retrieves the correct entry. - When deleting a
Payment
entity, JPA usesPaymentId
to locate the record.
Example: Using PaymentId
to Find an Entity
<span>@EntityManager</span> <span>entityManager</span> <span>=</span> <span>...;</span><span>PaymentId</span> <span>paymentId</span> <span>=</span> <span>new</span> <span>PaymentId</span><span>(</span><span>103</span><span>,</span> <span>"CHK123456"</span><span>);</span> <span>// Composite Key</span><span>Payment</span> <span>payment</span> <span>=</span> <span>entityManager</span><span>.</span><span>find</span><span>(</span><span>Payment</span><span>.</span><span>class</span><span>,</span> <span>paymentId</span><span>);</span><span>@EntityManager</span> <span>entityManager</span> <span>=</span> <span>...;</span> <span>PaymentId</span> <span>paymentId</span> <span>=</span> <span>new</span> <span>PaymentId</span><span>(</span><span>103</span><span>,</span> <span>"CHK123456"</span><span>);</span> <span>// Composite Key</span> <span>Payment</span> <span>payment</span> <span>=</span> <span>entityManager</span><span>.</span><span>find</span><span>(</span><span>Payment</span><span>.</span><span>class</span><span>,</span> <span>paymentId</span><span>);</span>@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
andcheckNumber
fields fromPaymentId
to the corresponding@Id
fields inPayment
.
Alternative Approach: @EmbeddedId
Another way to define a composite key is using @EmbeddedId
, which embeds the primary key inside the entity.
With @EmbeddedId
:
<span>@Embeddable</span><span>public</span> <span>class</span> <span>PaymentId</span> <span>implements</span> <span>Serializable</span> <span>{</span><span>@ManyToOne</span><span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"customerNumber"</span><span>)</span><span>private</span> <span>Customer</span> <span>customer</span><span>;</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>// Constructors, equals(), hashCode(), Getters, and Setters</span><span>}</span><span>@Embeddable</span> <span>public</span> <span>class</span> <span>PaymentId</span> <span>implements</span> <span>Serializable</span> <span>{</span> <span>@ManyToOne</span> <span>@JoinColumn</span><span>(</span><span>name</span> <span>=</span> <span>"customerNumber"</span><span>)</span> <span>private</span> <span>Customer</span> <span>customer</span><span>;</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>// Constructors, equals(), hashCode(), Getters, and Setters</span> <span>}</span>@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:
<span>@Entity</span><span>@Table</span><span>(</span><span>name</span> <span>=</span> <span>"payments"</span><span>)</span><span>public</span> <span>class</span> <span>Payment</span> <span>{</span><span>@EmbeddedId</span><span>private</span> <span>PaymentId</span> <span>id</span><span>;</span><span>}</span><span>@Entity</span> <span>@Table</span><span>(</span><span>name</span> <span>=</span> <span>"payments"</span><span>)</span> <span>public</span> <span>class</span> <span>Payment</span> <span>{</span> <span>@EmbeddedId</span> <span>private</span> <span>PaymentId</span> <span>id</span><span>;</span> <span>}</span>@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()
andhashCode()
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.
© 版权声明
THE END
暂无评论内容