对于系统设计而言,应该尽量减少和避免在数据库表中引入与业务逻辑相关的主键关系;将业务逻辑主键引入库表,使得底层数据库表结构与业务逻辑想耦合,如果业务逻辑的变化,将很可能对底层数据库结构产生连带应影响;
例如:在项目开发初期,业务逻辑认为系统中的用户名是不可以重复的,随着新的需求产生,出现了用户名可重复的可能性,这样的话,我们就得从底层数据库开始更改,但是在某些关键的系统中,这样更在某些关键地方,将是一个大工程,这样的工作将难以接受;
这样,复合主键的引入,很大程度上意味着业务逻辑已经侵入到数据存储逻辑之中。因此在新的系统中,应该避免这样的设计;然而在旧的系统中遇到这样的情况,如果有复合主键的支持就显得很必要;
复合主键的使用,以Tuser为蓝本,将那么拆分成两个部分,firstname,lastname,以他们作为复合主键;对于复合主键而言,我们可以通过两种方式确定主键(Hibernate中通过<composite-id>定义复合主键):
- 基于实体类的复合主键
复合主键是由实体类的属性组成,此时,实体类本身也扮演着复合主键类的角色;定义如下(Hibernate要求复合主键类要实现equals和hasCode方法,以作为不同数据之间的标识):
Tuser类:
public class Tuser implements Serializable {
private String firstName;
private String lastName;
private List email = new ArrayList();
private List address = new ArrayList();
get/set()....
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + ((email == null) ? 0 : email.hashCode());
result = prime * result
+ ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result
+ ((lastName == null) ? 0 : lastName.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;
Tuser other = (Tuser) obj;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (email == null) {
if (other.email != null)
return false;
} else if (!email.equals(other.email))
return false;
if (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
if (lastName == null) {
if (other.lastName != null)
return false;
} else if (!lastName.equals(other.lastName))
return false;
return true;
}
}
Tuser.hbm.xml:
<?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.keith"> <class name="Tuser" table="tUser"> <composite-id> <key-property name="firstName" /> <key-property name="lastName" /> </composite-id> <property name="email" type="com.keith.EmailList"></property> </class> </hibernate-mapping>
先添加一条数据(测试代码):
Tuser tUser = new Tuser();
tUser.setFirstName("keke");
tUser.setLastName("EEE");
List email = new ArrayList();
email.add("keith@gmail.com");
email.add("keith@sina.coom");
tUser.setEmail(email);
session.save(tUser);
看下输出的SQL:
Hibernate: insert into tUser(email, firstName, lastName) values (?, ?, ?)
虽然数据已经添加进去了,但是对于表结构还有点模糊,看下表结构:
CREATE TABLE `tuser` (
`firstName` varchar(255) NOT NULL,
`lastName` varchar(255) NOT NULL,
`email` varchar(255) DEFAULT NULL,
PRIMARY KEY (`firstName`,`lastName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
这样就一目了然了,测试下如何查询(以tUser作为识别对象,通过session.load()方法加载数据):
Tuser tUser = new Tuser();
tUser.setFirstName("keke");
tUser.setLastName("EEE");
tUser = (Tuser) session.load(Tuser.class, tUser);
System.out.println("tUser's Email:"+tUser.getEmail());
- 基于主键类的复合主键
我们也可以将主键逻辑加以隔离,以一个单独的主键类对复合主键进行描述;这样我们就需要一个TuserPK类(用来装Tuser的主键):
package com.keith;
public class TuserPK implements java.io.Serializable {
private String firstName;
private String lastName;
get()/set()....
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result
+ ((lastName == null) ? 0 : lastName.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;
TuserPK other = (TuserPK) obj;
if (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
if (lastName == null) {
if (other.lastName != null)
return false;
} else if (!lastName.equals(other.lastName))
return false;
return true;
}
}
在Tuser中就这样:
public class Tuser implements Serializable {
private TuserPK tuserpk;
private List email = new ArrayList();
private List address = new ArrayList();
get()/set()...
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((address == null) ? 0 : address.hashCode());
result = prime * result + ((email == null) ? 0 : email.hashCode());
result = prime * result + ((tuserpk == null) ? 0 : tuserpk.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;
Tuser other = (Tuser) obj;
if (address == null) {
if (other.address != null)
return false;
} else if (!address.equals(other.address))
return false;
if (email == null) {
if (other.email != null)
return false;
} else if (!email.equals(other.email))
return false;
if (tuserpk == null) {
if (other.tuserpk != null)
return false;
} else if (!tuserpk.equals(other.tuserpk))
return false;
return true;
}
}
属性文件里要这样(<composite-id name="tuserpk" class="TuserPK">):
<?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.keith">
<class name="Tuser" table="tUser">
<composite-id name="tuserpk" class="TuserPK">
<key-property name="firstName" />
<key-property name="lastName" />
</composite-id>
<property name="email" type="com.keith.EmailList"></property>
</class>
</hibernate-mapping>
添加一条数据:
Tuser tUser = new Tuser();
TuserPK tUserPk = new TuserPK();
tUserPk.setFirstName("aaa");
tUserPk.setLastName("bbb");
tUser.setTuserpk(tUserPk);
List email = new ArrayList();
email.add("keith@gmail.com");
email.add("keith@sina.coom");
tUser.setEmail(email);
session.save(tUser);
根据主键进行查询:
Tuser tUser = new Tuser();
TuserPK tUserPk = new TuserPK();
tUserPk.setFirstName("aaa");
tUserPk.setLastName("bbb");
tUser = (Tuser) session.load(Tuser.class, tUserPk);
System.out.println("tUser's Email:"+tUser.getEmail());
以上就是复合主键的用法!