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());