hibernate-004: PaymentId.class

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

<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 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:

<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() 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.

原文链接:hibernate-004: PaymentId.class

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
Only his strong enough, will not be trampled.
只有自己足够强大,才不会被别人践踏
评论 抢沙发

请登录后发表评论

    暂无评论内容