学习任务

  • 关联映射
  • inverse属性、cascade属性
  • 单向的多对一、双向的一对多映射
  • 多对多映射

关联关系

类与类之间最普遍的关系就是关联关系。

单向的关联

双向的关联

单向多对一关联

以Emp和Dept为例。

配置单向多对一关联

Emp类中需要添加Dept属性。

1.Dept类

package com.etc.entity;

import java.io.Serializable;

/** 部门持久化类 */
public class Dept implements Serializable { private static final long serialVersionUID = 1L; private Short deptNo;// 部门编号(id属性。在hibernate中,id属性被称为对象标识符---Object Identifier,OID)
private String deptName;// 部门名称
private String location;// 部门所在地区 public Dept() {
} public Dept(Short deptNo, String deptName) {
super();
this.deptNo = deptNo;
this.deptName = deptName;
} public Short getDeptNo() {
return deptNo;
} public void setDeptNo( Short deptNo) {
this.deptNo = deptNo;
} public String getDeptName() {
return deptName;
} public void setDeptName(String deptName) {
this.deptName = deptName;
} public String getLocation() {
return location;
} public void setLocation(String location) {
this.location = location;
} }

  

2.Emp类

package com.etc.entity;

import java.util.Date;

/**员工持久化类*/
public class Emp {
private Integer empNo;// 员工编号
private String empName;// 员工姓名
private String job;// 工作
private Integer mgr;// 所属经理
private Date hireDate;// 入职日期
private Double sal;// 工资
private Double comm;// 奖金
//private Integer deptNo;// 部门编号
private Dept dept;//部门 public Integer getEmpNo() {
return empNo;
} public void setEmpNo(Integer empNo) {
this.empNo = empNo;
} public String getEmpName() {
return empName;
} public void setEmpName(String empName) {
this.empName = empName;
} public String getJob() {
return job;
} public void setJob(String job) {
this.job = job;
} public Integer getMgr() {
return mgr;
} public void setMgr(Integer mgr) {
this.mgr = mgr;
} public Date getHireDate() {
return hireDate;
} public void setHireDate(Date hireDate) {
this.hireDate = hireDate;
} public Double getSal() {
return sal;
} public void setSal(Double sal) {
this.sal = sal;
} public Double getComm() {
return comm;
} public void setComm(Double comm) {
this.comm = comm;
} public Dept getDept() {
return dept;
} public void setDept(Dept dept) {
this.dept = dept;
} }

  

3.Dept.hbm.xml映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.etc.entity.Dept" table="`DEPT`" schema="scott"
dynamic-update="true">
<id name="deptNo" type="java.lang.Short" column="`DEPTNO`">
<!-- 主键生成器替换为increment,hibernate负责增长主键 -->
<!-- <generator class="assigned" /> -->
<generator class="increment"></generator>
</id>
<property name="deptName" type="java.lang.String" column="`DNAME`" />
<property name="location" type="java.lang.String">
<column name="`LOC`"></column>
</property>
</class>
</hibernate-mapping>

  

4.Emp.hbm.xml映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.etc.entity.Emp" table="`EMP`" schema="scott"
dynamic-update="true">
<id name="empNo" type="java.lang.Integer" column="`EMPNO`">
<generator class="increment" />
</id>
<property name="empName" type="java.lang.String" column="`ENAME`" />
<property name="job" type="java.lang.String" column="`JOB`" />
<property name="mgr" type="java.lang.Integer" column="`MGR`" />
<property name="hireDate" type="java.util.Date" column="`HIREDATE`" />
<property name="sal" type="java.lang.Double" column="`SAL`" />
<property name="comm" type="java.lang.Double" column="`COMM`" />
<!-- 员工和部门之间多多对一关系 -->
<many-to-one name="dept" column="`DEPTNO`" class="com.etc.entity.Dept" />
</class>
</hibernate-mapping>

  

<many-to-one>元素建立了Emp表的外健DEPTNO和dept属性之间的映射。包括以下属性:

  • name:持久化类的属性名。
  • column:持久化类属性对应的表的外键。
  • class:持久化类属性的类型。

实现单向多对一关联的持久化操作

添加或者修改Emp对象,外键信息存储在Dept对象中

1.DAO关键代码

	/** 保存员工信息 */
public void save(Emp emp) throws Exception {
this.getSession().save(emp);
}

  

2.BIZ关键代码

     /** 添加员工信息 */
public void addNewEmp(Emp emp) {
Transaction tx = null;
try {
tx = empDao.getSession().beginTransaction();// 开启事务
empDao.save(emp);// 持久化操作
tx.commit(); // 提交事务
} catch (Exception e) {
e.printStackTrace();
if (tx != null) {
tx.rollback();// 回滚事务
}
}
}

  

