Hibernate中除了使用<generator>来生成主键之外,还可以根据具体的需求让持久化类的标识符属性映射成数据库的符合主键。为了说明问题,我们首先在数据库建立一个USERS表,其中主键使用USERNAME和BIRTH_DATE作为复合主键。其中创建表的SQL语句如下:
create table USERS(
USERNAME VARCHAR2(20) NOT NULL,
PASSWORD VARCHAR2(20) NOT NULL,
BIRTH_DATE VARCHAR2(8) NOT NULL, --使用String类型,存4位年2位月2位日
EMAIL VARCHAR2(40),
PRIMARY KEY(USERNAME,BIRTH_DATE)
);
持久化对象User如下:
package demo.domain;
public class User implements java.io.Serializable {
private String username;
private String password;
private String birthDate;
private String email;
public User() {
}
public User(String username, String birthDate) {
this.username = username;
this.birthDate = birthDate;
}
// 省略所有getter和setter方法
}
映射文件如下,使用复合主键方式映射:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="demo.domain.User" table="USERS">
<composite-id>
<key-property name="username" type="java.lang.String"
column="USERNAME" />
<key-property name="birthDate" type="java.lang.String"
column="BIRTH_DATE" />
</composite-id>
<property name="password" type="java.lang.String" column="PASSWORD" />
<property name="email" type="java.lang.String" column="EMAIL" />
</class>
</hibernate-mapping>
字段含义和单主键的是一致的,就不用多说什么了。测试类如下:
package demo;
import org.hibernate.*;
import demo.domain.User;
public class Test {
public void addUser(User user) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction tx = session.beginTransaction();
session.save(user);
tx.commit();
}
public User getUser(User user) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction tx = session.beginTransaction();
User user2 = (User) session.get(User.class, user);
tx.commit();
return user2;
}
public void printUser(User user) {
System.out.print("用户名:" + user.getUsername() + "\t");
System.out.print("密码:" + user.getPassword() + "\t");
System.out.print("出生日期:" + user.getBirthDate() + "\t");
System.out.println("email地址:" + user.getEmail() + "\t");
}
public static void main(String[] args) {
Test test = new Test();
User user = new User("Sarin", "19870801");
user.setPassword("123456");
user.setEmail("[email protected]");
test.addUser(user);
System.out.println("----------读取一条记录----------");
user = new User();
user.setUsername("Sarin");
user.setBirthDate("19870801");
test.printUser(test.getUser(user));
HibernateUtil.getSessionFactory().close();
}
}
运行程序,我们得到如下结果:
Hibernate在查询记录是就按照联合主键方式来查找了。
当然,我们也可以抽取主键类来映射符合主键,则把上面的代码改为如下方式:
package demo.domain;
import java.io.Serializable;
public class UserId implements Serializable {
private String username;
private String birthDate;
public UserId() {
}
public UserId(String username, String birthDate) {
this.username = username;
this.birthDate = birthDate;
}
// 省略所有getter和setter方法
}
package demo.domain;
public class User implements java.io.Serializable {
private UserId id;
private String password;
private String email;
public User() {
}
public User(UserId id, String password, String email) {
this.id = id;
this.password = password;
this.email = email;
}
// 省略所有getter和setter方法
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="demo.domain.User" table="USERS">
<composite-id name="id" class="demo.domain.UserId">
<key-property name="username" type="java.lang.String"
column="USERNAME" />
<key-property name="birthDate" type="java.lang.String"
column="BIRTH_DATE" />
</composite-id>
<property name="password" type="java.lang.String" column="PASSWORD" />
<property name="email" type="java.lang.String" column="EMAIL" />
</class>
</hibernate-mapping>
映射文件修改很简单,在<composite-id>元素上加入name和class两个属性即可。其他都不用修改。测试类如下修改即可:
package demo;
import org.hibernate.*;
import demo.domain.*;
public class Test {
public void addUser(User user) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction tx = session.beginTransaction();
session.save(user);
tx.commit();
}
public User getUser(UserId id) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction tx = session.beginTransaction();
User user = (User) session.get(User.class, id);
tx.commit();
return user;
}
public void printUser(User user) {
System.out.print("用户名:" + user.getId().getUsername() + "\t");
System.out.print("密码:" + user.getPassword() + "\t");
System.out.print("出生日期:" + user.getId().getBirthDate() + "\t");
System.out.println("email地址:" + user.getEmail() + "\t");
}
public static void main(String[] args) {
Test test = new Test();
User user = new User();
UserId id = new UserId("Sarin", "19870801");
user.setId(id);
user.setPassword("123456");
user.setEmail("[email protected]");
test.addUser(user);
System.out.println("----------读取一条记录----------");
id = new UserId("Sarin", "19870801");
user = test.getUser(id);
test.printUser(user);
HibernateUtil.getSessionFactory().close();
}
}
代码中的HibernateUtil是抽取出的Hibernate单例模式工具类。在实际项目中能尽可能减少复合主键也是有必要的,因为复合主键使用的是自然属性作为码,一旦自然属性需要修改,那么修改主键是非常麻烦的一件事情,所以使用代理主键作为数据库的主键策略是最好的。