Hibernate 無主鍵表(復合主鍵)映射 – JAVA編程語言程序開發技術文章

1. 為什麼要有復合主鍵映射
在現實中我們可能會遇到許多表可能是沒有主鍵的,那麼我們對其做映射後使用會是什麼樣的結果?能正常得到我們想要的嗎?結果應該是得不到想要的結果,而得到的可能會是如下的報錯:

Caused by:org.hibernate.AnnotationException: No identifier specified for entity: xxx.xxx.xxx

這個結果告訴我們:Hibernate映射表是需要主鍵的。

所以復合主鍵映射就應運而生瞭。

2. 註意點
復合主鍵使用一個可嵌入的類作為主鍵表示,因此你需要使用@Id和@Embeddable兩個註解.還有一種方式是使用@EmbeddedId註解.當然還有一種方法那就是——使用@IdClass註解。具體請查看【hibernate-annotations-3.4.0.GA 2.2.6.映射復合主鍵與外鍵】。

當然,隻需要選擇其中的一種就可以瞭。^_^

註意所依賴的類必須實現 serializable以及實現equals()/hashCode()方法.

舉一個具體事例:

2.1. User.java
[java] 
<span style="font-size: 12px;">package com.sourcefour.bean;
// default package
import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* User entity. @author MyEclipse Persistence Tools
*/
@Entity
@Table (name="t_user_composite_pk"
)
public class User implements java.io.Serializable {
// Fields
private UserId id;
// Constructors
/** default constructor */
public User() {
}
/** full constructor */
public User(UserId id) {
this.id = id;
}
// Property accessors
@EmbeddedId
@AttributeOverrides( {
@AttributeOverride(name="intId", column=@Column (name="intId", nullable=false) ),
@AttributeOverride(name="varcName", column=@Column (name="varcName", length=50) ),
@AttributeOverride(name="varcAddress", column=@Column (name="varcAddress", length=50) ),
@AttributeOverride(name="intAge", column=@Column (name="intAge", nullable=false) ) } )
public UserId getId() {
return this.id;
}
public void setId(UserId id) {
this.id = id;
}
}
</span>
package com.sourcefour.bean;
// default package

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;

/**
 * User entity. @author  MyEclipse Persistence Tools
 */
@Entity
@Table(name="t_user_composite_pk"
)

public class User  implements java.io.Serializable {

    // Fields   

     private UserId id;

    // Constructors

    /** default constructor */
    public User() {
    }

   
    /** full constructor */
    public User(UserId id) {
        this.id = id;
    }

  
    // Property accessors
    @EmbeddedId
   
    @AttributeOverrides( {
        @AttributeOverride(name="intId", column=@Column(name="intId", nullable=false) ),
        @AttributeOverride(name="varcName", column=@Column(name="varcName", length=50) ),
        @AttributeOverride(name="varcAddress", column=@Column(name="varcAddress", length=50) ),
        @AttributeOverride(name="intAge", column=@Column(name="intAge", nullable=false) ) } )

    public UserId getId() {
        return this.id;
    }
   
    public void setId(UserId id) {
        this.id = id;
    }
}
2.2. UserId.java

[java]
<span style="font-size: 12px;">package com.sourcefour.bean;
// default package
import javax.persistence.Column;
import javax.persistence.Embeddable;
/**
* UserId entity. @author MyEclipse Persistence Tools
*/
@Embeddable
public class UserId implements java.io.Serializable {
// Fields
private int intId;
private String varcName;
private String varcAddress;
private int intAge;
// Constructors
/** default constructor */
public UserId() {
}
/** minimal constructor */
public UserId(int intId, int intAge) {
this.intId = intId;
this.intAge = intAge;
}
/** full constructor */
public UserId(int intId, String varcName, String varcAddress, int intAge) {
this.intId = intId;
this.varcName = varcName;
this.varcAddress = varcAddress;
this.intAge = intAge;
}
// Property accessors
@Column (name = "intId", nullable = false)
public int getIntId() {
return this.intId;
}
public void setIntId(int intId) {
this.intId = intId;
}
@Column (name = "varcName", length = 50)
public String getVarcName() {
return this.varcName;
}
public void setVarcName(String varcName) {
this.varcName = varcName;
}
@Column (name = "varcAddress", length = 50)
public String getVarcAddress() {
return this.varcAddress;
}
public void setVarcAddress(String varcAddress) {
this.varcAddress = varcAddress;
}
@Column (name = "intAge", nullable = false)
public int getIntAge() {
return this.intAge;
}
public void setIntAge(int intAge) {
this.intAge = intAge;
}
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof UserId))
return false;
UserId castOther = (UserId) other;
return (this.getIntId() == castOther.getIntId())
&& ((this.getVarcName() == castOther.getVarcName()) || (this.getVarcName() != null
&& castOther.getVarcName() != null && this.getVarcName().equals(castOther.getVarcName())))
&& ((this.getVarcAddress() == castOther.getVarcAddress()) || (this.getVarcAddress() != null
&& castOther.getVarcAddress() != null && this.getVarcAddress().equals(
castOther.getVarcAddress()))) && (this.getIntAge() == castOther.getIntAge());
}
public int hashCode() {
int result = 17;
result = 37 * result + this.getIntId();
result = 37 * result + (getVarcName() == null ? 0 : this.getVarcName().hashCode());
result = 37 * result + (getVarcAddress() == null ? 0 : this.getVarcAddress().hashCode());
result = 37 * result + this.getIntAge();
return result;
}
}</span>
package com.sourcefour.bean;