3.测试方法关键代码

	/** 测试员工 */
public void testAdd() {
// 创建Emp对象
Emp emp = new Emp();
emp.setEmpName("张三");
// 指定员工所在部门
Dept dept = new Dept();
dept.setDeptNo((short) 10);
emp.setDept(dept);
// 保存员工数据
new EmpBiz().addNewEmp(emp);
}

  

4.如果部门存在,生成SQL

Hibernate:
insert
into
scott.
"EMP" ("ENAME", "JOB", "MGR", "HIREDATE", "SAL", "COMM", "DEPTNO", "EMPNO")
values
(?, ?, ?, ?, ?, ?, ?, ?)

  

5.如果部门不存在,将提示错误

WARN - SQL Error: 2291, SQLState: 23000
ERROR - ORA-02291: 违反完整约束条件 (SCOTT.FK_DEPTNO) - 未找到父项关键字

  

按照指定Dept对象查询相关Emp对象

1.DAO关键代码

	/** 按照指定的dept对象查询 */
public List<Emp> listEmps(Dept dept){
String hql="from Emp where dept=?";
return this.getSession().createQuery(hql).setParameter(0, dept).list();
}

  

2.BIZ关键代码

	/** 按照指定的dept对象查询 */
public List<Emp> listEmps(Dept dept) {
Transaction tx = null;
List<Emp> list = null;
try {
tx = empDao.getSession().beginTransaction();
list = empDao.listEmps(dept);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if (tx != null) {
tx.rollback();
}
}
return list;
}

  

3.测试方法关键代码

      /** 测试查询员工 */
public void testList() {
Dept dept = new Dept();
dept.setDeptNo((short) 10);
List<Emp> list = new EmpBiz().listEmps(dept);
for (Emp item : list) {
System.out.println(item.getEmpNo() + "\t" + item.getEmpName());
} }

  

输出指定Emp集合中的所有Emp对象及其关联的Dept对象信息

由于Emp和Demp之间存在单向多对一关系,所以只要调用emp.getDept()方法,就可以方便地从Emp对象导航到Dept对象。

1.DAO关键代码

	/** 从Emp导航到Dept,输出部门信息*/
public List<Emp> listAll(){
String hql="from Emp";
return this.getSession().createQuery(hql).list();
}

  

2.BIZ关键代码

      /** 从Emp导航到Dept,输出部门信息 */
public List<Emp> listAll() {
Transaction tx = null;
List<Emp> list = null;
try {
tx = empDao.getSession().beginTransaction();
list = empDao.listAll();
for(Emp item:list){
System.out.println(item.getEmpNo()+"\t"+item.getEmpName()+"\t"+item.getDept().getDeptName());//会话关闭,无法测试,在biz中测试
}
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if (tx != null) {
tx.rollback();
}
}
return list;
}

  

3.测试方法关键代码

/** 测试查询员工:从Emp导航到Dept */
public void testListAll() {
new EmpBiz().listAll();
}

  

建立双向一对多关联关系

双向关联关系可以很容易从一个对象导航到另外一个对象或者对象的集合。

配置双向一对多关联

在本章单向多对一案例基础上,Dept对象增加员工集合属性

1.Dept类的配置

package com.etc.entity;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set; /** 部门持久化类 */
public class Dept implements Serializable { private static final long serialVersionUID = 1L; private Short deptNo;// 部门编号(id属性。在hibernate中,id属性被称为对象标识符---Object Identifier,OID)
private String deptName;// 部门名称
private String location;// 部门所在地区
private Set<Emp> emps=new HashSet<Emp>();//员工集合:一对多双向关联 public Dept() {
} public Dept(Short deptNo, String deptName) {
super();
this.deptNo = deptNo;
this.deptName = deptName;
} public Short getDeptNo() {
return deptNo;
} public void setDeptNo( Short deptNo) {
this.deptNo = deptNo;
} public String getDeptName() {
return deptName;
} public void setDeptName(String deptName) {
this.deptName = deptName;
} public String getLocation() {
return location;
} public void setLocation(String location) {
this.location = location;
} public Set<Emp> getEmps() {
return emps;
} public void setEmps(Set<Emp> emps) {
this.emps = emps;
} }

  

注意:Hibernate要求在持久化类中定义集合类型属性时,必须把属性声明为接口类型,如Set或者其泛型Set<Emp>。

同时为了避免访问集合出现Null异常,在定义集合属性的使用同时进行实例化。

