Hibernate关系映射的主要任务是实现数据库关系表与持久化类之间的映射。
1.一对一关联
有两种实现方式:共享主键方式(使两个数据表的主键使用相同的值,通过主键形成一对一映射关系)和唯一外键方式(一个表的外键与另一个表的唯一主键对应形成一对一映射关系)。
1)共享主键方式
在注册某个论坛会员的时候,往往不但要填写登录账号和密码,还要填写其他的详细信息,这两部分信息通常会放在不同的表中,如表4.1、表4.2所示。
字 段 名 称 |
数 据 类 型 |
主 键 |
自 增 |
允 许 为 空 |
描 述 |
ID |
int(4) |
是 |
ID号 |
||
USERNAME |
varchar(20) |
登录账号 |
|||
PASSWORD |
varchar(20) |
登录密码 |
表4.1 登录表Login
字 段 名 称 |
数 据 类 型 |
主 键 |
自 增 |
允 许 为 空 |
描 述 |
ID |
int(4) |
是 |
增1 |
ID号 |
|
TRUENAME |
varchar(8) |
是 |
真实姓名 |
||
varchar(50) |
是 |
电子邮件 |
表4.2 详细信息表Detail
登录表和详细信息表属于典型的一对一关联关系,可按共享主键方式进行。步骤如下:
(1) 创建Java项目,命名为“Hibernate_mapping”。
(2)添加Hibernate开发能力,步骤同4.2.1节第4步。HibernateSessionFactory类同样位于org.util包下。(注意先要配置数据库连接)
(3)编写生成数据库表对应的Java类对象和映射文件。(注意放置的位置:选对项目名)
a)Login表对应的POJO类Login.java:
package org.model;
public class Login implements java.io.Serializable{
private int id; // ID号
private String username; // 登录账号
private String password; // 密码
private Detail detail; // 详细信息
// 省略上述各属性的getter和setter方法
}
b)Detail表对应的Detail.java:
package org.model;
public class Detail implements java.io.Serializable{
private int id; // ID号
private String trueName; // 真实姓名
private String email; // 电子邮件
private Login login; // 登录信息
// 省略上述各属性的getter和setter方法
}
c)Login表与Login类的ORM映射文件Login.hbm.xml。
<?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>
<!-- name指定POJO类,table指定对应数据库的表 -->
<class name="org.model.Login" table="login">
<!-- name指定主键,type指定主键类型 -->
<id name="id" type="java.lang.Integer">
<column length="4" name="ID"/>
<!-- 采用foreign标志生成器,直接采用外键的属性值,达到共享主键的目的-->
<generator class="foreign">
<param name="property">detail</param>
</generator>
</id>
<!-- POJO属性及表中字段的对应 -->
<property name="username" type="java.lang.String">
<column name="USERNAME"/>
</property>
<property name="password" type="java.lang.String">
<column name="PASSWORD"/>
</property>
<!-- name表示属性名字,class表示被关联的类的名字,constrained="true"表明当前的主键上存在一个外键约束-->
<one-to-one class="org.model.Detail" constrained="true" name="detail"/>
</class>
</hibernate-mapping>
d)Detail表与Detail类的ORM映射文件Detail.hbm.xml:
<?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>
<!-- name指定POJO类,table指定对应数据库的表 -->
<class name="org.model.Detail" table="detail">
<!-- name指定主键,type指定主键类型 -->
<id name="id" type="java.lang.Integer">
<column length="4" name="ID"/>
<generator class="identity"/>
</id>
<!-- POJO属性及表中字段的对应 -->
<property name="trueName" type="java.lang.String">
<column name="TRUENAME"/>
</property>
<property name="email" type="java.lang.String">
<column name="EMAIL"/>
</property>
<!-- name表示属性名字,class表示被关联的类的名字,cascade="all"表明主控类的所有操作,对关联类也执行同样操作,lazy="false"表示此关联为立即加载-->
<one-to-one cascade="all" class="org.model.Login" lazy="false" name="login"/>
</class>
</hibernate-mapping>
(4)在hibernate.cfg.xml文件中加入配置映射文件的语句。
<mapping resource="org/model/Detail.hbm.xml"/>
<mapping resource="org/model/Login.hbm.xml"/>
(5)创建测试类。 在src文件夹下创建包test,在该包下建立测试类,命名为“Test.java”。其代码。
package test;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.model.*;
import org.util.HibernateSessionFactory;
import java.sql.*;
public class Test {
public static void main(String[] args) {
// 调用HibernateSessionFactory的getSession方法创建Session对象
Session session = HibernateSessionFactory.getSession();
// 创建事务对象
Transaction ts = session.beginTransaction();
Detail detail = new Detail();
Login login = new Login();
login.setUsername("yanhong");
login.setPassword("123");
detail.setTrueName("严红");
detail.setEmail("yanhong@126.com");
// 相互设置关联
login.setDetail(detail);
detail.setLogin(login);
// 这样完成后就可以通过Session对象调用session.save(detail)来持久化该对象
session.save(detail);
ts.commit();
HibernateSessionFactory.closeSession();
}
}
(6) 运行程序,测试结果。 因为该程序为Java Application,所以可以直接运行。在完全没有操作数据库的情况下,程序就完成了对数据的插入。插入数据后,Login表和Detail表的内容如图4.12、图4.13所示。