// default package

import javax.persistence.Column;
import javax.persistence.Embeddable;

/**
 * UserId entity. @author  MyEclipse Persistence Tools
 */
@Embeddable
public class UserId implements java.io.Serializable {

 // Fields

 private int intId;
 private String varcName;
 private String varcAddress;
 private int intAge;

 // Constructors

 /** default constructor */
 public UserId() {
 }

 /** minimal constructor */
 public UserId(int intId, int intAge) {
  this.intId = intId;
  this.intAge = intAge;
 }

 /** full constructor */
 public UserId(int intId, String varcName, String varcAddress, int intAge) {
  this.intId = intId;
  this.varcName = varcName;
  this.varcAddress = varcAddress;
  this.intAge = intAge;
 }

 // Property accessors

 @Column(name = "intId", nullable = false)
 public int getIntId() {
  return this.intId;
 }

 public void setIntId(int intId) {
  this.intId = intId;
 }

 @Column(name = "varcName", length = 50)
 public String getVarcName() {
  return this.varcName;
 }

 public void setVarcName(String varcName) {
  this.varcName = varcName;
 }

 @Column(name = "varcAddress", length = 50)
 public String getVarcAddress() {
  return this.varcAddress;
 }

 public void setVarcAddress(String varcAddress) {
  this.varcAddress = varcAddress;
 }

 @Column(name = "intAge", nullable = false)
 public int getIntAge() {
  return this.intAge;
 }

 public void setIntAge(int intAge) {
  this.intAge = intAge;
 }

 public boolean equals(Object other) {
  if ((this == other))
   return true;
  if ((other == null))
   return false;
  if (!(other instanceof UserId))
   return false;
  UserId castOther = (UserId) other;

  return (this.getIntId() == castOther.getIntId())
    && ((this.getVarcName() == castOther.getVarcName()) || (this.getVarcName() != null
      && castOther.getVarcName() != null && this.getVarcName().equals(castOther.getVarcName())))
    && ((this.getVarcAddress() == castOther.getVarcAddress()) || (this.getVarcAddress() != null
      && castOther.getVarcAddress() != null && this.getVarcAddress().equals(
      castOther.getVarcAddress()))) && (this.getIntAge() == castOther.getIntAge());
 }

 public int hashCode() {
  int result = 17;

  result = 37 * result + this.getIntId();
  result = 37 * result + (getVarcName() == null ? 0 : this.getVarcName().hashCode());
  result = 37 * result + (getVarcAddress() == null ? 0 : this.getVarcAddress().hashCode());
  result = 37 * result + this.getIntAge();
  return result;
 }

}

3. 可能還會出現的問題

具體來說問題就是查詢出來的結果列表為‘null’(這一點我這次在我機器上測試時沒有出現)。
如果出現瞭該問題,那麼看到這一點就應該能解決問題啦,如果不出現那就更好,呵呵!
但是個人覺得這一點應該還是得說的。^_^
有時候查詢出來的結果列表為‘null’,這令人很是費解,可以想下這是什麼原因?
直接上原因,嘎嘎……。
原因:作為聯合主鍵的字段理論上不應該包含可能為空的字段。
原因分析:根據原因,說明實體Bean中的某個(些)對應的表字段有空值。
解決方案:隻需要將可能為空的字段不作為聯合主鍵的一部分就可以。
說的估計暈頭瞭吧,直接來個事例吧(個人一直覺得,例子是解釋問題的最好說明)。

假設表中的varcName和varcAddress是可能為空的,其它都不可能為空,那麼映射應該是這樣的

User.java

[java] 
……
private UserId id;
private String varcName;
private String varcAddress;
……
/*
這裡加入varcName和varcAddress映射內容,嗯還是貼出來吧,反正電子檔的又不怕木有地方,嘎嘎……
*/
@Column (name="varcName", length=50)
public String getVarcName() {
return this.varcName;
}
public void setVarcName(String varcName) {
this.varcName = varcName;
}
@Column (name="varcAddress", length=50)
public String getVarcAddress() {
return this.varcAddress;
}
public void setVarcAddress(String varcAddress) {
this.varcAddress = varcAddress;
}
……
private UserId id;
private String varcName;
private String varcAddress;
……
/*
這裡加入varcName和varcAddress映射內容,嗯還是貼出來吧,反正電子檔的又不怕木有地方,嘎嘎……
*/
 @Column(name="varcName", length=50)
    public String getVarcName() {
        return this.varcName;
    }
   
    public void setVarcName(String varcName) {
        this.varcName = varcName;
    }

    @Column(name="varcAddress", length=50)
    public String getVarcAddress() {
        return this.varcAddress;
    }
   
    public void setVarcAddress(String varcAddress) {
        this.varcAddress = varcAddress;
    }

UserId.java

[java] view plain copy print ?
……
private int intId;
private int intAge;
……

發佈留言