Hibernate配置复合主键 composite primary key (一)
这篇是关于如何配置Hibernate实现复合主键的映射功能。
摘自圣思园Hibernate.25的后半部分和26的前半部分。
1.要使用复合主键,对应类Student必须实现Serializable接口。
2.要重写hashCode和equals方法。
重写hashCode和equals方法的原因:
Hibernate要判断两个对象是否相同,避免出现两个复合主键相同的对象实例被加入数据库(数据库也不会接收)。
因此Hibernate会通过hashCode和equals方法来判断是否可以将两个对象放入诸如Set这样的集合中去。
Student.java
package composite;
import java.io.Serializable;
public class Student implements Serializable
{
//这里用name和cardId作为联合主键
private String cardId;
private String name;
private int age;
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((cardId == null) ? 0 : cardId.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (cardId == null)
{
if (other.cardId != null)
return false;
}
else if (!cardId.equals(other.cardId))
return false;
if (name == null)
{
if (other.name != null)
return false;
}
else if (!name.equals(other.name))
return false;
return true;
}
}Student.hbm.xml
<hibernate-mapping package="composite"> <class name="Student" table="test_student"> <!-- composite-id表示复合主键 --> <composite-id> <!-- key-property表示组成主键的元素 --> <key-property name="cardId" column="card_id" type="string"/> <key-property name="name" column="name" type="string"/> </composite-id> <property name="age" column="age" type="int"/> </class> </hibernate-mapping>
运行configure(),会产生以下SQL语句。
createtabletest_student(
card_idvarchar2(255)notnull,
namevarchar2(255)notnull,
agenumber(10),
primarykey(card_id,name));
插入
运行以下插入代码,便会报错了。
Session session=HibernateUtil.openSession();
Transaction tx=session.beginTransaction();
Student s1=new Student("111", "alleni", 22);
Student s2=new Student("111","alleni",22);
session.save(s1);
session.save(s2);
tx.commit();Exceptioninthread"main"org.hibernate.NonUniqueObjectException:adifferentobjectwiththesameidentifiervaluewasalreadyassociatedwiththesession:[composite.Student#composite.Student@abc1a76f]
atorg.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:179)
atorg.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:135)
atorg.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:206)
atorg.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:55)
atorg.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:191)
atorg.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:49)
atorg.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
atorg.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:764)
atorg.hibernate.internal.SessionImpl.save(SessionImpl.java:756)
atorg.hibernate.internal.SessionImpl.save(SessionImpl.java:752)
atcomposite.Hibernate_1Insert.main(Hibernate_1Insert.java:27)
org.hibernate.NonUniqueObjectException:adifferentobjectwiththesameidentifiervaluewasalreadyassociatedwiththesession
即是说,这个identifier值已经被session存储了,并且指向已经存在的对象。而再存入另一个对象,也就是s2的时候,又提取出来了同样的identifier。
查询
随便瞎写一个查询语句:
Student s=(Student) session.get(Student.class,"111"); System.out.println(s.getName());
这里的报错信息是:
org.hibernate.TypeMismatchException:Providedidofthewrongtypeforclasscomposite.Student.Expected:classcomposite.Student,gotclassjava.lang.String
提供id类型不正确,期待的是composite.Student类型,而不是java.lang.String.
为什么Student要实现Serializable接口?
在使用get或load方法的时候需要先构建出来该实体类的对象,并且将查询依据(联合主键)设置进去,然后作为get或者load方法的第二个参数传进去即可。
用过Hibernate的就知道,Hibernate的get和load方法接受的都是一个Class和一个Serializable类型对象。
HibernateAPI文档:
Objectorg.hibernate.Session.get(Classclazz,Serializableid)
Returnthepersistentinstanceofthegivenentityclasswiththegivenidentifier,ornullifthereisnosuchpersistentinstance.(Iftheinstanceisalreadyassociatedwiththesession,returnthatinstance.Thismethodneverreturnsanuninitializedinstance.)
因此在Student实现了Serializable接口之后,我们就可以通过如下的查询方式:
Session session=HibernateUtil.openSession();
//先构建出来查询的依据,一个Student对象。
Student student_primaryKey=new Student();
student_primaryKey.setCardId("111");
student_primaryKey.setName("alleni");
Student s=(Student) session.get(Student.class,student_primaryKey);
System.out.println(s.getName());
System.out.println(s.getAge());