图4.12 Login表

图4.13 Detail表
2)唯一外键方式
唯一外键的情况很多,例如,每个人对应一个房间。其实在很多情况下,可以是几个人住在同一个房间里面,就是多对一的关系。但是如果把这个多变成唯一,也就是说让一个人住一个房间,就变成了一对一的关系了,这就是前面说的一对一的关系其实是多对一关联关系的一种特殊情况。对应的Person表和Room表如表4.3、表4.4所示。
字 段 名 称 |
数 据 类 型 |
主 键 |
自 增 |
允 许 为 空 |
描 述 |
Id |
int |
是 |
增1 |
ID号 |
|
name |
varchar(20) |
姓名 |
|||
room_id |
int |
是 |
房间号 |
表4.3 Person表
字 段 名 称 |
数 据 类 型 |
主 键 |
自 增 |
允 许 为 空 |
描 述 |
id |
int(4) |
是 |
增1 |
ID号 |
|
address |
varchar(100) |
地址 |
表4.4 Room表
步骤如下:
(1)在项目Hibernate_mapping的org.model包下编写生成数据库表对应的Java类对象和映射文件。
Person表对应的POJO类Person.java:
package org.model;
public class Person implements java.io.Serializable {
private Integer id;
private String name;
private Room room;
// 省略上述各属性的getter和setter方法
}
Room表对应的POJO类Room.java:
package org.model;
public class Room implements java.io.Serializable{
private int id;
private String address;
private Person person;
// 省略上述各属性的getter和setter方法
}
Person表与Person类的ORM映射文件Person.hbm.xml:
<?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="org.model.Person" table="Person">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="room" column="room_id" class="org.model.Room"
cascade="all" unique="true" />
<!-- name="room" 属性名称
column="room_id" 充当外键的字段名
class="org.model.Room" 被关联的类的名称
cascade="all" 主控类所有操作,对关联类也执行同样操作
unique="true" 唯一性约束,实现一对一 -->
</class>
</hibernate-mapping>
去掉roomId的配置:
<property name="roomId" type="java.lang.Integer">
<column name="room_id" />
</property>
Room表与Room类的ORM映射文件Room.hbm.xml:
<?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="org.model.Room" table="Room">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="address" column="address" type="java.lang.String"/>
<one-to-one name="person"
class="org.model.Person"
property-ref="room"/>
<!-- name="person" 属性名
class="org.model.Person" 被关联的类的名称
property-ref="room" 指定关联类的属性名 -->
</class>
</hibernate-mapping>
(2)在hibernate.cfg.xml文件中加入如下的配置映射文件的语句。
<mapping resource="org/model/Person.hbm.xml"/>
<mapping resource="org/model/Room.hbm.xml"/>
(3)编写测试代码。
在src文件夹下的包test的Test类中加入如下代码:
Person person=new Person();
person.setName("liumin");
Room room=new Room();
room.setAddress("NJ-S1-328");
person.setRoom(room);
session.save(person);
(4)运行程序,测试结果。
因为该程序为Java Application,所以可以直接运行。在完全没有操作数据库的情况下,程序就完成了对数据的插入。插入数据后,Person表和Room表的内容如图4.14、图4.15所示。