2.Dept.hbm.xml映射文件的配置

由于在DEPT表中没有直接与emps属性对应的字段,所以不能用<property>元素来映射emps属性,而是使用<set>元素。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.etc.entity.Dept" table="`DEPT`" schema="scott"
dynamic-update="true">
<id name="deptNo" type="java.lang.Short" column="`DEPTNO`">
<!-- 主键生成器替换为increment,hibernate负责增长主键 -->
<!-- <generator class="assigned" /> -->
<generator class="increment"></generator>
</id>
<property name="deptName" type="java.lang.String" column="`DNAME`" />
<property name="location" type="java.lang.String">
<column name="`LOC`"></column>
</property>
<set name="emps">
<!-- set关联持久化类所在表的外键 -->
<key column="DEPTNO" />
<!-- set管理的持久化类 -->
<one-to-many class="com.etc.entity.Emp" />
</set>
</class>
</hibernate-mapping>

  

<set>元素的name属性:设定持久化类的属性名,此处为Dept类的emps属性。<set>元素还包含两个子元素:

  • <key>元素:column属性设定与所关联的持久化类相对应的表的外键,此处为EMP表的DEPTNO字段。
  • <one-to-many>元素:class属性设定所关联的持久化类型,此处为Emp类。

双向一对多关联关系的增删改操作

在双向关联关系中,可以实现对象间的自动化级联处理。

级联关系通过持久化类映射文件中的cascade属性和inverse属性进行控制。

一、cascade属性

在Dept、Emp类和相关映射文件已经编写完成的情况下,需要完成以下工作:

  1. 先创建Dept对象,然后创建一个Emp对象,将两个对象进行关联。最后保存Dept对象,同时自动保存Emp对象。
  2. 删除Dept对象,并级联删除与Dept对象关联的Emp对象。

要完成以上两个持久化操作, 需要在<set>元素中配置<cascade>属性。

在对象一关系映射文件中,用于映射持久化类之间关联关系的元素,如<set>、<many-to-one>都有一个cascade属性。它用于指定如何操纵与当前对象关联的其他对象。

cascade属性值

描           述

none

当Session操纵当前对象时,忽略其他关联的对象。它是cascade属性的默认值

save-update

当通过Session的save()、update()及saveOrUpdate()方法来保存或更新当前对象时,级联保存所有关联的新建的瞬时状态的对象,并且级联更新所有关联的游离状态的对象

merge

当通过Session的merge()方法来保存或更新当前对象时,对其关联对象也执行merge()方法

delete

当通过Session的delete()方法删除当前对象时,会级联删除所有关联的对象

all

包含所有的级联行为

完成第一个持久化工作:添加部门的同时添加员工

1.修改Dept.hbm.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.etc.entity.Dept" table="`DEPT`" schema="scott"
dynamic-update="true">
<id name="deptNo" type="java.lang.Short" column="`DEPTNO`">
<!-- 主键生成器替换为increment,hibernate负责增长主键 -->
<!-- <generator class="assigned" /> -->
<generator class="increment"></generator>
</id>
<property name="deptName" type="java.lang.String" column="`DNAME`" />
<property name="location" type="java.lang.String">
<column name="`LOC`"></column>
</property>
<!-- 级联更新或者保存 -->
<set name="emps" cascade="save-update">
<!-- set关联持久化类所在表的外键 -->
<key column="DEPTNO" />
<!-- set管理的持久化类 -->
<one-to-many class="com.etc.entity.Emp" />
</set>
</class>
</hibernate-mapping>

  

2.DAO关键代码

	//保存Dept时级联保存Emp
public void save(Dept dept) throws Exception{
this.getSession().save(dept);
}

  

3.BIZ关键代码

	// 保存Dept时级联保存Emp
public void save(Dept dept) {
Transaction tx=null;
try{
tx=deptDao.getSession().beginTransaction();
deptDao.save(dept);
tx.commit();
}catch (Exception e) {
e.printStackTrace();
if(tx!=null){
tx.rollback();
}
}
}

  

4.测试方法代码

	// 保存Dept时级联保存Emp
public void testAdd() {
Dept dept = new Dept();
//dept.setDeptNo((short) 80);
dept.setDeptName("开发部");
dept.setLocation("厦门"); Emp emp=new Emp();
emp.setEmpName("李四"); dept.getEmps().add(emp);
//保存部门信息
new DeptBiz().save(dept);
}

  

5.生成相关SQL语句

