文章目录
Hibernate 关联映射
客观世界中的对象很少有孤立存在的,这种实例对象之间的互相访问就是关联关系。
单向关系:只需单向访问关联端。例如,只能通过老师访问学生,或者只能通过学生访问老师。
双向关系:关联的两端可以互相访问。例如,老师和学生之间可以互相访问。
关联映射的类别
单向关联:
- 单向1- 1
- 单向1-N
- 单向N-1
- 单向N-N
双向关联:
- 双向1-1
- 双向1-N
- 双向N-N
注意:双向关系里没有N- 1,因为双向关系1 -N和N- 1是完全相同的
单向 N-1 关联
访问顺序就是应该是从N方去访问1方。
N-1的关系在配置的时候,使用many-to-one元素,必须拥有name属性。
可选属性如下:
- column:该属性指定进行关联的外键列的列名, 该列名默认与该属性同名。
- class:该属性指定关联实体(持久化类)的全限定类名,默认是通过反射得到该属性所属类的类名。
- cascade:该属性指定哪些持久化操作会从主表记录级联到子表记录。
- fetch:该属性指定Hibernate的抓取策略,该属性值只能是join (使用外连接抓取)和select(使用选择抓取)两个值的其中之一。
比如现在有一个学生和班级的关联关系,你要通过某个学生查找班级的关系,下图是对应两个抓取策略的:
- update, insert: 指定对应的字段是否包含在用于Hibernate生成的update或insert语句中,两个属性的默认值都是true。
如果将二者指定为false,则表明这是一个纯粹的外源关联,它的值是通过映射同一个(或多个)字段的其他属性得到的,或由触发器或其他程序来负责生成。(我们正常理解,就是如果设置update、insert 这个属性是不可以插入和更新) - property-ref:指定关联类的一个属性,这个属性将会和本类的外键相对应(当外键参照唯一键时需指定该属性)。如果没有指定,直接使用对方关联类的主键。
- unique:指定Hibernate通过DDL为外键列添加唯一约束。 此外,也可以用做property-ref的目标属性。 这使关联同时具有一对一的效果。
- not-null: 指定使用DDL为外键字段添加非空约束。
- optimistic-lock:该属性指定其更新时是否需要获得乐观锁定,也就是其值决定引用关联实体的属性发生改变时版本值是否增长。
- lazy:指定引用关联实体的延迟加载特性,该属性只能接受false、proxy、 no-proxy 三个值。该属性默认值是proxy。实际上,Hibernate 默认会启动单实例关联(即多对一、一对一)的代理,指定 no-proxy 即要求该属性应该在实例变量第一次被访问时采用延迟抓取,lazy="false"指定此关联实体总是被预先抓取。
- not-found: 该属性指定当外键参照的主表记录不存在时如何处理。该属性只接受ignore 和exception两个值,默认是exception,即不存在时抛出异常;如果程序希望Hibernate忽略这个异常,则可指定该属性值为ignore。
- formula: 指定一一个SQL表达式,该外键值将根据该SQL表达式来计算。
使用例子(基于外键)
关联关系:多个人对应一个地址。
public class Person {
private Integer id;
private Integer age;
private Address address;// 这里的属性不是一个组件属性,而是一个持久化类,这个是和之前不同
//省略set、get方法,需自己补充
}
private Integer addressid;
private String addressdetail;
//省略set、get方法、自行补充
<class name="Person" table="PERSON" dynamic-insert="true" dynamic-update="false">
<id name="id" type="integer" >
<generator class="identity"></generator>
</id>
<property name="age" type="integer"></property>
<!-- 这里的N-1 是持久类之间的映射关系 ,N对应的是从表,1 对应的是主表-->
<!-- column 该属性的列名-->
<!--cascade 这里的级联,对应到数据库中是级联更新和级联删除,级联更新是在更新的时候,优先更新主表,在更新从表,级联删除是在删除的时候,先删除从表,在删除主表 -->
<many-to-one name="address" cascade="all" class="Address" column="address_id"></many-to-one>
<!-- 在这里address 对应的属性应该映射成外键列,这里实际还要指定该外键列和主表哪一列关联property_ref指定,默认是和主表的主键关联 -->
</class>
<class name="Address" table="ADDRESS">
<id name="addressid" column="address_id">
<generator class="identity" ></generator>
</id>
<property name="addressdetail" />
</class>
Person p = new Person();
p.setAge(20);
com.example.test.bean.Address a = new com.example.test.bean.Address();
a.setAddressdetail("大中路");
p.setAddress(a);
ss.save(p);//在前面的配置文件中,我已经说过N对应的是从表,1对应的是主表,我们这里认为
//一个1个地址,对应多个人,person是从表,你在插入从表的时候需要依照主表的相关联的键插入,可以
//在这看address 主表,还没有持久化,只是一个瞬态,所以抛出异常TransientObjectException。解决的办法就是在从表的映射文件
//中添加cascade="all",这时会先执行插入主表,然后插入从表,就不会有问题的。
注意:从表的插入更新都是相对于主表的,参看不了主表,你搞个捶捶。(删除的时候,就要反着来,先删除从表,咋在删主表)
上面这个例子在处理单向N-1的关系的时候,在N方加入一个外键,即可实现关联,但是有时也会在数据库新建一个表来维护这种关系,看下面的例子。
使用例子(基于关系维护表)
关联关系:多个人对应一个地址。
修改Person.hbm.xml
<join table="person_address"><!-- table 用于指定关联表 -->
<key column="id" ></key><!-- 映射表参考主表的主键作为外键列,也是该表的主键列 -->
<many-to-one name="address" cascade="all" class="Address" fetch="join" column="address_id"></many-to-one>
</join>
这时生成的关联表,两个字段都是以外键列存在的。
使用<join…>元素映射连接表时还需要外键关联, 应在配置文件中增加<ke…/>子元素来映射外键,并为join元素增加<many-to-on…/>子元素,用于映射N- 1的关联实体。该<many-to-on…/> 子元素的用法与不使用连接表的<many-to-on…/>子元素的用法完全相同。
单向的1-1
访问的顺序:你在某个持久类A中定义另外一个持久化类B的实体,顺序就是A-B,A是从表,B是主表。
单向的1-1 关系和单向N-1的映射配置基本类似,只需要在标签中 增加元素unique ,控制N端唯一,不就是1-