图4.14 Person表

图4.15 Room表
2.多对一单向关联
只要把上例中的一对一的唯一外键关联实例稍微修改就可以变成多对一。步骤如下:
(1)在项目Hibernate_mapping的org.model包下编写生成数据库表对应的Java类对象和映射文件。
其对应表不变,Person表对应的类也不变,对应的Person.hbm.xml文件修改如下(只是去掉 <many-to-one/>中的unique="true"):
<?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="org.model.Person" table="Person">
<id name="id" column="id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="name" column="name" type="java.lang.String"/>
<many-to-one name="room"
column="room_id"
class="org.model.Room"
cascade="all"/>
</class>
</hibernate-mapping>
而Room表不变,对应的POJO类如下:(去掉private Person person及其方法;)
package org.model;
public class Room implements java.io.Serializable{
private int id;
private String address;
// 省略上述各属性的getter和setter方法
}
Room表与Room类的ORM映射文件Room.hbm.xml如下(去掉<one-to-one/>配置):
<?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="org.model.Room" table="room">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="address"
column="address"
type="java.lang.String"/>
</class>
</hibernate-mapping>
因为是单向多对一,所以无需在“一”的一边指定“多”的一边。(好比一班学生可以记住一个老师,这个老师记不住每个学生)
(2)编写测试代码。
在src文件夹下的包test的Test类中加入如下代码(跟上例一样,可以修改一下数据):
Person person=new Person();
person.setName("liuyanmin");
Room room=new Room();
room.setAddress("NJ-S1-328");
person.setRoom(room);
session.save(person);
(3) 运行程序,测试结果。
因为该程序为Java Application,所以可以直接运行。在完全没有操作数据库的情况下,程序就完成了对数据的插入。插入数据后,Person表和Room表的内容如图4.16、图4.17所示。

图4.16 Person表

图4.17 Room表
3.一对多双向关联
多对一单向关联,“一”的一方也知道“多”的一方,就变成了一对多(或多对一)双向关联。下面通过修改4.3.2节的例子来完成双向多对一的实现。步骤如下:
(1) 在项目Hibernate_mapping的org.model包下编写生成数据库表对应的Java类对象和映射文件。
Person表对应的POJO及其映射文件不用改变,现在来修改Room表对应的POJO类及其映射文件。对应的POJO类Room.java。
package org.model;
import java.util.HashSet;
import java.util.Set;
public class Room {
private int id;
private String address;
private Set person=new HashSet(); //集合,存放多个Person对象
public int getId() {
return id;
}
public void setId(int id) {
this.id=id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address=address;
}
public Set getPerson() {
return person;
}
public void setPerson(Set person) {
this.person=person;
}
}
Room表与Room类的ORM映射文件Room.hbm.xml。
<?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="org.model.Room" table="room">
<id name="id" column="id">
<generator class="native"/>
</id>
<property name="address" column="address" type="java.lang.String"/>
<set name="person"
inverse="false"
cascade="all">
<key column ="room_id" />
<one-to-many class="org.model.Person" />
<!-- name="person" 此属性为Set集合类型,由name指定属性名字
inverse="false" 表示关联关系的维护工作由谁来负责,默认false,表示由主控方负责;true表示由被控方负责。由于该例是双向操作,故需要设为false,也可不写
cascade="all" 级联程度
key column ="room_id" 充当外键的字段名
one-to-many class="org.model.Person" 被关联的类名字 -->
</set>
</class>
</hibernate-mapping>
该配置文件中cascade配置的是级联程度,它有以下几种取值:
all:表示所有操作句在关联层级上进行连锁操作。
save-update:表示只有save和update操作进行连锁操作。
delete:表示只有delete操作进行连锁操作。
all-delete-orphan:在删除当前持久化对象时,它相当于delete;在保存或更新当前持久化对象时,它相当于save-update。另外它还可以删除与当前持久化对象断开关联关系的其他持久化对象。
(2)编写测试代码。在src文件夹下的包test的Test类中加入如下代码:
Person person1=new Person();
Person person2=new Person();
Room room=new Room();
room.setAddress("NJ-S1-328");
person1.setName("李方方");
person2.setName("王艳");
person1.setRoom(room);
person2.setRoom(room);
//这样完成后就可以通过Session对象调用session.save(person1)和session.save(person)会自动保存room
session.save(person1);
session.save(person2);
(3)运行程序,测试结果。 因为该程序为Java Application,所以可以直接运行。在完全没有操作数据库的情况下,程序就完成了对数据的插入。插入数据后,Person表和Room表的内容如图4.18、图4.19所示。

