不管是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。