Hibernate 5.3 (四)

本文详细介绍了Hibernate的关联映射,包括单向和双向的各种关联类型,如1-1、1-N、N-1、N-N等。通过外键和关系维护表的方式展示了关联映射的配置,解释了各属性的作用,如column、class、cascade、fetch等,并提供了多个示例来说明如何在实际应用中设置和使用这些关联。此外,文章还讨论了级联操作、延迟加载和外键的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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-1关系了。

使用例子(基于外键)

关联关系:一个人对应一个对应一个地址

持久类从上,不做任何修改。

修改Person.hbm.xml如下:

    <many-to-one name="address" cascade="all" class="Address" fetch="join"  **unique="true"** column="address_id"></many-to-one>

添加unique 之后,表中会生成一个唯一索引来表示。

在从表的Person 会生成主表的外键列address_id,默认指向主表的主键(如果不指定property-ref)。

使用例子(基于关联表)

关联关系:一个人对应一个地址。

持久化类不动,修改Person.hbm.xml:

 <join table="person_address">
    <key column="id" ></key><!-- 映射表参考本实例的主键作为外键列 ,也是该表的主键列-->
     <many-to-one name="address" cascade="all" class="Address" fetch="join"  unique="true"  column="address_id"></many-to-one>
  </join>
基于主键单向1-1

1-1的关联可以基于主键关联,基于主键关联的持久化类不能拥有自己的主键生成器策略,它的主键由关联实体来负责生成

采用基于主键的1一1关联时,应使用 元素来映射关联实体,配置元素时需指定一个name属性,其值为关联实体属性的属性名。除此之外,该元素还可接受如下几个可选属性:

  • class:该属性指定关联实体的全限定类名,默认是通过反射得到该属性所属类的类名。
  • cascade:该属性指定哪些操作会从主表记录级联到子表记录。
  • constrained:表明该类对应的表,和被关联的对象所对应的数据库表之间,通过一个外键引用对主键进行约束。这个选项影响save()和 delete()在级联执行时的先后顺序,以及决定该关联能否被委托(也在 schema export tool中被使用)。
  • fetch:该属性设置抓取策略,该属性接受join(外连接抓取)和 select(选择抓取)两个值的其中之一。
  • property-ref:指定关联类的一个属性,这个属性将会和本类的主键相对应。如果没有指定,默认使用对方关联类的主键。(该值是指定的持关联久化类的某一个属性,该属性主要用在select 的join 查询的时候,比较多,以为两表的联合查询,需要关联某一个字段)
  • access:指定 Hibernate访问该关联属性的访问策略,默认是 property。
  • lazy:指定引用关联实体的延迟加载特性,该属性只能接受 false、 proxy、no- proxy三个值。该属性默认值是 proxy。实际上, Hibernate默认会启动单实例关联(即多对一、一对一)的代理,指定no- proxy即要求该属性应该在实例变量第一次被访问时采用延迟抓取,lazy=" false"指定此关联实体总是被预先抓取。
使用例子
     <id name="id" type="integer" >
    <generator class="foreign"><!--主键的生成原则是参考主表的主键去生成  -->
    <param name="property">address</param><!-- 这里的值要和 <one-to-one>中name一致 -->
    </generator>
    </id>
    <one-to-one name="address" class="Address" cascade="all"></one-to-one>

这样的话,person表主键值会参看address表的主键值。这样也是一种一对一的实现。

单向1-N关联

单向1-N关联的持久化类发生了改变,持久化类里需要使用集合属性。因为1的一端需要访问N的一端,而N的一端将以集合(Set)形式表现。从这个意义上来看,1-N(实际上还包括N-N)和前面的集合映射非常相似,只是将前面用于映射集合元素的 元素改为(里面是映射持久化类)使用元素。

