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:

<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 for serialization)</span>
<span>public</span> <span>PaymentId</span><span>()</span> <span>{}</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</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() and hashCode() for proper comparison in JPA</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</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 for serialization)</span>
    <span>public</span> <span>PaymentId</span><span>()</span> <span>{}</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</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() and hashCode() for proper comparison in JPA</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</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 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:

<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, the Payment entity would use:

<span>@EmbeddedId</span>
<span>private</span> <span>PaymentId</span> <span>id</span><span>;</span>
<span>@EmbeddedId</span>
<span>private</span> <span>PaymentId</span> <span>id</span><span>;</span>
@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.

原文链接:hibernate-003: @IdClass(PaymentId.class)

© 版权声明
THE END
喜欢就支持一下吧
点赞11 分享
Mankind is made great or little by its own will.
一个人伟大或渺小,取决于他的意志力
评论 抢沙发

请登录后发表评论

    暂无评论内容