Hibernate:
select
max("DEPTNO")
from
"DEPT"
Hibernate:
select
max("EMPNO")
from
"EMP"
Hibernate:
insert
into
scott.
"DEPT" ("DNAME", "LOC", "DEPTNO")
values
(?, ?, ?)
Hibernate:
insert
into
scott.
"EMP" ("ENAME", "JOB", "MGR", "HIREDATE", "SAL", "COMM", "DEPTNO", "EMPNO")
values
(?, ?, ?, ?, ?, ?, ?, ?)
Hibernate:
update
scott."EMP"
set
DEPTNO=?
where
"EMPNO"=?

  

完成第二个持久化工作:删除部门对象的同时删除关联员工对象

1.修改Dept.hbm.xml映射文件

		<!-- 级联删除 -->
<set name="emps" cascade="delete">
<!-- set关联持久化类所在表的外键 -->
<key column="DEPTNO" />
<!-- set管理的持久化类 -->
<one-to-many class="com.etc.entity.Emp" />
</set>

  

2.DAO关键代码

     //删除Dept时级联删除Emp
//删除前要根据对象id从数据库加载对象
public Dept load(Serializable id){
return (Dept) this.getSession().load(Dept.class, id);
}
public void delete(Dept dept) throws Exception{
this.getSession().delete(this.load(dept.getDeptNo()));
}

  

3.BIZ关键代码

        // 删除Dept时级联删除Emp
public void delete(Dept dept) {
Transaction tx=null;
try{
tx=deptDao.getSession().beginTransaction();
deptDao.delete(dept);
tx.commit();
}catch (Exception e) {
e.printStackTrace();
if(tx!=null){
tx.rollback();
}
}
}

  

4.测试方法关键代码

	// 删除Dept时级联删除Emp
public void testDel() {
Dept dept = new Dept();
dept.setDeptNo((short)42);
new DeptBiz().delete(dept);
}

  

二.<set>元素的inverse属性

<set>元素的inverse属性的值有两个,即true和false,默认为false。

  • inverse设置为false,则为主动方,由主动方负责维护关联关系,默认是false。
  • inverse设置为true,不负责维护关联关系。

员工调整部门示例。

1.Emp.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- `反单引号:避免表名或者字段名和数据库关键字冲突,以及解决名字之中存在空格等特殊字符 -->
<class name="com.etc.entity.Emp" table="`EMP`" schema="scott"
dynamic-update="true">
<id name="empNo" type="java.lang.Integer" column="`EMPNO`">
<generator class="increment" />
</id>
<property name="empName" type="java.lang.String" column="`ENAME`" />
<property name="job" type="java.lang.String" column="`JOB`" />
<property name="mgr" type="java.lang.Integer" column="`MGR`" />
<property name="hireDate" type="java.util.Date" column="`HIREDATE`" />
<property name="sal" type="java.lang.Double" column="`SAL`" />
<property name="comm" type="java.lang.Double" column="`COMM`" />
<!-- 员工和部门之间多多对一关系 -->
<many-to-one name="dept" column="`DEPTNO`" class="com.etc.entity.Dept" />
</class>
</hibernate-mapping>

  

2.Dept.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- `反单引号:避免表名或者字段名和数据库关键字冲突,以及解决名字之中存在空格等特殊字符 -->
<class name="com.etc.entity.Dept" table="`DEPT`" schema="scott"
dynamic-update="true">
<id name="deptNo" type="java.lang.Short" column="`DEPTNO`">
<!-- 主键生成器替换为increment,hibernate负责增长主键 -->
<!-- <generator class="assigned" /> -->
<generator class="increment"></generator>
</id>
<property name="deptName" type="java.lang.String" column="`DNAME`" />
<property name="location" type="java.lang.String">
<column name="`LOC`"></column>
</property>
<set name="emps">
<!-- set关联持久化类所在表的外键 -->
<key column="DEPTNO" />
<!-- set管理的持久化类 -->
<one-to-many class="com.etc.entity.Emp" />
</set>
</class>
</hibernate-mapping>

  

3.DAO关键代码

/**DeptDAO:加载部门对象*/
public Dept load(Serializable id){
return (Dept) this.getSession().load(Dept.class, id);
} /**EmpDAO:加载员工对象*/
public Emp load(Serializable empNo){
return (Emp) this.getSession().load(Emp.class, empNo);
}

  

4.BIZ关键代码

	/** 调整部门 */
public void changeDept(Integer empNo, Short deptNo) {
Transaction tx = null;
try {
tx = empDao.getSession().beginTransaction();
// 加载Emp和Dept持久化对象
Dept dept = new DeptDao().load(deptNo);
Emp emp = empDao.load(empNo);
// 建立dept和emp对象的关联关系
emp.setDept(dept);
dept.getEmps().add(emp);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if (tx != null) {
tx.rollback();
}
}
}

  

