Hibernate-03 关联映射

学习任务

  • 关联映射
  • 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);
	}

  

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

原文地址:https://www.cnblogs.com/rask/p/8693276.html

时间: 2024-11-07 19:28:09

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

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

上一篇,记录了Hibernate注解----类级别注解以及属性注解详解 ,我们这一节主要讲解的是Hibernate注解----关联映射注解以及课程总结详解. 本节的主要内容: 第3章 关联映射注解 3-1 本章简介 3-2 实体之间的关系 3-3 一对一单向外键关联(一) 3-4 一对一单向外键关联(二) 3-5 一对一双向外键关联 3-6 一对一单向外键联合主键 3-7 多对一单向外键关联(一) 3-8 多对一单向外键关联(二) 3-9 一对多单向外键关联 3-9 一对多双向外键关联 3-10

006——hibernate一对一关联映射

一对一关联映射有两种方式:主键关联和外键关联 以夫妻为例(Husband----Wife) 单向关联实体类: Husband.java package com.java.hibernate; public class Husband { private int id; private String name; private Wife wife; public Wife getWife() { return wife; } public void setWife(Wife wife) { thi

9、Hibernate之关联映射(hibernate映射)

回顾Hibernate: 1. hibernate开发环境搭建 --> 引入jar: hibernate.jar + required + jpa + 驱动包 -> hibernate.cfg.xml -> javabean/*.hbm.xml -> Application 测试 2.hibernate api --> Configuration --> SessionFactory --> Session -> Transaction --->Que

Hibernate的关联映射之1-1关联映射

二.1-1 无论是单向1-1映射关联,还是双休1-1映射关联,都有三种映射策略:基于主键.基于外键.采用连接表. 1.单向1-1映射关联 1.1.基于主键的单向1-1映射关联 对于基于主键的单向1-1关联,基于主键关联的持久化类不能拥有自己的主键生成器策略,它的主键由关联实体来负责生成. 是根据他自己的person属性来获得的.即他通过自身的一个getPerson()方法来获得Person对象.然后通过Person对象中的getID()方法获得id,然后赋值给自身id.这样就可以不需要自己来生成

Hibernate的关联映射之1-N关联映射

三.1-N 对于1-N而言,它的持久化类发生了一点改变,持久化类里需要使用集合属性.因为1的一端需要访问N的一端,而N的一端将以集合(Set)形式表现. 1.单向1-N关联 对于单向的1-N关联关系,只需要在1的一端增加Set类型的属性,该属性记录当前实体的关联实体. 同样以员工-部门为例(Employee-->Department).两个持久化类如下: Department [java] view plain copy print? public class Department { priv

hibernate 之 关联映射的基于外键的双向一对一关联

1. 人和身份证是一个一对一的关系,他们的表结构为: 2. 类结构:Person.java public class Person { private int id; private String name; private int age; private IdCard idCard; public Person() { } public Person(String name, int age) { super(); this.name = name; this.age = age; } pu

hibernate 之 关联映射的双向一对多关联

1. 考虑学生表 和 教师表,表结构和单向的表结构一样. 2. 类结构:Teacher.java public class Teacher { private int id; private String name; private Set<Student> students = new HashSet<Student>(); public Teacher() { } public Teacher(String name) { super(); this.name = name;

hibernate 之 关联映射的基于外键的单向一对一映射

1. 人和身份证是一个一对一的关系,他们的表结构为: 2. 类结构:IdCard.java : public class IdCard { private String id; private String address; public IdCard() { } public IdCard(String id, String address) { super(); this.id = id; this.address = address; } public String getId() { r

hibernate+mysql关联映射师遇到java.lang.StackOverflowError异常

原因在进行关联映射时在多的一方的toString方法中有对属性对应的一的一方的输出 比如: 一的一方: @Entity@Table(name = "app",schema = "", catalog = "game")public class AppEntity { @Id @Column(name = "appId") private String appId; @Basic @Column(name = "app

Hibernate值关联映射

一.关联关系   在软件开发中,类与类中之间最普遍的关系就是关联关系,而且关联是有方向的. 以部门(Dept)和员工(Emp)为例: 一个部门下可以有多个员工,而一个员工只能属于一个部门.所以从Emp到Dept的关联是一对多的关联关系,这就意味着每个Emp对象只会引用一个Dept对象. 从Dept到Emp是一对多的关联关系,这意味着Dept对象会引用一组Emp对象,因此在Dept类中应该定义一个集合类型的属性,来引用所关联的Emp对象. 如果仅有从Emp到Dept对象的关联,或者仅有从Dept到