图4.18 Person表

图4.19 Room表
由于是双向的,当然也可以从Room的一方来保存Person,在Test.java中加入如下代码:
Person person1=new Person();
Person person2=new Person();
Room room=new Room();
room.setAddress("NJ-S1-328");
person1.setName("李方方");
person2.setName("王艳");
Set persons=new HashSet();
persons.add(person1);
persons.add(person2);
room.setPerson(persons);
//这样完成后,就可以通过Session对象调用session.save(room)会自动保存person1和person2
session.save(room);
运行程序,插入数据后,Person表和Room表的内容如图4.20、图4.21所示。

图4.20 Person表

图4.21 Room表
4.多对多关联
1)多对多单向关联
学生和课程就是多对多的关系,一个学生可以选择多门课程,而一门课程又可以被多个学生选择。多对多关系在关系数据库中不能直接实现,还必须依赖一张连接表。如表4.6、表4.7和表4.8所示。
字 段 名 称 |
数 据 类 型 |
主 键 |
自 增 |
允 许 为 空 |
描 述 |
ID |
int |
是 |
增1 |
ID号 |
|
SNUMBER |
varchar(10) |
学号 |
|||
SNAME |
varchar(10) |
是 |
姓名 |
||
SAGE |
int |
是 |
年龄 |
表4.6 学生表student
字 段 名 称 |
数 据 类 型 |
主 键 |
自 增 |
允 许 为 空 |
描 述 |
ID |
int |
是 |
增1 |
ID号 |
|
CNUMBER |
varchar(10) |
课程号 |
|||
CNAME |
varchar(20) |
是 |
课程名 |
表4.7 课程表course
字 段 名 称 |
数 据 类 型 |
主 键 |
自 增 |
允 许 为 空 |
描 述 |
SID |
int |
是 |
学生ID号 |
||
CID |
int |
是 |
课程ID号 |
表4.8 连接表stu_cour
由于是单向的,也就是说从一方可以知道另一方,反之不行。这里以从学生知道选择了哪些课程为例实现多对多单向关联。步骤如下:
(1)在项目Hibernate_mapping的org.model包下编写生成数据库表对应的Java类对象和映射文件。student表对应的POJO类如下:
package org.model;
import java.util.HashSet;
import java.util.Set;
public class Student implements java.io.Serializable{
private int id;
private String snumber;
private String sname;
private int sage;
private Set courses=new HashSet();
//省略上述各属性的getter和setter方法
}
student表与Student类的ORM映射文件Student.hbm.xml。
<?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="org.model.Student" table="student">
<id name="id" type="java.lang.Integer">
<column name="ID" length="4" />
<generator class="identity"/>
</id>
<property name="snumber" type="java.lang.String">
<column name="SNUMBER"/>
</property>
<property name="sname" type="java.lang.String">
<column name="SNAME" />
</property>
<property name="sage" type="java.lang.Integer">
<column name="SAGE"></column>
</property>
<set name="courses" // set标签表示此属性为Set集合类型,由name指定属性名称
table="stu_cour" // 连接表的名称
lazy="true" // 表示此关联为延迟加载,所谓延迟加载就是到了用的时候进行加载,避免大量暂时无用的关系对象
cascade="all"> // 级联程度
<key column="SID"></key> // 指定参照student表的外键名称
<many-to-many class="org.model.Course" // 被关联的类的名称
column="CID"/> // 指定参照course表的外键名称
</set>
</class>
</hibernate-mapping>
course表对应的POJO如下:
package org.model;
public class Course implements java.io.Serializable{
private int id;
private String cnumber;
private String cname;
//省略上述各属性的getter和setter方法。
}
course表与Course类的ORM映射文件Course.hbm.xml。
<?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="org.model.Course" table="course">
<id name="id" type="java.lang.Integer">
<column name="ID" length="4" />
<generator class="identity"/>
</id>
<property name="cnumber" type="java.lang.String">
<column name="CNUMBER"/>
</property>
<property name="cname" type="java.lang. String ">
<column name="CNAME" />
</property>
</class>
</hibernate-mapping>
(2)在hibernate.cfg.xml文件中加入如下的配置映射文件的语句。
<mapping resource="org/model/Student.hbm.xml"/>
<mapping resource="org/model/Course.hbm.xml"/>
(3)编写测试代码。在src文件夹下的包test的Test类中加入如下代码。
Course cour1=new Course();
Course cour2=new Course();
Course cour3=new Course();
cour1.setCnumber("101");
cour1.setCname("计算机基础");
cour2.setCnumber("102");
cour2.setCname("数据库原理");
cour3.setCnumber("103");
cour3.setCname("计算机原理");
Set courses=new HashSet();
courses.add(cour1);
courses.add(cour2);
courses.add(cour3);
Student stu=new Student();
stu.setSnumber("081101");
stu.setSname("李方方");
stu.setSage(21);
stu.setCourses(courses);
session.save(stu);
//设置完成后就可以通过Session对象调用session.save(stu)完成持久化
(4) 运行程序,测试结果。 因为该程序为Java Application,所以可以直接运行。在完全没有操作数据库的情况下,程序就完成了对数据的插入。插入数据后,student表、course表及连接表stu_cour表的内容如图4.22、图4.23、图4.24所示。