5.测试方法关键代码

	/**测试部门调整*/
@Test
public void testInverse(){
new EmpBiz().changeDept(7369, (short)40);//原来部门编号为20
}

  

6.SQL语句分析

# Hibernate:加载员工对象
select
emp0_."EMPNO" as EMPNO1_1_0_,
emp0_."ENAME" as ENAME2_1_0_,
emp0_."JOB" as JOB3_1_0_,
emp0_."MGR" as MGR4_1_0_,
emp0_."HIREDATE" as HIREDATE5_1_0_,
emp0_."SAL" as SAL6_1_0_,
emp0_."COMM" as COMM7_1_0_,
emp0_."DEPTNO" as DEPTNO8_1_0_
from
scott."EMP" emp0_
where
emp0_."EMPNO"=? # Hibernate:加载部门对象
select
dept0_."DEPTNO" as DEPTNO1_0_0_,
dept0_."DNAME" as DNAME2_0_0_,
dept0_."LOC" as LOC3_0_0_
from
scott."DEPT" dept0_
where
dept0_."DEPTNO"=? # Hibernate:根据部门编号查找员工对象集合 (对应:dept.getEmps().add(emp);语句中的getEmps()生成的SQL)
select
emps0_.DEPTNO as DEPTNO0_1_,
emps0_."EMPNO" as EMPNO1_1_,
emps0_."EMPNO" as EMPNO1_1_0_,
emps0_."ENAME" as ENAME2_1_0_,
emps0_."JOB" as JOB3_1_0_,
emps0_."MGR" as MGR4_1_0_,
emps0_."HIREDATE" as HIREDATE5_1_0_,
emps0_."SAL" as SAL6_1_0_,
emps0_."COMM" as COMM7_1_0_,
emps0_."DEPTNO" as DEPTNO8_1_0_
from
scott."EMP" emps0_
where
emps0_.DEPTNO=? # Hibernate:更新员工的部门编号信息(emp.setDept(dept);语句中emp对象属性发生变化,Hibernate刷新缓存生成的SQL)
update
scott."EMP"
set
"DEPTNO"=?
where
"EMPNO"=?
# Hibernate:更新员工的部门编号信息(dept主动维护数据导致:dept.getEmps().add(emp);语句导致,Hibernate检查到dept对象的emps属性发生变化后,会去维护emp对象的deptno,确保数据库外键的正确)
update
scott."EMP"
set
"DEPTNO"=?
where
"EMPNO"=?

  

问题:hibernate重复执行了update语句,影响了效率。实际上emp.setDept(dept)语句已经正确设定了emp和dept对象的关联关系(dept对象为加载出来持久态对象)。

解决:通过设定<set>元素的inverse属性值为true来解决该问题。

Dept.hbm.xml关键代码:

		<set name="emps" inverse="true">
<!-- set关联持久化类所在表的外键 -->
<key column="DEPTNO" />
<!-- set管理的持久化类 -->
<one-to-many class="com.etc.entity.Emp" />
</set>

  

控制台将只输出一条UPDATE SQL语句。

<set>标签的inverse设置为true后可能导致的问题:

1.仅建立Emp对Dept的关联关系,可以正确更新Emp数据。关键代码如下:

			tx = empDao.getSession().beginTransaction();
// 加载Emp和Dept持久化对象
Dept dept = new DeptDao().load(deptNo);
Emp emp = empDao.load(empNo);
// 仅建立emp对象的关联关系
emp.setDept(dept);
tx.commit();

  

2.如果不建立Emp到Dept对象的关联,导致Emp对象的数据库表中的外键无法得到更新,关键代码如下:

			tx = empDao.getSession().beginTransaction();
// 加载Emp和Dept持久化对象
Dept dept = new DeptDao().load(deptNo);
Emp emp = empDao.load(empNo);
// 仅建立dept对emp对象的关联关系
dept.getEmps().add(emp);
tx.commit();

  

3.inverse属性使用建议

  • 建议关联对象“一”方把inverse设置为true,提高应用性能。
  • 在建立两个对象的双向关联时,应该同时修改两个关联对象的相关属性。
  • 解除双向关联时,也要同时修改关联两端的对象的相应属性。代码如下所示:
			emp.setDept(null);
dept.getEmps().remove(emp);
  • set元素属性的参考配置
<!-- 级联删除、保存和更新,放弃主动维护对象关联 -->
<set name="emps" cascade="all" inverse="true">

  

