从单向关系映射开始,然后考虑双向关系映射,逐步讲解典型的案例。
在单向关联的例子中,将用 Student类和 Classes类。
在双向关联的例子中,将用 Person类和 Event类。
单向 many-to-one 关联是最常见的单向关联关系。
Student.java
package com.dfdc.hibernate.domain;
import java.io.Serializable;
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
private Long sid;
private String sname;
//维护多对一
private Classes clazz;
public Classes getClazz() {
return clazz;
}
public void setClazz(Classes clazz) {
this.clazz = clazz;
}
public Long getSid() {
return sid;
}
public void setSid(Long sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
}
Classes.java
import java.io.Serializable;
import java.util.Set;
public class Classes implements Serializable{
private static final long serialVersionUID = 1L;
private Long cid;
private String cname;
public Long getCid() {
return cid;
}
public void setCid(Long cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
}
Student.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>
<class name="com.dfdc.hibernate.domain.Student" table="STUDENT">
<id name="sid" type="java.lang.Long">
<column name="SID" />
<generator class="native" />
</id>
<property name="sname" type="java.lang.String">
<column name="SNAME" />
</property>
<!-- 多对一,无关联表 -->
<many-to-one name="clazz" column="CID" not-null="true"/>
</class>
</hibernate-mapping>
以下为了代码简洁将省略DTD部分。
Classes.hbm.xml内容
<hibernate-mapping>
<class name="com.dfdc.hibernate.domain.Classes" table="CLASSES">
<id name="cid" type="java.lang.Long">
<column name="CID" />
<generator class="native" />
</id>
<property name="cname" type="java.lang.String">
<column name="CNAME" />
</property>
</class>
</hibernate-mapping>
基于外键关联的单向一对一关联和单向多对一关联几乎是一样的。唯一的不同就是单向一对一关联中的外键字段具有唯一性约束。
在上面Student.hbm.xml内容的节点增加unique=”true”
基于主键关联的单向一对一关联通常使用一个特定的 id 生成器。
注释掉student类的clazz属性,并在classes类中增加Student类型的stu属性,即Classes类持有Student的引用。
Student.hbm.xml内容
<id name="sid" type="java.lang.Long">
<column name="SID" />
<generator class="native" />
</id>
<property name="sname" type="java.lang.String">
<column name="SNAME" />
</property>
**Classes.hbm.xml内容**class节点内容改成如下:
<!-- 基于主键的单向一对一 -->
<id name="cid" type="java.lang.Long">
<column name="SID" />
<generator class="foreign">
<!-- property只关联对象 -->
<param name="property">stu</param>
</generator>
</id>
<property name="cname" type="java.lang.String">
<column name="CNAME" />
</property>
<!-- constrained,通过一个外键引用对主键进行约束。-->
<one-to-one name="stu" constrained="true"/>
基于外键关联的单向一对多关联是一种很少见的情况,我们不推荐使用它。
我们认为对于这种关联关系最好使用连接表(关联表)。
基于连接表的单向一对多关联应该优先被采用。
注意,通过指定unique=”true”,我们可以把多样性从多对多改变为一对多。
student映射文件内容:
<id name="sid" type="java.lang.Long">
<column name="SID" />
<generator class="native" />
</id>
<property name="sname" type="java.lang.String">
<column name="SNAME" />
</property>
classes映射文件内容:
<id name="cid" type="java.lang.Long">
<column name="CID" />
<generator class="native"/>
</id>
<property name="cname" type="java.lang.String">
<column name="CNAME" />
</property>
<!-- 基于连接表的多对多
指定unique="true"转换为两个一对多 -->
<set name="students" table="CLASSES_STUDENT">
<key column="CID"></key>
<many-to-many column="SID" unique="true" class="com.dfdc.hibernate.domain.Student"></many-to-many>
</set>
注释掉student类的clazz属性,并在classes类中设置Set集合,属性为students。
基于连接表的单向多对一关联在关联关系可选的情况下应用也很普遍。
student映射文件内容:
<id name="sid" type="java.lang.Long">
<column name="SID" />
<generator class="native" />
</id>
<property name="sname" type="java.lang.String">
<column name="SNAME" />
</property>
<!-- 基于连接表的单向多对一
table:被连接表的名称。
optional(可选 — 默认是 false):如果为true,Hibernate
只会在此连接定义的属性非空时插入一行数据,并且总是使用一个外连接来得到这些属性。
-->
<join table="CLASSES_STUDENT" optional="true" >
<key column="SID" unique="true"/>
<many-to-one name="clazz" column="CID" not-null="true" unique="true"/>
</join>
classes映射文件内容:
<id name="cid" type="java.lang.Long">
<column name="CID" />
<generator class="native"/>
</id>
<property name="cname" type="java.lang.String">
<column name="CNAME" />
</property>
以上的student类和classes类不变。
基于连接表的单向一对一关联也是可行的,但非常少见,就不举例了。
最后,这里是一个单向多对多关联的例子。
student映射文件内容:
<id name="sid" type="java.lang.Long">
<column name="SID" />
<generator class="native" />
</id>
<property name="sname" type="java.lang.String">
<column name="SNAME" />
</property>
<!-- 基于连接表的单向多对多 -->
<set name="clazzSet" table="CLASSES_STUDENT">
<key column="SID"/>
<many-to-many column="CID" class="com.dfdc.hibernate.domain.Classes"/>
</set>
classes映射文件内容:
<id name="cid" type="java.lang.Long">
<column name="CID" />
<generator class="native"/>
</id>
<property name="cname" type="java.lang.String">
<column name="CNAME" />
</property>
把上面的student类的clazz属性类型改成Set即可。
从双向关系映射开始,逐步讲解典型的案例。
在双向关联的例子中,将用 Event 和 Person。
Event.java
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
public class Event {
private Long id;
private Date date;
private Set<Person> personSet = new HashSet<Person>();
public Set<Person> getPersonSet() {
return personSet;
}
public void setPersonSet(Set<Person> personSet) {
this.personSet = personSet;
}
public Event() {}
public Long getId() {
return id;
}
//只有hibernate能操作标识符
private void setId(Long id) {
this.id = id;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
Person.java
import java.util.HashSet;
import java.util.Set;
public class Person {
private Long id;
private int age;
public Person() {}
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
双向多对一关联,是最常见的关联关系。
person映射文件内容:
<id name="id">
<column name="PERSON_ID"/>
<generator class="native"></generator>
</id>
<property name="age"/>
<!-- 双向多对一/一对多 -->
<many-to-one name="events" column="EVENT_ID" class="com.dfdc.hibernate.domain.Event" not-null="true"/>
event映射文件内容:
<id name="id">
<column name="EVENT_ID" />
<generator class="increment" />
</id>
<property name="date" type="timestamp">
<column name="DATE" />
</property>
<!-- 双向一对多/多对一 -->
<set name="persons" inverse="true">
<key column="EVENT_ID"/>
<one-to-many class="com.dfdc.hibernate.domain.Person"/>
</set>
在上面的person类添加Event类型的events属性。
基于外键关联的双向一对一关联也很常见。
person映射文件内容:
<id name="id">
<column name="PERSON_ID"/>
<generator class="native"></generator>
</id>
<property name="age"/>
<!-- 基于外键关联双向一对一
unique,作为 property-ref 引用的目标。这使关联同时具有一对一的效果。
-->
<many-to-one name="events" column="EVENT_ID" unique="true" not-null="true"/>
event映射文件内容:
<id name="id">
<column name="EVENT_ID" />
<generator class="native" />
</id>
<property name="date" type="timestamp">
<column name="DATE" />
</property>
<!-- 基于外键关联双向一对一
property-ref,被关联到此外键的类中的对应属性的名字。如果没有指定,被关联类的主键将被使用。
-->
<one-to-one name="persons" property-ref="events"/>
注释掉Event类的set类型的属性,并添加Person类型的属性persons。
在Person类添加Event类型的属性events。
person映射文件内容:
<!-- 1.基于主键关联双向一对一 -->
<id name="id">
<column name="EVENT_ID"/>
<!--
foreign,获取相关联的对象的标识符。它通常和 <one-to-one> 联合起来使用。
-->
<generator class="foreign">
<!--property,相关联的对象 -->
<param name="property">events</param>
</generator>
</id>
<property name="age"/>
<!--constrained="true",开启当前类与关联类之间的外键约束
将影响级联操作顺序。
-->
<one-to-one name="events" class="com.dfdc.hibernate.domain.Event" constrained="true"/>
event映射文件内容:
<id name="id">
<column name="EVENT_ID" />
<generator class="native" />
</id>
<property name="date" type="timestamp">
<column name="DATE" />
</property>
<!-- 1.基于主键关联双向一对一 -->
<one-to-one name="persons" class="com.dfdc.hibernate.domain.Person"/>
在上面的person类添加Event类型的events属性。
在上面的event类添加Person类型的persons属性,并注释Set类型的persons属性。
主键关联不需要额外的表字段,两个对象通过主键一对一关联,必须确认它们被赋予同样的标识值。
下面是一个基于连接表的双向一对多关联的例子。
person映射文件内容:
<id name="id">
<column name="PENSON_ID"/>
<generator class="native"></generator>
</id>
<property name="age"/>
<!-- 4. 基于连接表关联双向多对一/一对多 -->
<set name="eventSet" table="PERSONEVENT">
<key column="PERSON_ID"/>
<many-to-many column="EVENT_ID" unique="true" class="com.dfdc.hibernate.domain.Event"/>
</set>
event映射文件内容:
<id name="id">
<column name="EVENT_ID" />
<generator class="native" />
</id>
<property name="date" type="timestamp">
<column name="DATE" />
</property>
<!-- 4. 基于连接表关联双向多对一/一对多 -->
<join table="PERSONEVENT" inverse="true" optional="true">
<key column="EVETN_ID"/>
<many-to-one name="persons" column="PERSON_ID" not-null="true"/>
</join>
在上面的Person类(代表一)中定义Set类型的eventSet属性。
在event类(代表多)中注释掉Set类型的persons属性,并添加Person类型的persons属性,即持有Person类的引用。
基于连接表的双向一对一关联也是可行的,但极为罕见。这里就不阐述了。
下面是一个双向多对多关联的例子
person映射文件内容:
<id name="id">
<column name="PENSON_ID"/>
<generator class="native"></generator>
</id>
<property name="age"/>
<set name="eventSet" table="person_event">
<key column="PERSON_ID"></key>
<many-to-many class="com.dfdc.hibernate.domain.Event" column="EVENT_ID"></many-to-many>
</set>
event映射文件内容:
<id name="id">
<column name="EVENT_ID" />
<generator class="native" />
</id>
<property name="date" type="timestamp">
<column name="DATE" />
</property>
<set name="personSet" table="person_event" inverse="true">
<key column="EVENT_ID"/>
<many-to-many column="PERSON_ID" class="com.dfdc.hibernate.domain.Person"/>
</set>
在上面的Person类中定义Set类型的eventSet属性。
以上。。。