[键入文字]
的集合对象也是一样的道理。
九、关联关系中的高级应用 1.级联操作:
在前面的一对多操作中已经有所体现,现在回到前面的一对多操作中,进行一些修改,修改内容如下:
Set
depart.setEmps(emps); 去掉这段注释,并把“s.save(emp1); s.save(emp2);”注释掉,然后运行发现出错。原因是:当我们保存Department depart对象时,发现它所关联到的两个Employee emp对象并未保存,而这种要依赖别的对象的操作即是级联操作,级联操作主要体现在关联关系中,且是可配置的,只是默认的级联操作是被设为”none”,而如果我们希望进行级联操作,只需要在
cascade=\属性后,再来执行会发现正常,原因是Hibernate进行了级联操作,相当Hibernate帮我们写了注释掉的“s.save(emp1); s.save(emp2)”这两句内容。下面列表说明级联操作的可选值。 cascade属性值 功能作用描述 none 在保存、修改对象时,不对与之关联的对象进行任何操作,它作为cascade默认值。 save-update 当保存或修改对象时,级联保存所有与之关联的临时对象,级联更新所有与之关联的脱管(游离)对象。 delete 当删除时,级联删除所有与之关联的对象 all 包括save-update和delete两种行为 delete-orphan 删除所有和本对象解除关联关系的对象,主要表现在“一对多”中。 all-delete-orphan 包括all和delete-orphan的行为 说明:(1)cascade可以在
这些关联关系中,“级联”都是可配的在一般的对象中不能配置。
(2)可以为其配置多个属性值,中间用逗号分隔。如cascade=”save-update,delete”.
(3)一般只在“一对一 一对多”配置级联,而不会在“多对一 多对多”中配置级联,但在配置时最好的方法是根据实际情况进行配置。现举例说明:比如在“部门和员工”之间,如果我们希望部门取消时,属于该部门的员工并不会被取消,而是分到的别的部门,我们就不应为cascade配置“delete”值。
2.inverse属性: inverse的作用:询问是否放弃维护关系,默认是false,即不放弃维护关系,即要维护关系。下面借助前面的“一对多”实例来详谈“维护关系问题”。
static void add() {
Session s = null;
Transaction tx = null; try {
Department depart = new Department(); depart.setName(\ Employee emp1 = new Employee(); emp1.setName(\
emp1.setDepart(depart);//员工维护它们之间的关系
Employee emp2 = new Employee(); emp2.setName(\ emp2.setDepart(depart);
Set
emps.add(emp2); //员工维护它们之间的关系
depart.setEmps(emps); //部门维护它们之间的关系
- 21 - / 42
[键入文字]
}
s = HibernateUtil.getSession(); tx = s.beginTransaction(); s.save(emp2); s.save(emp1); s.save(depart);
//以上的代码的书写顺序分两种情况讨论 tx.commit(); } finally {
if (s != null) s.close(); }
下面借助上面的代码来进行分析:一种情况下是员工维护它们之间的关系。其中的关键代码为已在上面说明
(即emp1.setDepart(depart),emp2.setDepart(depart)):此时,我们再注释掉部门维护关系的代码(即注释掉depart.setEmps(emps);),然后执行;执行后再交换s.save(emp2); s.save(emp1);s.save(depart);的保存顺序,再执行。
第二种情况是,部门维护它们之间的关系,需注释掉员工维护关系,再分保存的顺序讨论。 第三种情况是,共同维护它们之间的关系。 下面再列表说明: 先保存员工,再保存部门 先保存部门,再保存员工 由于是员工维护关系,当我们先由于先保存了部门信息,然后再来保存员工维护关系 保存员工再保存部门时,会自动员工时,便能获知部门信息,所以只进把部门信息更新到员工表中,这行了插入操作,不会有任何更新操作。 时会有两条更新操作。 由于是部门来维护关系,不论怎样的保存顺序,最终都会有两条更新操作。部门维护关系 原因是我们要更新的是员工表,当我们最终保存好部门和员工后,都会再把部门信息更新到员工表中。 共同维护关系 4条更新操作,结合上面分析 2条更新操作,结合上面分析。 分析了上面的维护关系情况,下面再来讨论由谁来维护关系:举例,在“一对多”中,如果让“一”来维护关系,相对就低效,如果让“多”来维护关系,则会在效率上有很大的提高。再据此举个生活中的例子,一个老师会有多个学生,如果让老师来维护关系,让老师来记住学生,这将会很难,但如果我们让学生来维护关系,即让每个学生来记住老师,这样将会使效率上有很大的提高,所以通常在关联关系中,我们总是会让“多”这方来维护关系,而让“一”这方放弃维护关系。因此我们需要在“一”的一方配置“inverse”属性,让它来放弃维护关系。为什么要一方放弃维护关系?在上面的例子中,我们在讨论让双方共同维护关系时,会发现有重复的操作,这就是为什么要提出放弃维护关系的原因,在前面的“多对多”实例中,如果让双方都来维护关系将会报错,原因很简单:“多对多”采取的中间表关联,而双方维护关系,将会试图在中间表插入重复的记录,当然是不可行的,至于在“一对多”等关联关系中可以双方维护关系是因为它采取的是更新外键操作。 总结:在“一对多”中,维护关系表现为更新外键,而“多对多”,维护关系表现为在“中间表”中增减记录。 下面我们修改Department.hbm.xml文件,在
注意:inverse只出现在集合类型的标签中,但不能在list等有序集合中进行配置。 3.继承关系:
在前面的部门员工实例中,我们设定的员工只是普通的员工,现在假如有Sale和Skill两类员工,它们作为Employee的子类。如何实现这些子类的映射?为了方便以后的操作说明和不影响以前的操作,我们把前面用到的员工部门类及相关实体配置文件,主配置文件等拷贝到一个新的项目theExtend下进行操作。
(1)共享一张表:
意思是我们把子类的当作父类来处理共同映射成一张表。
>>步骤一,创建Employee的子类:Sale和Skill。内容如下:
package com.asm.hibernate.domain;
- 22 - / 42
[键入文字]
public class Sale extends Employee { private String signSale; ...省略getXXX setXXX() }
package com.asm.hibernate.domain; public class Skill extends Employee{ private String signSkill; ...省略getXXX setXXX() }
>>步骤二、修改Employee.hbm.xml配置文件:
配置文件说明: 理解
到的“类”,下面的
>>步骤三、修改主配置文件,由于是拷贝的前面的文件,所以需要去掉无关的映射文件,否则会提示找不到这此
映射文件。
>>步骤四、编写测试类:
package com.asm.hibernate.test; public class ManyToOneTest {
public static void main(String[] args) { add();
Employee emp=query(2);
System.out.println(\ }
static Employee query(int empId) { Session s = null; try {
s = HibernateUtil.getSession();
Employee emp = (Employee) s.get(Employee.class, empId);
System.out.println(\ return emp;
- 23 - / 42
[键入文字]
}
} finally {
if (s != null) s.close(); } }
static void add() { Session s = null;
Transaction tx = null; try {
Department depart = new Department(); depart.setName(\ Employee emp = new Employee(); emp.setName(\ emp.setDepart(depart); Sale emp2 = new Sale();
emp2.setName(\ emp2.setSignSale(\ emp2.setDepart(depart); Skill emp3 = new Skill();
emp3.setName(\ emp3.setSignSkill(\ emp3.setDepart(depart);
s = HibernateUtil.getSession(); tx = s.beginTransaction(); s.save(emp); s.save(emp2); s.save(emp3); s.save(depart); tx.commit(); } finally {
if (s != null) s.close(); } }
说明:没的什么可多言的,只是要注意在查询时能返回其子类型。 下面来看执行后employee表的内容:
+----+------+---------------+-----------+----------+-----------+ | id | sign | name | depart_id | signSale | signSkill | +----+------+---------------+-----------+----------+-----------+ | 1 | 0 | empName | 1 | NULL | NULL | | 2 | 1 | saleEmployee | 1 | saleName | NULL | | 3 | 2 | skillEmployee | 1 | NULL | skillName | +----+------+---------------+-----------+----------+-----------+
先来看sign这列:由于sign是鉴别类型设定的字段,且分别在前面为Employee、Sale、Skill分别配置了“0、1、2”所以它们会在sign体现出来。其实 再来看“signSale、signSkill”字段:它们本身是专为特定的类的属性配置的字段(比如在第一个 - 24 - / 42 [键入文字] 充,这也就是如果采取“共享一张表”的最大缺点,它限制了我们不能在子类属性所映射的字段上设定“非空”。由于查询只涉及到一张表,所以效率较高。 (2)每个子类一张附表: 意思是每个类均会有一张表,但是它不是完整的表,因为它的一些字段还在父类的表中。即是说:公共字段放在父表中,子类子类分别放在子类所映射的表中,它们之间采取主外键关联。这样解决了上面的“不能设定为空”的缺限。接上面只需修改Employee.hbm.xml配置文件,修改后的内容如下: 配置文件说明:当每个子类都会有一张表,在class子元素下设定了 指定为子类映射成一张表,特别要说明的是我们为每个子类都配置了 (3)联合使用表: 意思是同时使用(1)(2)的形式,主要目的是为了能使用鉴别类型,但同时也能“设定为非空”。同样只需要修改配置文件,修改后的内容如下: - 25 - / 42