建立多对多关联关系

多对多数据表SQL语句

SELECT * FROM emp;

DROP TABLE PROEMP;
DROP TABLE PROJECT;
DROP TABLE EMPLOYEE; # 项目表
CREATE TABLE PROJECT(
PROID NUMBER(6),
PRONAME NVARCHAR2(50)
); # 员工表
CREATE TABLE EMPLOYEE(
EMPID NUMBER(6),
EMPNAME NVARCHAR2(50)
); # 项目员工表
CREATE TABLE PROEMP(
RPROID NUMBER(6),
REMPID NUMBER(6)
); --添加约束
ALTER TABLE PROJECT
ADD CONSTRAINT PK_PROID PRIMARY KEY (PROID); ALTER TABLE EMPLOYEE
ADD CONSTRAINT PK_EMPID PRIMARY KEY (EMPID); ALTER TABLE PROEMP
ADD CONSTRAINT PK_PROEMP PRIMARY KEY (RPROID,REMPID); --联合主键 ALTER TABLE PROEMP
ADD CONSTRAINT FK_PROID
FOREIGN KEY(RPROID) REFERENCES PROJECT(PROID); ALTER TABLE PROEMP
ADD CONSTRAINT FK_REMPID
FOREIGN KEY(REMPID) REFERENCES EMPLOYEE(EMPID); --
SELECT * FROM EMPLOYEE;
SELECT * FROM PROJECT;
SELECT * FROM PROEMP;

  

配置单向多对多关联

假设Projcet到Employee为单向多对多关联。

  • Project类中定义员工集合
  • 员工类中不定义和Project相关集合属性

配置

1.Project类

package com.etc.entity;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set; /** 项目实体类 */
public class Project implements Serializable { private static final long serialVersionUID = 1L; private Integer proid;// 项目编号
private String proname;// 项目名称
private Set<Employee> employees = new HashSet<Employee>();// 员工集合 public Project() {
super();
} public Project(Integer proid, String proname) {
super();
this.proid = proid;
this.proname = proname;
} public Integer getProid() {
return proid;
} public void setProid(Integer proid) {
this.proid = proid;
} public String getProname() {
return proname;
} public void setProname(String proname) {
this.proname = proname;
} public Set<Employee> getEmployees() {
return employees;
} public void setEmployees(Set<Employee> employees) {
this.employees = employees;
}
}

  

2.Employee类

package com.etc.entity;

import java.io.Serializable;

/** 员工实体类 */
public class Employee implements Serializable { private static final long serialVersionUID = 1L;
private Integer empid;// 员工编号
private String empname;// 员工姓名 public Employee() {
super();
} public Employee(Integer empid, String empname) {
super();
this.empid = empid;
this.empname = empname;
} public Integer getEmpid() {
return empid;
} public void setEmpid(Integer empid) {
this.empid = empid;
} public String getEmpname() {
return empname;
} public void setEmpname(String empname) {
this.empname = empname;
} }

  

3.Project.hbm.xml映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.etc.entity.Project" table="PROJECT" schema="scott"
dynamic-update="true">
<id name="proid" type="java.lang.Integer" column="PROID">
<generator class="assigned" />
</id>
<property name="proname" type="java.lang.String" column="PRONAME" />
<!-- 项目到员工单向多对多关联 -->
<set name="employees" table="PROEMP" cascade="save-update">
<key column="RPROID" />
<many-to-many class="com.etc.entity.Employee" column="REMPID" />
</set>
</class>
</hibernate-mapping>

<set>元素的table属性指定关系表的名称为PROEMP。

<set>元素的cascade属性为save-update,表明保存或更新Project对象时,会级联保存或更新与它关联的Employee对象。但是不建议设置为“all”或者“delete”,因为删除项目的时候可能会导致删除员工信息。

<set>元素的<key>子元素指定PROEMP的外健RPROID,用来参照PROJECT表。

<many-to-many>子元素的class属性指定employees集合中存放的是Employee对象,column属性指定PROEMP表的外键REMPID , 用来参照EMPLOYEE表。

  

4.Employee.hbm.xml映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.etc.entity.Employee" table="EMPLOYEE" schema="scott"
dynamic-update="true">
<id name="empid" type="java.lang.Integer" column="`EMPID`">
<generator class="assigned" />
</id>
<property name="empname" type="java.lang.String" column="EMPNAME" />
</class>
</hibernate-mapping>

  

持久化操作

创建两个Project对象和两个Employee对象,建立他们之间的关联关系,在保存Project对象的同时保存员工信息。

