Hibernate关联关系之一对多
1.一对多关联
1的一方存储Set集合;多的一方存储1方的对象。
注意:Hibernate中的mapping和数据库的主外键约束没有绝对的联系。即使数据库中不设置主外键约束,同样可以在hibernate中设置一对多,或多对一,多对多的映射关系,只要你的表符合外键关联的设计要求就可以。
一对多可以不用放弃维护,多对多一定要有一方放弃维护,否则报错。
inverse维护关系:
用于指示本方是否参与维护关系,设置为true表示不维护,设置false表示维护,默认为false。
本属性一般用于一对多关系中一端,并设置为false。
案例:一个部门有多名员工
数据库表结构
#部门表
CREATE TABLE T_DEPT(
DEPT_ID INT PRIMARY KEY AUTO_INCREMENT COMMENT ‘部门ID’,
DEPT_NAME VARCHAR(30) COMMENT ‘部门名称’
);
#员工表
CREATE TABLE T_EMP(
EMP_ID INT PRIMARY KEY AUTO_INCREMENT COMMENT ‘员工ID’,
EMP_NAME VARCHAR(20) COMMENT ‘员工姓名’,
SALARY FLOAT COMMENT ‘薪水’,
DEPT_ID INT COMMENT ‘部门ID’
#注意:这里DEPT_ID部门ID并没有创建外键关系,这里可以创建,也可以不创建,Hibernate对象关系映射中可指定
);
持久化类:
//部门类
public class Dept{
private int deptID;
private String deptName;
//当前为1的一方,创建Set集合用于存储多方的Emp对象
private Set<Emp> empSet = new HashSet<Emp>();
//构造函数和getter/setter方法
}
//员工类
public class Emp{
private int empId; //员工的编号
private String empName; //员工的名称
private double salary; //员工的薪资
private Dept dept; //员工和部门的关系
//省略构造函数和getter/setter方法
}
映射文件:
//Dept.hbm.xml对象关系映射文件
<hibernate-mapping package="com.zkgin.pojo">
<class name="Dept" table="T_DEPT">
<id name="deptId" column="DEPT_ID">
<generator class="native" />
</id>
<property name="deptName" column="DEPT_NAME" />
<!--映射关系:部门vs员工(一对多)
<set>表示当前类中保存多的set集合;
name指定映射的集合的属性名:empSet;
table指定empSet对应的集合表,如T_EMP,即set集合中保存对象映射的表;
cascade指级联保存、修改、删除,可选项:all,save-update,delete等
fetch指抓取,join表示把关联的对象全部抓取出来
Key指集合表的外键字段(说白了就是多中的外键名,这需要一方和多方共同维护)。
One-to-many指一对多,class指集合中对象类型
-->
<set name="empSet" table="T_EMP" cascade="save-update" fetch="join">
<key column="DEPT_ID" />
<one-to-many class="Emp" />
</set>
</class>
</hibernate-mapping>
// Emp.hbm.xml映射文件:
<hibernate-mapping package="com.zkgin.pojo">
<class name="Emp" table="T_EMP">
<id name="empId" column="EMP_ID">
<generator class="native" />
</id>
<property name="empName" column="EMP_NAME" />
<property name="salary" column="SALARY" />
<!--映射关系:部门vs员工(一对多)
name对应Email类中一方的属性:user
class对应一方的属性对应的类
column表示一和多方共同维护的外键名称(说白了就是多中的外键名,这需要一方和多方共同维护)
-->
<many-to-one name="dept" class="Dept" column="DEPT_ID"></many-to-one>
</class>
</hibernate-mapping>
注意:一对多,一的一方持久化类放Set集合,多的一方持久化类放对象。
添加映射文件:
<mapping resource="com/zking/pojo/Dept.hbm.xml" />
<mapping resource="com/zking/pojo/Emp.hbm.xml" />
CURD操作:
public class HibernateTest {
private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;
@Before
public void init(){
sessionFactory = HibernateUtils.getSessionFactory();
session = sessionFactory.openSession();
transaction = session.beginTransaction();
}
@After
public void destory(){
transaction.commit();
session.close();
sessionFactory.close();
}
@Test
public void TestSave(){
//1. 一对多添加操作
//(1)初始化部门对象
Dept dept = new Dept();
dept.setDeptName("开发部");
//(2)初始化员工对象
Employee emp1 = new Employee();
emp1.setEmpName("张三1");
Employee emp2 = new Employee();
emp2.setEmpName("李四2");
//(3)为部门对象的Set集合中添加员工
dept.getEmps().add(emp1);
dept.getEmps().add(emp2);
//(4)为员工对象添加部门对象
emp1.setDept(dept);
emp2.setDept(dept);
//(5)保存一得一方
session.save(dept);
**注意1**:如果Dept.hbm.xml的关联关系中没有配置级联cascade=”save-update”,则除了保存一得一方外,还要保存多的一方,即还要添加如下两行代码:
session.save(emp1);
session.save(emp2);
**注意2**:session对象提供的saveOrUpdate()方法功能比save()要强大,即可当保存使用,也可当更新使用
session.saveOrUpdate(dept);
}
@Test
public void TestDel(){
/**
* 级联删除:即删除一个Dept对象,会将其对应的所有Emp删除
* 1. 先修改一方的映射文件:Dept.hbm.xml
* <set name="empSet" cascade="delete,save-update">//多个以逗号分隔:save-update,delete
* 2. 执行删除操作
*/
//根据主键ID查找对象
Dept dept = session.get(Dept.class, 2);
//删除对象
session.delete(dept);
}
@Test
public void TestUpdate(){
// 需求:修改主键为2的部门的员工属性
// 1. 根据主键查询Dept对象
Dept dept = session.get(Dept.class, 2);
// 2. 根据主键查询Emp对象
Emp emp = session.get(Emp.class, 1);
// 3. 设置Emp员工对象的属性值
emp.setEmpName(“刘涛”);
dept.getEmpName().add(emp);
注意:如果修改两次,是因为hibernate中双向维护外键,在Dept和Emp里面都需要维护外键,修改Dept时修改一次外键,修改Emp时也修改一次外键,造成效率问题
解决方式:双向维护的情况下,让其中一方放弃外键的维护。谁放弃?外键在哪个表,我们就让哪一方来维护。
具体实现:在放弃关系维护映射文件中,进行配置,在set标签上使用inverse属性。
-------Dept.hbm.xml--------------------------------
<set name="empSet" cascade=”save-update” inverse="true"> //false不放弃true放弃
}
}