資料表:雙欄位主鍵
CREATE TABLE user (
name VARCHAR(100) NOT NULL,
phone VARCHAR(50) NOT NULL,
age INT,
PRIMARY KEY (name, phone)
);
方式一:複合主鍵欄位直接放在實體類別
實體類別需實作 Serializable、equals、hashCode
public class User implements Serializable {
private String name;
private String phone;
private Integer age;
// getter/setter ...
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof User)) return false;
User other = (User) obj;
return new EqualsBuilder()
.append(this.name, other.name)
.append(this.phone, other.phone)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder()
.append(this.name)
.append(this.phone)
.toHashCode();
}
}
使用 Apache Commons Lang EqualsBuilder/HashCodeBuilder 簡化實作,避免手寫錯誤。
映射檔:<composite-id>
<class name="com.hb3.pack_03.model.User" table="user">
<composite-id>
<key-property name="name" column="name" type="string"/>
<key-property name="phone" column="phone" type="string"/>
</composite-id>
<property name="age" column="age" type="integer"/>
</class>
操作:主鍵即實體本身
User user = new User();
user.setName("shenbin");
user.setPhone("0970123456");
user.setAge(28);
session.save(user);
// 載入需傳入完整實體作為主鍵
user = (User) session.load(User.class, user);
方式二:複合主鍵提取為獨立 PK 類別
PK 類別
public class UserPK implements Serializable {
private String name;
private String phone;
// getter/setter + equals/hashCode (同上)
}
實體類別持有 PK 物件
public class User implements Serializable {
private UserPK userPK;
private Integer age;
// getter/setter ...
}
映射檔:<composite-id name="userPK" class="UserPK">
<class name="com.hb3.pack_04.model.User" table="user">
<composite-id name="userPK" class="com.hb3.pack_04.model.UserPK" unsaved-value="any">
<key-property name="name" column="name" type="string"/>
<key-property name="phone" column="phone" type="string"/>
</composite-id>
<property name="age" column="age" type="integer"/>
</class>
unsaved-value="any":Hibernate 總視為新物件(因無單一 ID 判斷),配合 save() 使用。
操作:以 PK 物件載入
UserPK pk = new UserPK();
pk.setName("shenbin");
pk.setPhone("0970123456");
User user = (User) session.load(User.class, pk);
兩種方式比較
| 維度 | 方式一:內嵌欄位 | 方式二:獨立 PK 類別 |
| 程式碼簡潔度 | 實體類別較大 | 職責分離,PK 可重用 |
| 序列化/傳遞 | 需傳整個實體 | 可只傳 PK 物件(輕量) |
| 映射複雜度 | 較簡單 | 稍多一個類別映射 |
| 適用場景 | 簡單複合鍵、單一實體使用 | 複合鍵被多實體共享、需獨立傳遞 PK |
重要提醒
- equals/hashCode 必須正確實作,否則 Session 快取、Set/Map 運作會出錯。
- 複合主鍵**不支援自動生成策略**,應用程式需自行賦值。
- 關聯映射時,外鍵需包含所有複合鍵欄位,映射用
<key-many-to-one> 或多個 <key-property>。
- 儲存後**必須呼叫
tx.commit()** 才會持久化到資料庫。