1.DAO关键代码

	public void save(Project project) throws Exception {
this.getSession().save(project);
}

  

2.BIZ关键代码

	public void addNewProject(Project project) {
Transaction tx = null;
try {
tx = projectDao.getSession().beginTransaction();
projectDao.save(project);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if (tx != null) {
tx.rollback();
}
}
}

  

3.测试方法代码

@Test
public void testAdd() {
Employee employee1=new Employee(1,"张三");
Employee employee2=new Employee(2,"李四"); Project project1 =new Project(1,"1号项目");
Project project2 =new Project(2,"2号项目"); project1.getEmployees().add(employee1);
project1.getEmployees().add(employee2); project2.getEmployees().add(employee1); ProjectBiz pb = new ProjectBiz();
pb.addNewProject(project1);
pb.addNewProject(project2);
}

  

配置双向多对多关联

建立从Project类到Employee类的双向多对多关联,在Project类中需要定义集合类型的employees属性,并且在Employee类中也需要定义集合类型的projects属性。

配置

1.Project类

	private Integer proid;// 项目编号
private String proname;// 项目名称
private Set<Employee> employees = new HashSet<Employee>();// 员工集合

  

2.Employee类

	private Integer empid;// 员工编号
private String empname;// 员工姓名
private Set<Project> projects = new HashSet<Project>(); // 项目集合

  

3.Project.hbm.xml映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.etc.entity.Project" table="PROJECT" schema="scott"
dynamic-update="true">
<id name="proid" type="java.lang.Integer" column="PROID">
<generator class="assigned" />
</id>
<property name="proname" type="java.lang.String" column="PRONAME" />
<!-- 项目到员工双向向多对多关联 -->
<set name="employees" table="PROEMP" cascade="save-update">
<key column="RPROID" />
<many-to-many class="com.etc.entity.Employee" column="REMPID" />
</set>
</class>
</hibernate-mapping>

  

4.Employee.hbm.xml映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.etc.entity.Employee" table="EMPLOYEE" schema="scott"
dynamic-update="true">
<id name="empid" type="java.lang.Integer" column="`EMPID`">
<generator class="assigned" />
</id>
<property name="empname" type="java.lang.String" column="EMPNAME" />
<!-- 员工到项目多对多关系 -->
<set name="projects" table="PROEMP" inverse="true">
<key column="REMPID" />
<many-to-many class="com.etc.entity.Project" column="PPROID" />
</set>
</class>
</hibernate-mapping>

  注意:Employee.hbm.xml的set属性设置为true。

持久化

1.DAO关键代码

	public void save(Project project) throws Exception {
this.getSession().save(project);
}

  

2.BIZ关键代码

	public void addNewProject(Project project) {
Transaction tx = null;
try {
tx = projectDao.getSession().beginTransaction();
projectDao.save(project);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
if (tx != null) {
tx.rollback();
}
}
}

  

3.测试方法关键代码

	@Test
public void testAdd() {
Employee employee1=new Employee(1,"张三");
Employee employee2=new Employee(2,"李四"); Project project1 =new Project(1,"1号项目");
Project project2 =new Project(2,"2号项目"); project1.getEmployees().add(employee1);
project1.getEmployees().add(employee2);
project2.getEmployees().add(employee1); employee1.getProjects().add(project1);
employee1.getProjects().add(project2);
employee2.getProjects().add(project1); ProjectBiz pb = new ProjectBiz();
pb.addNewProject(project1);
pb.addNewProject(project2);
}

  

在实际开发过程中,如果连接表中除了两个外键,还包括其他业务字段,根据业务需要,可以把多对多关联分解为两个一对多关联。

