对于系统设计而言,应该尽量减少和避免在数据库表中引入与业务逻辑相关的主键关系;将业务逻辑主键引入库表,使得底层数据库表结构与业务逻辑想耦合,如果业务逻辑的变化,将很可能对底层数据库结构产生连带应影响;
例如:在项目开发初期,业务逻辑认为系统中的用户名是不可以重复的,随着新的需求产生,出现了用户名可重复的可能性,这样的话,我们就得从底层数据库开始更改,但是在某些关键的系统中,这样更在某些关键地方,将是一个大工程,这样的工作将难以接受;
这样,复合主键的引入,很大程度上意味着业务逻辑已经侵入到数据存储逻辑之中。因此在新的系统中,应该避免这样的设计;然而在旧的系统中遇到这样的情况,如果有复合主键的支持就显得很必要;
复合主键的使用,以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("[email protected]"); email.add("[email protected]"); 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("[email protected]"); email.add("[email protected]"); 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());
以上就是复合主键的用法!