在日常开发中会遇到这样一种情况
数据库中的某张表需要多个字段列才能唯一确定一行记录
这时表需要使用复合主键
面对这样的情况Hibernate为我们提供了两种方式来解决复合主键问题
方式一将复合主键对应的属性与实体其他普通属性放在一起
例如实体类People中id和name属性对应复合主键
/*实体类使用复合主键必须实现Serializable接口*/
public class People implements Serializable
{
private static final long serialVersionUID = L;
private String id;
private String name;
private int age;
public People()
{
}
public String getId()
{
return id;
}
public void setId(String id)
{
thisid = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
thisname = name;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
thisage = age;
}
@Override
public int hashCode()
{
final int prime = ;
int result = ;
result = prime * result + ((id == null)
? : idhashCode())
result = prime * result + ((name == null) ? : namehashCode())
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != objgetClass())
return false;
People other = (People) obj;
if (id == null)
{
if (otherid != null)
return false;
}
else if (!idequals(otherid))
return false;
if (name == null)
{
if (othername != null)
return false;
}
else if (!nameequals(othername))
return false;
return true;
} } Peoplehbmxml:
<?xml version= encoding=utf?>
<!DOCTYPE hibernatemapping PUBLIC //Hibernate/Hibernate Mapping DTD //EN mappingdtd>
<hibernatemapping>
<class name=comsuxiaoleihibernatepojosPeople table=people>
<! 复合主键使用compositeid标签 >
<compositeid>
<! keyproperty标签表示哪一些属性对应复合主键 >
<keyproperty name=id column=id type=string></keyproperty>
<keyproperty name=name column=name type=string></keyproperty>
</compositeid>
<property name=age column=age type=integer></property>
</class>
</hibernatemapping>
Hibernate中使用复合主键时需要注意一些规则
使用复合主键的实体类必须实现Serializable接口必须实现Serializable接口的原因很简单我们查找数据的时候是根据主键查找的打开Hibernate的帮助文档我们可以找到get与load方法的声明形式如下
Object load(Class theClassSerializable id)
Object get(Class theClassSerializable id)
当我们查找复合主键类的对象时需要传递主键值给get()或load()方法的id参数而id参数只能接收一个实现了Serializable接口的对象而复合主键类的主键不是一个属性可以表示的所以只能先new出复合主键类的实例(例如new People())然后使用主键属性的set方法将主键值赋值给主键属性然后将整个对象传递给get()或load()方法的id参数实现主键值的传递所以复合主键的实体类必须实现Serializable接口
使用复合主键的实体类必须重写equals和hashCode方法必须重写equals和hashCode方法也很好理解这两个方法使用于判断两个对象 (两条记录)是否相等的为什么要判断两个对象是否相等呢?因为数据库中的任意两条记录中的主键值是不能相同的所以我们在程序中只要确保了两个对象的主键值不同就可以防止主键约束违例的错误出现也许这里你会奇怪为什么不使用复合主键的实体类不重写这两个方法也没有主键违例的情况出现这是因为使用单一主键方式主键值是Hibernate来维护的它会确保主键不会重复而复合主键方式主键值是编程人员自己维护的所以必须重写equals和hashCode方法用于判断两个对象的主键是否相同
重写的equals和hashCode方法只与主键属性有关普通属性不要影响这两个方法进行判断这个原因很简单主键才能决定一条记录其他属性不能决定一条记录
保存测试
public class Client {
public static void main(String[] args)
{
Session session = HibernateUtilgetSessionFactory()openSession()
Transaction tx = null;
try
{
tx = sessionbeginTransaction()
People people = new People()
/*主键值由我们自己维护*/
peoplesetId()
peoplesetName(zhangsan)
peoplesetAge()
sessionsave(people)
mit()
}
catch (Exception e)
{
if(tx != null)
{
txrollback()
}
eprintStackTrace()
}
finally
{
sessionclose()
}
}
}
看看数据库
数据被正确的插入到数据库中了
读取数据测试
public class Client
{
public static void main(String[] args)
{
Session session = HibernateUtilgetSessionFactory()openSession()
Transaction tx = null;
try
{
tx = sessionbeginTransaction()
/*查询复合主键对象需要先构建主键*/
People peoplePrimaryKey = new People()
peoplePrimaryKeysetId()
peoplePrimaryKeysetName(zhangsan)
/*然后将构建的主键值传入get方法中获取对应的People对象*/
People people = (People)sessionget(Peopleclass peoplePrimaryKey)
Systemoutprintln(people age is:+peoplegetAge())
mit()
}
catch (Exception e)
{
if(tx != null)
{
txrollback()
}
eprintStackTrace()
}
finally
{
sessionclose()
}
}
}
控制台输出
people age is:可以看到数据成功的取出了
方式二将主键属性提取到一个主键类中实体类只需包含主键类的一个引用
主键类
/*必须实现Serializable接口*/
public class PeoplePrimaryKey implements Serializable
{
private static final long serialVersionUID = L;
/*复合主键值*/
private String id;
private String name;
public PeoplePrimaryKey()
{
}
/*复合主键值的get和set方法*/
public String getId()
{
return id;
}
public void setId(String id)
{
thisid = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
thisname = name;
}
@Override
public int hashCode()
{
final int prime = ;
int result = ;
result = prime * result + ((id == null) ? : idhashCode())
result = prime * result + ((name == null) ? : namehashCode())
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != objgetClass())
return false;
PeoplePrimaryKey other = (PeoplePrimaryKey) obj;
if (id == null)
{
if (otherid != null)
return false;
}
else if (!idequals(otherid))
return false;
if (name == null)
{
if (othername != null)
return false;
}
else if (!nameequals(othername))
return false;
return true;
}
}
实体类
public class People {
/*持有主键类的一个引用使用该引用作为这个类的OID*/
private PeoplePrimaryKey peoplePrimaryKey;
private int age;
public People()
{
}
public PeoplePrimaryKey getPeoplePrimaryKey()
{
return peoplePrimaryKey;
}
public void setPeoplePrimaryKey(PeoplePrimaryKey peoplePrimaryKey)
{
thispeoplePrimaryKey = peoplePrimaryKey;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
thisage = age;
}
}
Peoplehbmxml文件稍有一点变动
<?xml version= encoding=utf?>
<!DOCTYPE hibernatemapping PUBLIC //Hibernate/Hibernate Mapping DTD //EN mappingdtd>
<hibernatemapping>
<class name=comsuxiaoleihibernatepojosPeople table=people>
<! 复合主键使用compositeid标签 >
<! name 指定了复合主键对应哪一个属性
class 指定了复合主键属性的类型 >
<compositeid name=peoplePrimaryKey class=comsuxiaoleihibernatepojosPeoplePrimaryKey>
<! keyproperty指定了复合主键由哪些属性组成 >
<keyproperty name=id column=id type=string></keyproperty>
<keyproperty name=name column=name type=string></keyproperty>
</compositeid>
<property name=age column=age type=integer></property>
</class> </hibernatemapping>
场景测试与方式一大同小异这里不再举例了主键类为什么实现Serializable接口和为什么重写equals和hashCode方法上面已经解释的很清楚了