图4.22 student表

图4.23 course表

图4.24 stu_cour表
2)多对多双向关联
同时实现 两个互逆的多对多单项关联即可。 首先将其Course表所对应的POJO对象修改成如下代码:
package org.model;
import java.util.HashSet;
import java.util.Set;
public class Course implements java.io.Serializable{
private int id;
private String cnumber;
private String cname;
private Set stus=new HashSet();
//省略上述各属性的getter和setter方法
}
Course表与Course类的ORM映射文件Course.hbm.xml:
<?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="org.model.Course" table="course">
<id name="id" type="java.lang.Integer">
<column name="ID" length="4" />
<generator class="identity"/>
</id>
<property name="cnumber" type="java.lang.String">
<column name="CNUMBER"/>
</property>
<property name="cname" type="java.lang.String">
<column name="CNAME" />
</property>
<set name="stus" // set标签表示此属性为Set集合类型,由name指定一个属性名称
table="stu_cour" // 连接表的名称
lazy="true" // 关联为延迟加载
cascade="all"> // 级联操作为所有
<key column="CID"></key> // 指定参照course表的外键名称
<many-to-many class="org.model.Student" // 被关联的类名
column="SID"/> // 指定参照student表的外键名称
</set>
</class>
</hibernate-mapping>
实际用法与多对多单项关联相同,只是主控方不同而已。
来源:oschina
链接:https://my.oschina.net/u/220508/blog/626223