一,概述
需求:员工与部门,一个部门有多个员工,多个员工可以在同一个部门,这是典型的一对多与多对一关系.这种关系在Hibernate中怎样映射呢?(Hibernate中这种映射叫关联映射)
二,设计JavaBean
1)部门
package com.bighuan.b_one2many;
import java.util.HashSet;
import java.util.Set;
public class Dept {
private int deptId;
private String deptName;
/**
* 一对多,一个部门对应多个员工
*/
private Set<Employee> emps=new HashSet<Employee>();
public Dept(){
}
public int getDeptId() {
return deptId;
}
public void setDeptId(int deptId) {
this.deptId = deptId;
}
public String getDeptName() {
return deptName;
}
public void setDeptName(String deptName) {
this.deptName = deptName;
}
public Set<Employee> getEmps() {
return emps;
}
public void setEmps(Set<Employee> emps) {
this.emps = emps;
}
}
2)员工
package com.bighuan.b_one2many;
public class Employee {
private int empId;
private String empName;
private double salary;
/**
* 多对一:多个员工对应一个部门
*/
private Dept dept;
public Employee(){
}
public int getEmpId() {
return empId;
}
public void setEmpId(int empId) {
this.empId = empId;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Dept getDept() {
return dept;
}
public void setDept(Dept dept) {
this.dept = dept;
}
}
三,JavaBean对应的映射文件
1)Dept.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.bighuan.b_one2many">
<!-- 对象与表对应 -->
<class name="Dept" table="t_dept">
<!-- 属性与字段对应 -->
<!-- 主键 ,映射-->
<id name="deptId">
<generator class="native" />
</id>
<!-- 属性映射 -->
<property name="deptName" length="20"></property>
<!--
一对多关联映射配置 (通过部门管理到员工)
Dept 映射关键点:
1. 指定 映射的集合属性: "emps"
2. 集合属性对应的集合表: "t_employee"
3. 集合表的外键字段 "t_employee. dept_id"
4. 集合元素的类型
dept_id需要和Employ.hbm.xml中<many-to-one>中的column一样
-->
<set name="emps" table="t_employee"><!-- table="t_employee" 可以省略 -->
<key column="dept_id"></key>
<one-to-many class="Employee"/>
</set>
</class>
</hibernate-mapping>
2)Employee.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.bighuan.b_one2many">
<!-- 对象与表对应 -->
<class name="Employee" table="t_employee">
<!-- 属性与字段对应 -->
<!-- 主键 ,映射-->
<id name="empId">
<generator class="native" />
</id>
<!-- 属性映射 -->
<property name="empName" length="20"></property>
<property name="salary" type="double"></property>
<!--
多对一映射配置
Employee 映射关键点:
1. 映射的部门属性 : dept
2. 映射的部门属性,对应的外键字段: dept_id
3. 部门的类型
many-to-one可以做到将一个对象映射生成一个外键字段
-->
<many-to-one name="dept" column="dept_id" class="Dept"></many-to-one>
</class>
</hibernate-mapping>
(hibernate.cfg.xml在前面几篇关于Hibernate的博客里都有介绍,不粘贴了)
四,测试
1)插入数据
package com.bighuan.b_one2many;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.junit.Test;
import com.bighuan.a_collection.User;
public class App {
private static SessionFactory sf;
static {
sf = new Configuration()
.configure()
.addClass(Dept.class)
.addClass(Employee.class)//测试时用
.buildSessionFactory();
}
//保存:部门方,一的一方进行操作
@Test
public void testSave() throws Exception {
// 获得session
org.hibernate.Session session = sf.openSession();
session.beginTransaction();
// 部门对象
Dept dept = new Dept();
dept.setDeptName("宣传部");
// 员工对象
Employee emp_zs = new Employee();
emp_zs.setEmpName("张三");
Employee emp_ls = new Employee();
emp_ls.setEmpName("李四");
// 关系
dept.getEmps().add(emp_zs);
dept.getEmps().add(emp_ls);
// 保存(与保存顺序无关)
session.save(emp_zs);
session.save(emp_ls);
session.save(dept); // 保存部门,部门下所有的员工
session.getTransaction().commit();
session.close();
}
//保存:员工方,多的一方进行操作,推荐使用多的一方进行保存数据
@Test
public void testSave2() throws Exception {
// 获得session
org.hibernate.Session session = sf.openSession();
session.beginTransaction();
// 部门对象
Dept dept = new Dept();
dept.setDeptName("战略部");
// 员工对象
Employee emp_zs = new Employee();
emp_zs.setEmpName("张三");
Employee emp_ls = new Employee();
emp_ls.setEmpName("李四");
// 关系
emp_zs.setDept(dept);
emp_ls.setDept(dept);
session.save(dept);
session.save(emp_zs);
session.save(emp_ls);
session.getTransaction().commit();
session.close();
}
}
2)既可以通过部门方来保存数据,也可以通过员工方来保存数据.通过部门方保存数据执行了如下sql语句:
Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
Hibernate: insert into t_dept (deptName) values (?)
Hibernate: update t_employee set dept_id=? where empId=?
Hibernate: update t_employee set dept_id=? where empId=?
而通过员工方保存数据则执行了更少的sql语句,如下:
Hibernate: insert into t_dept (deptName) values (?)
Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
Hibernate: insert into t_employee (empName, salary, dept_id) values (?, ?, ?)
通过对比可以得知,通过多的一方来保存数据可以减少update语句,因此开发中建议通过多的一方来保存数据,减少sql语句的执行,提高Hibernate效率.
五,inverse与cascade属性
1)Inverse属性,是在维护关联关系的时候起作用的, 表示控制权是否转移。(在一的一方起作用)
Inverse , 控制反转;Inverse = false 不反转;当前方有控制权;inverse= true 控制反转,当前方没有控制权.
2)维护关联关系中,是否设置inverse属性:
a. 保存数据
有影响。
如果设置控制反转,即inverse=true, 然后通过部门方维护关联关系。在保存部门的时候,同时保存员工, 数据会保存,但关联关系不会维护。即外键字段为NULL.
b. 获取数据
无影响.
c. 解除关联关系?
有影响。
inverse=false,可以解除关联;inverse=true,前方(部门)没有控制权,不能解除关联关系(不会生成update语句,也不会报错).
d. 删除数据对关联关系的影响?
有影响。
inverse=false, 有控制权, 可以删除。先清空外键引用,再删除数据。
inverse=true, 没有控制权: 如果删除的记录有被外键引用,会报错,违反主外键引用约束! 如果删除的记录没有被引用,可以直接删除。
3)cascade 属性
cascade 表示级联操作 【可以设置到一的一方或多的一方】
none 不级联操作, 默认值
save-update 级联保存或更新
delete 级联删除
save-update,delete 级联保存、更新、删除
all 同上。级联保存、更新、删除
六,总结
配置一对多与多对一,这种叫“双向关联”;只配置一对多,叫“单项一对多”;只配置多对一, 叫“单项多对一”.
注意:配置了哪一方,哪一方才有维护关联关系的权限!