不管是xml方式還是annotation方式的聯合主鍵都需要使用到一個額外的主鍵生成類,這個類必須是序列化的,即需要implements Serializable,另外,需要重寫equals和hashCode方法,以保證數據正常傳輸和主鍵的唯一性。
1.新建主鍵生成類StudentPK
package com.baosight.model;
import java.io.Serializable;
/**
* <p>Title:StudentPK </p>
* <p>Description:TODO </p>
* <p>Company: </p>
* @author yuan
* @date 2016-4-15 下午8:08:16*/
public class StudentPK implements Serializable{
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(obj instanceof StudentPK){
StudentPK pk = (StudentPK)obj;
if(this.id.equals(pk.getId())&&this.name.equals(pk.getName())){
return true;
}
}
return false;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return this.id.hashCode();
}
}
2.使用xml方式的聯合主鍵
在Student中引用StudentPK
package com.baosight.model;
/**
* <p>Title: </p>
* <p>Description:Student </p>
* <p>Company: </p>
* @author yuan
* @date 2016-4-10 下午12:32:46*/
public class Student {
// private String id;
// private String name;
private StudentPK pk;
private int age;
// public String getId() {
// return id;
// }
// public void setId(String id) {
// this.id = id;
// }
// public String getName() {
// return name;
// }
// public void setName(String name) {
// this.name = name;
// }
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public StudentPK getPk() {
return pk;
}
public void setPk(StudentPK pk) {
this.pk = pk;
}
}
3.配置Student.hbm.xml,需要使用composite-id來定義聯合主鍵
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.baosight.model">
<class name="Student">
<!-- <id name="id" >
<generator class="uuid"></generator>
</id> -->
<composite-id name="pk" class="StudentPK">
<key-property name="id"></key-property>
<key-property name="name"></key-property>
</composite-id>
<!-- <property name="name"></property> -->
<property name="age"></property>
</class>
</hibernate-mapping>
其中,用name指明Student中聯合主鍵的名稱,用class指明主鍵生成類,使用key-property指明主鍵生成類中作爲聯合主鍵的屬性
4.使用HibernateIDTest進行JUnit測試
package com.baosight.model;
import static org.junit.Assert.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* <p>Title:HibernateIDTest </p>
* <p>Description:TODO </p>
* <p>Company: </p>
* @author yuan
* @date 2016-4-14 下午9:11:00*/
public class HibernateIDTest {
private static SessionFactory sf = null;
@BeforeClass
public static void beforeClass(){
// 讀取配置文件
Configuration cfg = new AnnotationConfiguration();
// 得到session工廠
sf = cfg.configure().buildSessionFactory();
}
@Test
public void testStudent() {
// 學生測試類
Student s = new Student();
StudentPK pk = new StudentPK();
pk.setId("1");
pk.setName("zhangsan");
s.setPk(pk);
// s.setName("s1");
s.setAge(20);
// 得到session
Session session = sf.openSession();
// 開啓事務
session.beginTransaction();
// session執行save
session.save(s);
// 事務提交
session.getTransaction().commit();
// 關閉session
session.close();
}
@Test
public void testTeacher() {
// 教師測試類
Teacher t = new Teacher();
// TeacherPK pk = new TeacherPK();
// pk.setId("1");
// pk.setName("t1");
// t.setPk(pk);
t.setId("1");
t.setName("t1");
t.setTitle("中級");
// 得到session
Session session = sf.openSession();
// 開啓事務
session.beginTransaction();
// session執行save
session.save(t);
// 事務提交
session.getTransaction().commit();
// 關閉session
session.close();
}
@AfterClass
public static void afterClass(){
// 關閉session工廠
sf.close();
}
}
測試系果爲:
上述異常是因爲沒有將主鍵生成類進行序列化導致的
上面雖然運行了,但是有警告,是提醒要將主鍵生成類的equals和hashCode方法進行重寫
5.hibernate的annotation形式的聯合主鍵
先看下API文檔
2.2.6. 映射覆合主鍵與外鍵
組合主鍵使用一個可嵌入的類作爲主鍵表示,因此你需要使用@Id 和@Embeddable兩個註解. 還有一種方式是使用@EmbeddedId註解.注意所依賴的類必須實現 serializable以及實現equals()/hashCode()方法. 你也可以如Mapping identifier properties一章中描述的辦法使用@IdClass註解.
@Entity public class RegionalArticle implements Serializable { @Id public RegionalArticlePk getPk() { ... } } @Embeddable public class RegionalArticlePk implements Serializable { ... }
或者
@Entity public class RegionalArticle implements Serializable { @EmbeddedId public RegionalArticlePk getPk() { ... } } public class RegionalArticlePk implements Serializable { ... }Mapping identifier properties一章中描述如下:
下面是定義組合主鍵的幾種語法:
- 將組件類註解爲@Embeddable,並將組件的屬性註解爲@Id
- 將組件的屬性註解爲@EmbeddedId
- 將類註解爲@IdClass,並將該實體中所有屬於主鍵的屬性都註解爲@Id
對於EJB2的開發人員來說 @IdClass是很常見的, 但是對於Hibernate的用戶來說就是一個嶄新的用法. 組合主鍵類對應了一個實體類中的多個字段或屬性, 而且主鍵類中用於定義主鍵的字段或屬性和 實體類中對應的字段或屬性在類型上必須一致.下面我們看一個例子:
@Entity @IdClass(FootballerPk.class) public class Footballer { //part of the id key @Id public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } //part of the id key @Id public String getLastname() { return lastname; } public void setLastname(String lastname) { this.lastname = lastname; } public String getClub() { return club; } public void setClub(String club) { this.club = club; } //appropriate equals() and hashCode() implementation } @Embeddable public class FootballerPk implements Serializable { //same name and type as in Footballer public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } //same name and type as in Footballer public String getLastname() { return lastname; } public void setLastname(String lastname) { this.lastname = lastname; } //appropriate equals() and hashCode() implementation }
如上, @IdClass指向對應的主鍵類.
綜上所述共有3中方式:一是使用@Embeddable@Id
二是使用@EmbeddedId
三是使用@IdClass@Id
6.使用@Embeddable@Id,即將組件類註解爲@Embeddable,並將組件的屬性註解爲@Id
新建主鍵生成類TeacherPK
package com.baosight.model;
import java.io.Serializable;
import javax.persistence.Embeddable;
/**
* 聯合主鍵類
* <p>Title:TeacherPK </p>
* <p>Description:TODO </p>
* <p>Company: </p>
* @author yuan
* @date 2016-4-15 下午9:01:00
*/
@Embeddable
public class TeacherPK implements Serializable{
private String id;
private String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
if(obj instanceof StudentPK){
StudentPK pk = (StudentPK)obj;
if(this.id.equals(pk.getId())&&this.name.equals(pk.getName())){
return true;
}
}
return false;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
return this.id.hashCode();
}
}
在Teacher中引用TeacherPK
package com.baosight.model;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.SequenceGenerator;
import javax.persistence.TableGenerator;
/**
* <p>Title: </p>
* <p>Description:Teacher </p>
* <p>Company: </p>
* @author yuan
* @date 2016-4-10 下午12:32:46*/
@Entity
@TableGenerator(name="tableGEN",table="table_gen",pkColumnName="pk_key",valueColumnName="pk_value",pkColumnValue="teacher",allocationSize=1)
@SequenceGenerator(name="teacherSEQ",sequenceName="teacherSEQ_DB")
@IdClass(value=TeacherPK.class)
public class Teacher {
// private String id;
// private String name;
private String title;
private TeacherPK pk;
// @Id
// @GeneratedValue//auto
// @GeneratedValue(strategy=GenerationType.TABLE,generator="tableGEN")
// @GeneratedValue(strategy=GenerationType.SEQUENCE,generator="teacherSEQ")
/*public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Id
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}*/
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
// @EmbeddedId
@Id
public TeacherPK getPk() {
return pk;
}
public void setPk(TeacherPK pk) {
this.pk = pk;
}
}
使用JUnit進行單元測試
package com.baosight.model;
import static org.junit.Assert.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* <p>Title:HibernateIDTest </p>
* <p>Description:TODO </p>
* <p>Company: </p>
* @author yuan
* @date 2016-4-14 下午9:11:00*/
public class HibernateIDTest {
private static SessionFactory sf = null;
@BeforeClass
public static void beforeClass(){
// 讀取配置文件
Configuration cfg = new AnnotationConfiguration();
// 得到session工廠
sf = cfg.configure().buildSessionFactory();
}
@Test
public void testStudent() {
// 學生測試類
Student s = new Student();
StudentPK pk = new StudentPK();
pk.setId("1");
pk.setName("zhangsan");
s.setPk(pk);
// s.setName("s1");
s.setAge(20);
// 得到session
Session session = sf.openSession();
// 開啓事務
session.beginTransaction();
// session執行save
session.save(s);
// 事務提交
session.getTransaction().commit();
// 關閉session
session.close();
}
@Test
public void testTeacher() {
// 教師測試類
Teacher t = new Teacher();
TeacherPK pk = new TeacherPK();
pk.setId("1");
pk.setName("t1");
t.setPk(pk);
// t.setId("1");
// t.setName("t1");
t.setTitle("中級");
// 得到session
Session session = sf.openSession();
// 開啓事務
session.beginTransaction();
// session執行save
session.save(t);
// 事務提交
session.getTransaction().commit();
// 關閉session
session.close();
}
@AfterClass
public static void afterClass(){
// 關閉session工廠
sf.close();
}
}
測試結果爲:
7.使用@EmbeddedId,即將組件的屬性註解爲@EmbeddedId
TeacherPK類不需要使用註解
Teacher類如下:
package com.baosight.model;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.SequenceGenerator;
import javax.persistence.TableGenerator;
/**
* <p>Title: </p>
* <p>Description:Teacher </p>
* <p>Company: </p>
* @author yuan
* @date 2016-4-10 下午12:32:46*/
@Entity
@TableGenerator(name="tableGEN",table="table_gen",pkColumnName="pk_key",valueColumnName="pk_value",pkColumnValue="teacher",allocationSize=1)
@SequenceGenerator(name="teacherSEQ",sequenceName="teacherSEQ_DB")
@IdClass(value=TeacherPK.class)
public class Teacher {
// private String id;
// private String name;
private String title;
private TeacherPK pk;
// @Id
// @GeneratedValue//auto
// @GeneratedValue(strategy=GenerationType.TABLE,generator="tableGEN")
// @GeneratedValue(strategy=GenerationType.SEQUENCE,generator="teacherSEQ")
/*public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Id
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}*/
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@EmbeddedId
// @Id
public TeacherPK getPk() {
return pk;
}
public void setPk(TeacherPK pk) {
this.pk = pk;
}
}
運行結果:
8.使用@IdClass和@Id,即將類註解爲@IdClass,並將該實體中所有屬於主鍵的屬性都註解爲@Id
注意此時Teacher使用自己的屬性註解爲@Id,使用@IdClass指明主鍵生成類
Teacher如下:
package com.baosight.model;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.SequenceGenerator;
import javax.persistence.TableGenerator;
/**
* <p>Title: </p>
* <p>Description:Teacher </p>
* <p>Company: </p>
* @author yuan
* @date 2016-4-10 下午12:32:46*/
@Entity
@TableGenerator(name="tableGEN",table="table_gen",pkColumnName="pk_key",valueColumnName="pk_value",pkColumnValue="teacher",allocationSize=1)
@SequenceGenerator(name="teacherSEQ",sequenceName="teacherSEQ_DB")
@IdClass(value=TeacherPK.class)
public class Teacher {
private String id;
private String name;
private String title;
// private TeacherPK pk;
@Id
// @GeneratedValue//auto
@GeneratedValue(strategy=GenerationType.TABLE,generator="tableGEN")
// @GeneratedValue(strategy=GenerationType.SEQUENCE,generator="teacherSEQ")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Id
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
/*// @EmbeddedId
public TeacherPK getPk() {
return pk;
}
public void setPk(TeacherPK pk) {
this.pk = pk;
}*/
}
JUnit測試類如下:
package com.baosight.model;
import static org.junit.Assert.*;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
/**
* <p>Title:HibernateIDTest </p>
* <p>Description:TODO </p>
* <p>Company: </p>
* @author yuan
* @date 2016-4-14 下午9:11:00*/
public class HibernateIDTest {
private static SessionFactory sf = null;
@BeforeClass
public static void beforeClass(){
// 讀取配置文件
Configuration cfg = new AnnotationConfiguration();
// 得到session工廠
sf = cfg.configure().buildSessionFactory();
}
@Test
public void testStudent() {
// 學生測試類
Student s = new Student();
StudentPK pk = new StudentPK();
pk.setId("1");
pk.setName("zhangsan");
s.setPk(pk);
// s.setName("s1");
s.setAge(20);
// 得到session
Session session = sf.openSession();
// 開啓事務
session.beginTransaction();
// session執行save
session.save(s);
// 事務提交
session.getTransaction().commit();
// 關閉session
session.close();
}
@Test
public void testTeacher() {
// 教師測試類
Teacher t = new Teacher();
// TeacherPK pk = new TeacherPK();
// pk.setId("1");
// pk.setName("t1");
// t.setPk(pk);
t.setId("1");
t.setName("t1");
t.setTitle("中級");
// 得到session
Session session = sf.openSession();
// 開啓事務
session.beginTransaction();
// session執行save
session.save(t);
// 事務提交
session.getTransaction().commit();
// 關閉session
session.close();
}
@AfterClass
public static void afterClass(){
// 關閉session工廠
sf.close();
}
}
運行結果:
以上即爲聯合主鍵的內容,需要指明的是在實際的使用過程中,annotation中多使用@EmbeddedId或者@IdClass@Id。