使用元素可以指定如下两个可选属性:

  • class属性:该属性指定了关联实体的类型,默认通过反射来获取关联实体的类型,如果集合属性没有使用泛型,则必须指定该属性。
  • not-found:该属性值只能是 exception或 Ignore,指定当从表记录参照的主表记录不存在时,Hibernate如何处理这种情况。该属性值默认是 exception,即抛出一个异常;如果程序希望Hibernate忽略这个异常,可以指定 not-found=" Ignore
使用例子(基于外键)

关联关系:一个人有多个地址。

修改Person 持久化类,添加如下属性:

private Set<Address> address = new HashSet<Address>();
//自行提供set、get方法
 <set name="address" cascade="all">
      <key column="id"></key><!--映射本实例的外键列,默认关联是本实例的主键, 这个外键是在从表Address 这个表中  -->
      <one-to-many class="Address" not-found="exception"></one-to-many> 
 </set> 
使用例子(基于连接表)

对于有连接表的1 -N关联映射,映射文件不再使用元素映射关联实体,而是使用元素,但为了保证当前实体是1的一端,因此要为元素指定unique=“true”。

使用<many-to-many…/>元素时还可指定如下几个可选属性:

  • class:指定关联实体的类名,默认由Hibernate通过反射来获取该类名。
  • not-found:该属性指定当外键参照的主表记录不存在时如何处理。该属性只接受ignore 和exception两个值,默认是exception, 即不存在时抛出异常;如果程序希望Hibernate忽略这个异常,则可指定该属性值为ignore。
  • formula:指定-一个SQL表达式, 该外键值将根据该SQL表达式来计算。
  • outer-join:指定Hibernate是否要启动外连接来抓取关联实体,该属性只能是true、false 和auto三个值之一,其中true表示启用,false 表示不启用,auto 表示由程序来决定。
  • fetch:该属性指定Hibernate的抓取策略,该属性值只能是join (使用外连接抓取)和select(使用选择抓取)两个值的其中之一。
  • lazy:指定Hibernate是否需要启动延迟加载来加载关联实体。unique:指定本持久化实体是否增加唯一一约束, 默认是false。
  • where: 该属性指定一个SQL表达式, 指定当查询、获取关联实体时的过滤条件,只有满足该where条件的关联实体才会被加载。
  • order-by:该属性用于设置数据库对集合元素排序,该属性仅对1.4或更高版本的JDK有效。该属性的值为指定表的指定字段(一个或几个)加上asc或者desc关键字,这种排序是数据库执行SQL查询时进行的排序,而不是直接在内存中排序。
  • property-ref:指定关联类的一一个属性, 这个属性将会和本类的外键相对应(当外键参照唯一键时需指定该属性)。如果没有指定,直接使用对方关联的主键。
 <set name="address" table="peson_address" cascade="all"><!-- 这里1-N 就是不是使用<join>标签 -->
 <!-- cascade 设置级联更新、级联删除,否则会出现异常,前面讲过的 -->
    <key column="id"></key><!--外键 默认关联本实例person 的主键列,并且作为该表的主键列  -->
    <many-to-many  class="Address" fetch="join" unique="true " column="addres_id"></many-to-many>
    <!--默认的property-ref是指向关联表的主键 -->
 </set>
单向的N-N关联

N-N关联必须使用连接表(为啥要用连接表,不可以用外键呢,你这么想1-N 可以在N方添加1方的主键作为外键,而这时1变成N,外键只能有一个值,当时不可以,必须用关联表了),N-N关联与有连接表的1-N关联非常相似,只要去掉<many-to-many…/>元素的unique= ="true"属性即可。

使用例子(基于关联表)

一个人有多个地址,一个地址属于多个人。这种带有属于关系就是N-N关联。