Hibernate-03 关联映射的更多相关文章

  1. Hibernate注解----关联映射注解以及课程总结详解----图片版本

    上一篇,记录了Hibernate注解----类级别注解以及属性注解详解 ,我们这一节主要讲解的是Hibernate注解----关联映射注解以及课程总结详解. 本节的主要内容: 第3章 关联映射注解 3 ...

  2. Hibernate的关联映射——双向1-N关联

    Hibernate的关联映射--双向1-N关联 对于1-N的关联,Hibernate推荐使用双向关联,而且不要让1的一端控制关联关系,而是用N的一端控制关联关系.双线的1-N关联和N-1关联是两种相同 ...

  3. Hibernate的关联映射——单向1-N关联

    Hibernate的关联映射--单向1-N关联 单向1-N关联的持久化类里需要使用集合属性.因为1的一端需要访问N的一端,而N的一端将以集合(Set)形式表现.从这个意义上来看,1-N(实际上还包括N ...

  4. Hibernate的关联映射——单向1-1关联

    Hibernate的关联映射--单向1-1关联 对于单向的1-1关联关系,需要在持久化类里增加代表关联实体的成员变量,并为该成员变量添加setter方法和getter方法.从持久化类的代码上看,单向1 ...

  5. Hibernate的关联映射——单向N-1关联

    Hibernate的关联映射--单向N-1关联 N-1是非常常见的关联关系,最常见的父子关系也是N-1关联,单向的N-1关联只需从N的一端可以访问1的一端. 为了让两个持久化类能够支持这种关联映射,程 ...

  6. Hibernate之关联映射(一对多和多对一映射,多对多映射)

    ~~~接着之前的Hibernate框架接着学习(上篇面试过后发现真的需要学习以下框架了,不然又被忽悠让去培训.)~~~ 1:Hibernate的关联映射,存在一对多和多对一映射,多对多映射: 1.1: ...

  7. 初识Hibernate之关联映射(二)

    上篇我们介绍了关联映射的几种形式,有单向多对一,单向一对多,还有双向一对多.本篇接着介绍有关关联映射的其他几种映射方式,主要有以下几种: 基于外键的单向一对一关联映射 基于主键的单向一对一关联映射 单 ...

  8. Hibernate的关联映射关系

    一:多对一 <many-to-one 1.name:当前类的属性名(关联映射的类) 2.column:属性多对应的类的对应的表的外键(连接条件) 3.class:属性所对应的类的权限定名 4.n ...

  9. 关于Hibernate的关联映射

    何为关联映射 由于数据库的表与表之间存在的管理关系,可以分为一对一,一对多和多对多关联,一般情况下,在数据库设计中是通过表的外键来建立各种关系的,在Hibernate中则把数据库表与表之间的关系数据映 ...

  10. Hibernate框架--关联映射,一对多,多对多 inverse cascade

    回顾Hibernate: 1. hibernate开发环境搭建 ----> 引入jar: hibernate.jar + required + jpa + 驱动包 ---> hiberna ...

随机推荐

  1. Naïve Bayes Models

    贝叶斯模型假设: 为防止概率为零的情况,做拉普拉斯平滑得: 下面介绍一下朴素贝叶斯模型与多变量伯努利模型的区别: 朴素贝叶斯: 多变量伯努利: 即: 多变量伯努利模型不考虑样本出现的次数,每个特征的取 ...

  2. 任务49:Identity MVC:Model前端验证

    任务49:Identity MVC:Model前端验证 前端验证使用的是jquery的validate的组件 _ValidationScriptsPartial.cshtml 在我们的layout里面 ...

  3. EasyUI 表格点击右键添加或刷新 绑定右键菜单

    例1 在HTML页面中设置一个隐藏的菜单(前提是已经使用封装的Easyui) 代码: <div id="contextMenu_jygl" class="easyu ...

  4. 算法练习--LeetCode--54. Spiral Matrix 100%

      Spiral MatrixMedium Given a matrix of m x n elements (m rows, n columns), return all elements of t ...

  5. Gradle系列之一 Groovy语法精讲

    Gradle技术之一 Groovy语法精讲 gradle脚本是基于groovy语言开发的,想要学好gradle必须先要对groovy有一个基本的认识 1. Groovy特点 groovy是一种DSL语 ...

  6. 51nod 1050【DP】

    思路: 就是先正常的dp一下求一个最大连续子串,然后特殊情况就是sum-最小连续子串.. 比一比谁大谁小就好了 #include <stdio.h> #include <string ...

  7. 纯JS实现元素加速运动的函数封装

    //elem:给哪个元素添加位移:direction:是垂直方向的话就传入top,水平方向left:speed控制速度,向下.向右传入正值,反之传入负值:distance表示位移的距离function ...

  8. 工作中常用css样式总结

    一.HTML隐藏文本输入框 有三种方法: 1.<input type="hidden" value=""> 这是对任何元素都起作用的: 2.< ...

  9. CGI、ASP、PHP、JSP、 ASP.NET网站开发语言比较

    一.主流网站开发语言的简介及优缺点. 现在主流的网站开发语言主要包括cgi.asp.php.asp.net.jsp等. HTML:当然这是网页最基本的语言,每一个服务器语言都需要它的支持. (一)  ...

  10. 后缀数组 DC3构造法 —— 详解

    学习了后缀数组,顺便把DC3算法也看了一下,传说中可以O(n)复杂度求出文本串的height,先比较一下倍增算法和DC3算法好辣. DC3 倍增法 时间复杂度 O(n)(但是常数很大)   O(nlo ...