不管是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文档
组合主键使用一个可嵌入的类作为主键表示,因此你需要使用@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 一章中描述如下:
下面是定义组合主键的几种语法:
对于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。