持久化类和1-N的一样,修改Person.hbm.xml:

 <set name="address" table="peson_address" cascade="all"><!-- 这里1-N 就是不是使用<join>标签 -->
 <!-- cascade 设置级联更新、级联删除,否则会出现异常,前面讲过的 -->
    <key column="id"></key><!--外键 关联person 的主键列,并且作为该表的主键列  -->
    <many-to-many  class="Address" fetch="join"  column="addres_id"></many-to-many>
    <!--默认的property-ref是指向关联表的主键 -->
 </set>
		   Person p = new Person();
		   p.setAge(20);
		   Set<com.example.test.bean.Address> set = new java.util.HashSet<Address>();
		   com.example.test.bean.Address a = new com.example.test.bean.Address();
		   a.setAddressdetail("大中路");
		   com.example.test.bean.Address a1 = new com.example.test.bean.Address();
		   a1.setAddressdetail("大中路1");
		   set.add(a);
		   set.add(a1);
		   p.setAddress(set);
		   Person p1 = new Person();
		   p1.setAge(12);
		   p1.setAddress(set);
		   ss.save(p1);
		   ss.save(p);

生成的关联表中两个外键,都是参考于两张主表的主键。

双向的1-N

双向的1-N关联与N- 1关联是完全相同的两种情形,两端都需要增加对关联属性的访问,N的端增加引用到关联实体的属性,1的一端增加集合属性,集合元素为关联实体。

使用例子(基于外键)

Person 持久化类添加:

	private Set<Address> address = new HashSet<Address>();
	//自行添加set、get方法

Address 持久化类添加:

         private Person p;
         //自行补充set、get方法

修改Person.hbm.xml

<set name="address"  inverse="true">
 <key column="id"></key><!-- 外键 持久化类和集合关联  在集合这张表address中以person表的主键 作为外键 -->
 <one-to-many class="Address"  ></one-to-many>
 </set>

 <set name="address"  inverse="true">
 <!-- 这里我们没有级联操作 ,对于双向的1 - N关联映射,通常不应该允许1的一
 端控制关联关系,而应该由N的一 端来控制关联关系,此时我们可以在<set>元素中指定inverse= ="true",
  用于指定1的一端不控制关联关系-->
 <key column="id"></key><!-- 外键 持久化类和集合关联  在集合这张表address中以person表的主键 作为外键 -->
 <one-to-many class="Address"  ></one-to-many>
 </set>

修改Address.hbm.xml:

 <many-to-one name="p" class="Person" column="id" ></many-to-one><!-- property-ref 指向关联表person 的主键的值 -->
    <!--  底层数据库为了记录这种1 -N关联关系,实际上只需要在N的一端的数据表里增加一个外键列即可。
    这里就存在一个问题:双向映射时<many-to-one.../>元素将映射到外键列,而<set..>元素 里的<key/> 
    子元素也用于映射外键列,其他的都映射同一列,因此映射文件应为这两个元素指定column属性,并让两个column属性值相同。
     一致  -->
                  Person p = new Person();
		   p.setAge(20);
		   ss.save(p);
		   com.example.test.bean.Address a = new com.example.test.bean.Address();
		   a.setAddressdetail("大中路");
		   ss.save(a);
		   com.example.test.bean.Address a1 = new com.example.test.bean.Address();
		   a1.setAddressdetail("大中路1");
		   ss.save(a1);
		   a1.setP(p);//由address 去设置关联关系
		   a.setP(p);

