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

相关推荐