这里在操作的时候,就需要注意,因为没有使用级联操作,所以有些不同,同时需要注意的是:

  1. 首先还是要持久化Person,没有主表,那你持久化从表,怎么参考主表的主键列。
  2. 先设置Person和Address的关联关系,再保存持久化Address对象。如果顺序反过来,程序持久化Address对象时,该Address对象还没有关联实体,所以Hibernate不可能为对应记录的外键列指定值;等到设置关联关系时,
    Hibernate只能再次使用update语句来修改关联关系
    。(自己可以看控制台输出的sql语句,所以不建议上述我的做法,但是这样也是可以的,只是多了update 语句)
  3. 不要通过Person对象来设置关联关系,因为我们已经在Person映射文件的<set…/>元素中指定了inverse=“true”,这表明Person对象不能控制关联关系。(这也是inverse的作用,inverse 也就只有在双向关系,才发挥作用,只给某一方控制权,去维护关联关。如果一方的映射文件中设置为true,说明在映射关系中让对方来维护关系。级联的话控制权主要看,谁包含另外关联对象的引用,谁维持关系,在双向关系中,互相都有引用,那么双方都可以维护关系,开销不就增加了,所以在双向关系中,建议使用inverse
使用例子(基于连接表)

持久化类不变,修改两边的映射文件如下:

 <set name="address"  inverse="true" table="person_address" >
 <key column="person_id"></key><!-- 映射持久化实例的主键作为外键列 -->
 <many-to-many class="Address" unique="true"  column="address_id" ></many-to-many>
 <!-- 映射关联持久化实例的主键作为外键列  -->
 </set>
 <join table="person_address">
     <key column="address_id"></key>   <!--  映射持久化实例的主键,作为外键列,作为该表的主键,因为设置inserve 控制
     权在address 所以address_id 为关联表的主键-->
     <many-to-one class="Person" name="p"  column="person_id"></many-to-one><!--映射关联持久化实例的主键,作为外键列  -->
   </join>

连接表是以address_id作为主键,是因为address_id是唯一的,无需使用person_id 组合作为主键,因为都是参考的是主键,所以肯定不为空,可以作为主键。`

基于连接表的1 -N双向关联类似于N-N关联。1的一端使用集合元素映射,然后在集合元素里增加子元素,该子元素映射到关联类。为保证该实体是1的一端,应该为元素增加unique= ="true"属性。N的一端则使用元素来强制使用连接表。因此将使用<key…>子元素来映射连接表中外键列,且使用来映射连接表中关联实体的外键列。

两边指定连接表的table属性不能省略,且属性值必须相同,否则关联映射出错。

映射文件的两边都指定了两个外键列,一个是持久化类实例在连接表中的外键列名,另一个是关联持久化实例在连接表中的外键列名。 一定要保证两边映射的外键列名对应相同。

双向N-N

先来说一下什么是多对多的关系,举个例子,老师和学生,老师有语文老师,数学老师,英语老师等等,学生可以是1班的学生也可以是2班的学生,对于每个学生而言,他有多个老师给他讲课,而对于每一个老师而言,他要授课的学生也有很多,像这样的情况就可以描述成多对多了。即两个表之间,每一个表中的记录都对应另一个表的部分或全部记录的集合,这种情况就是多对多关系,而单向多对多与双向多对多的不同在于单向只是一方的数据模型有另一方的数据集合属性。

使用例子(基于连接表)

多对多的关系,只能是基于连接表。

在上述了解单向和双向多对多的区别,就需要修改持久化类,在两边都互相定义集合对象:

Person.hbm.xml


    	private Set<Address> address = new HashSet<Address>();
       //自行添加set、get方法

Address.hbm.xml

	private Set<Person> persons = new java.util.HashSet<Person>();
        //自行提供set、get方法
<set name="address"  inverse="true" table="person_address" >
 <key column="person_id"></key><!-- 映射关联表实体的外键列 -->
 <many-to-many class="Address"   column="address_id" ></many-to-many>
 <set table="person_address" name="persons">
   <key column="address_id"></key>
   <many-to-many class="Person" column="person_id"></many-to-many>
   </set>
``

连接表是以两个外键作为联合主键,是因为两个外键都不是唯一的,必须组合起来才是唯一,因为都是参考的是主键,所以肯定不为空,可以作为连接主键。`
   		   Person p = new Person();
	   p.setAge(20);
	   Person p1 = new Person();
	   p1.setAge(12);
	   ss.save(p);
	   ss.save(p1);
	   com.example.test.bean.Address a = new com.example.test.bean.Address();
	   Set<Person> set = new java.util.HashSet<Person>();
	   set.add(p1);
	   set.add(p);
	   a.setAddressdetail("大中路");
	   a.setPersons(set);
	   ss.save(a);

#### 双向的1-1	

与单向1-1关联类似的是,双向1- 1关联也有三种映射策略;基于主键、基于外键、使用连接表。下面分别介绍这三种映射策略。

关系:一个人对应一个地址,一个地址属于一个人。

##### 使用例子(基于外键)

修改持久类,持有对方的引用,并提供set、get方法。
Person.hbm.xml

Address.hbm.xml
<many-to-one name="person" class="Person" unique="true" column="person_id"   ></many-to-one>

上面映射标签,可以互相,主要的区别,就是你把外键设置在哪张表。

上述的映射文件,我主要用property-ref 这个有啥用,这个值是关联持久化类的某个属性,默认是关联持久化类的主键。在查询的时候,通常都是两张表关联查询,肯定要通过某一个字段,就是通过它设置的。

没有使用该属性,查询语句打印:

![在这里插入图片描述](https://img-blog.youkuaiyun.com/20180923162924360?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZlbnVzMzIx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

使用该属性,查询语句打印:
![在这里插入图片描述](https://img-blog.youkuaiyun.com/2018092316300137?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3ZlbnVzMzIx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

两者一比较,你就懂了。
	   Person p = new Person();
	   p.setAge(20);
	   ss.save(p);
	   com.example.test.bean.Address a1 = new com.example.test.bean.Address();
	   a1.setAddressdetail("大中路1");
	   ss.save(a1);
	   a1.setPerson(p);//设置关联关系,同时给外键列赋值,如果设置级联关系,在这设置级联关系,就不需要手动持久化Person,级联会自动帮你处理。
	   Person p1 = ss.load(Person.class, 1);
	   System.out.println(p1.getAddress().getAddressdetail());

##### 使用例子(基于主键)

如果需要采用基于主键的映射策略,则一端的主键生成器需要使用foreign 策略,表明将根据对方的主键来生成自己的主键,本实体不能拥有自己的主键生成策略。当然,任意一端都可以采用foreign 主键生成器策略,表明将根据对方主键来生成自己的主键。使用foreign主键生成策略的实体将不能主动指定主键,必须由关联实体的主键来生成自己的主键值。

在双方持久化类中定义互相的引用,并修改映射文件:

Person.hbm.xml:

<id name="id" type="integer"  column="person_id">
<generator class="foreign">
<param name="property">address</param><!--这里填的值必须是持久化类的属性,这里必须是one to one 中的name 不能直接指定另外持久化类的属性值,如果这样,你并知道关联的是哪个持久化类的实例 -->
</generator>
</id>
<one-to-one name="address"></one-to-one><!-- 指定关联哪个持久化类,其中property-ref 默认是关联持久化的主键 -->

Addres.hbm.xml:

<id name="addressid" column="address_id">
<generator class="identity" ></generator>
</id>
<property name="addressdetail" />
<one-to-one name="person"></one-to-one>

              Person p = new Person();
	   p.setAge(20);
	   com.example.test.bean.Address a1 = new com.example.test.bean.Address();
	   a1.setAddressdetail("大中路1");
	   p.setAddress(a1);// 如果没有该句,报出attempted to assign id from null one-to-one property 
	   //必须设置该句,因为我们映射文件的person的主键 是参看addres 的主键,这里如果设置在Address 参看
	   //person的主键,则需要使用a1.setPerson(p)
	   ss.save(p);
	   ss.save(a1);

##### 使用例子(基于连接表)

持久化类不变,修改映射文件:

Person.hbm.xml:






Address.hbm.xml:

```

我们可以看出上面两个映射文件的配置,是相互对应的,实际上只生成一张表,不要看配置了两个。

带连接表的双向1-1关联,两端都需指定连接表的表名、外键列的列名。两个集合元素,如<onn…/>的table 元素的值必须指定,而且要相同。<join…/>元素的两个子元素:<ke…>和<many-to-on…/>都必须指定column属性,<ke…/>和<many-to-on…> 分别是指定本持久化类、关联类在连接表中的外键列名,因此两边的<ke…>与<many-to-one…>的column属性交叉相同,也就是说,一-端的<join…>元素的<ke…/>的colomn值为a,<many-to-on…/>的column为b,则另一端的<join…>元素的<ke…/>的column值为b,<many-to-one…/>的column值为a.

		   Person p = new Person();
		   p.setAge(20);
		   com.example.test.bean.Address a1 = new com.example.test.bean.Address();
		   a1.setAddressdetail("大中路1");
		   a1.setPerson(p);//设置person和address 关联关系
		   p.setAddress(a1);
		   ss.save(a1);
		   ss.save(p);

Tip

外键:就是从表参照主表的中某一列,所以外键都是在从表中。

   <one-to-one></one-to-one>
   <many-to-one></many-to-one>
   <one-to-many></one-to-many> 
   <many-to-many></many-to-many>
//在这三种标签中,  <many-to-one>、<many-to-many> 还有集合是会产生外键的。可以看出,外键总是产生在多(N)方。外键在表先有一个字段,然后在指定这个字段是参考哪个表的哪个列。 另外,外键这个字段,你需要在代码set、它会根据引用的持久化类的属性值 去设置值,如果没有指定级联,这步需要你自己去操作。

cascade的值:

  • all: 所有情况下均进行关联操作,即save-update和delete。
  • none: 所有情况下均不进行关联操作。这是默认值。
  • save-update: 在执行save/update/saveOrUpdate时进行关联操作。
  • delete: 在执行delete 时进行关联操作。
  • all-delete-orphan: 当一个节点在对象图中成为孤儿节点时,删除该节点。

<many-to-one… ./>或<many-to-many…/>关系中指定级联没什么意义。级联通常在<one-to-one…/>和<one-to-many…/>关系中比较有用一 因为级联操作应该是由主表记录传播到从表记录, 而从表记录则不应该传播到主表记录。
  
这个all-delete-orphan:什么是孤儿节点,举个例子,班级和学生,一张classes表,一张student表,student表中有5个学生的数据,其5个学生都属于这个班级,也就是这5个学生中的外键字段都指向那个班级,现在删除其中一个学生(remove),进行的数据操作仅仅是将student表中的该学生的外键字段置为null,但是班级对它的引用还在,也就是说,则个学生是没有班级的,所以称该学生为孤儿节点,我们本应该要将他完全删除的,但是结果并不如我们所想的那样,所以设置这个级联属性,就是为了删除这个孤儿节点。也就是解决这类情况。

设置级联 cascade 是在产生外键的一方才可以设置,级联帮我们做的,是关联的表的持久化,它自动,不需要手动save(还有删除)。

如果在配置映射文件的时候,有时用连接表,一定要配置所有的属性之后,否则会报错。

我们建立在使用hibernate 配置映射关系的一定要给字段设置column ,不要用默认,很容易出问题

optional:举个例:某项订单(Order)中没有订单项(OrderItem),如果optional 属性设置为false,获取该项订单(Order)时,得到的结果将不显示,如果optional属性设置为true,仍然可以获取该项订单,但订单中 指向订单项的属性值为null。

optional=false 联接关系为inner join。
optional=true 联接关系为left join。

这也是内连接和外连接的区别,就是对条件不符合的时候,处理时不一样。

我们发现在使用映射关系,基于生成表的时候,如果集合标签,直接加table 属性,如果是其他的一律使用join标签,里面在进行相应的配置。

one to many和 many to many 一般是包含在set 和join 中使用的
one to one 和many to one 在class 标签直接是使用

在集合中和join 标签都要使用key 作为外键列,它默认是关联的是本实例的主键

参考学习
参考学习
参考学习
参考学习
参考学习
参考学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值