hibernate持久化类映射文件*.hbm.xml

通过 POJO 类的数据库映射文件,Hibernate可以理解持久化类和数据表之间的对应关系,也可以理解持久化类属性与数据库表列之间的对应关系。

在运行时 Hibernate 将根据这个映射文件来生成各种 SQL 语句。

hibernate-mapping 是 hibernate 映射文件的根元素,其属性如下:

schema: 指定所映射的数据库schema的名称。若指定该属性, 则表明会自动添加该 schema 前缀。

catalog:指定所映射的数据库catalog的名称。

default-cascade(默认为 none): 设置hibernate默认的级联风格. 若配置 Java 属性, 集合映射时没有指定 cascade 属性, 则 Hibernate 将采用此处指定的级联风格。

default-access (默认为 property): 指定 Hibernate 的默认的属性访问策略。默认值为 property, 即使用 getter, setter 方法来访问属性. 若指定 access, 则 Hibernate 会忽略 getter/setter 方法, 而通过反射访问成员变量。

default-lazy(默认为 true): 设置 Hibernat morning的延迟加载策略. 该属性的默认值为 true, 即启用延迟加载策略. 若配置 Java 属性映射, 集合映射时没有指定 lazy 属性, 则 Hibernate 将采用此处指定的延迟加载策略。

auto-import (默认为 true): 指定是否可以在查询语言中使用非全限定的类名(仅限于本映射文件中的类)。

package (可选): 指定一个包前缀,如果在映射文档中没有指定全限定的类名, 就使用这个作为包名。

根元素的子节点class子节点

class 元素用于指定类和表的映射,其属性如下:

name:指定该持久化类映射的持久化类的类名

table:指定该持久化类映射的表名, Hibernate 默认以持久化类的类名作为表名

dynamic-insert: 若设置为 true, 表示当保存一个对象时, 会动态生成 insert 语句, insert 语句中仅包含所有取值不为 null 的字段. 默认值为 false

dynamic-update: 若设置为 true, 表示当更新一个对象时, 会动态生成 update 语句, update 语句中仅包含所有取值需要更新的字段. 默认值为 false

select-before-update:设置 Hibernate 在更新某个持久化对象之前是否需要先执行一次查询. 默认值为 false

batch-size:指定根据 OID 来抓取实例时每批抓取的实例数

lazy: 指定是否使用延迟加载

mutable: 若设置为 true, 等价于所有的 <property> 元素的 update 属性为 false, 表示整个实例不能被更新. 默认为 true

discriminator-value: 指定区分不同子类的值. 当使用 <subclass/> 元素来定义持久化类的继承关系时需要使用该属性

映射对象标示符(主键)

Hibernate 使用对象标识符(OID) 来建立内存中的对象和数据库表中记录的对应关系。对象的 OID 和数据表的主键对应. Hibernate 通过标识符生成器来为 OID 赋值。

Hibernate 推荐在数据表中使用代理主键, 即不具备业务含义的字段.。代理主键通常为整数类型, 因为整数类型比字符串类型要节省更多的数据库空间。

在对象-关系映射文件中, <id> 元素用来设置对象标识符. <generator> 子元素用来设定标识符生成器.

id:设定持久化类的 OID 和表的主键的映射,其属性如下:

name: 标识持久化类 OID 的属性名 

column: 设置标识属性所映射的数据列的列名(主键字段的名字).

unsaved-value:若设定了该属性, Hibernate 会通过比较持久化类的 OID 值和该属性值来区分当前持久化类的对象是否为临时对象

type:指定 Hibernate 映射类型. Hibernate 映射类型是 Java 类型与 SQL 类型的桥梁. 如果没有为某个属性显式设定映射类型, Hibernate 会运用反射机制先识别出持久化类

的特定属性的 Java 类型, 然后自动使用与之对应的默认的 Hibernate 映射类型Java 的基本数据类型和包装类型对应相同的 Hibernate 映射类型. 基本数据类型无法表达

null, 所以对于持久化类的 OID 推荐使用包装类型

映射组成关系

Hibernate 把持久化类的属性分为两种:

       ①值(value)类型:没有 OID, 不能被单独持久化, 生命周期依赖于所属的持久化类的对象的生命周期。

       ②实体(entity)类型: 有 OID, 可以被单独持久化, 有独立的生命周期。

组成关系理解:实体类型的持久化类中包含有值类型的持久化类型属性。

譬如:Employee类中不仅有Integer类型id、String类型的员工name、int类型的员工age等属性,还有一个工资属性,工资属性的类型是Pay,Pay是另外一个实体类,

           Employee与Pay之间就可以称之为组成关系。那么要映射这种关系,<property/>标签是不行的,hibernate中提供了<component/>标签来映射这种关系。

<property name="age" column="AGE" type="int" length="3"/>
  <!-- 映射组成关系 -->
  <component name="pay" class="Pay">
   <property name="monthPay" column="MONTH_PAY"/>
   <property name="yearPay" column="YEAR_PAY" />
   <property name="vocationWithPay" column="VOCATION_WITH_PAY"/>
  </component> 
</class>

 

映射多对一关联关系

单向 N - 1

单向 n-1 关联只需从 n 的一端可以访问 1 的一端

域模型:从 Order 到 Customer 的多对一单向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中无需定义存放 Order 对象的集合属性。

关系数据模型:ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键。

Hibernate 使用 <many-to-one> 元素来映射多对一关联关系。

<many-to-one name="customer" class="Customer" column="CUSTOMER_ID" />

映射流程理解

在映射单向的关联关系时,只需在多的一端做如下操作即可:

1、在其持久化类中添加声明类型为另一端持久化类的属性,并为其提供get、set方法。private Customer customer;

2、在其持久化类映射文件中使用<many-to-one>标签映射上述属性即可。

<many-to-one name="customer" class="Customer" column="CUST_ID" />
 其中name属性用来指定被映射的属性名称,class属性用来标识该属性的持久化类名, column属性用来指定本映射文件对应数据表中外键列的名称,
 引用的是当前被映射属性对应持久化类对应数据表的主键作为外键。 其中class属性可以省略不写,如果省略了,hibernate会通过反射找到name属性值对应持久化类 的名称,

 因为多执行了一系列代码,所以会影响hibernate的整体执行效率,所以建议不要省略。 不仅是class属性,在配置文件中的所有属性尽量不要省略,原因和class属性一样。

注意:保存的时候,要先保存 ‘一’ 端,再保存N端,这样可以减少一次update操作

 

双向 1 - N

双向 1-n 需要在 1 的一端可以访问 n 的一端, 反之依然。

域模型:从 Order 到 Customer 的多对一单向关联需要在Order 类中定义一个 Customer 属性,而在 Customer 类中需定义存放 Order 对象的集合属性。

关系数据模型:ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键。

当 Session 从数据库中加载 Java 集合时,创建的是 Hibernate 内置集合类的实例, 因此在持久化类中定义集合属性时必须把属性声明为 Java 接口类型。

Hibernate 的内置集合类具有集合代理功能,支持延迟检索策略。

事实上, Hibernate 的内置集合类都封装了 JDK 中的集合类,这使得 Hibernate 能够对缓存中的集合对象进行脏检查,按照集合对象的状态来同步更新数据库。

在定义集合属性时,通常把它初始化为集合实现类的一个实例。这样可以提高程序的健壮性,避免应用程序访问取值为 null 的集合的方法而抛出 NullPointerException。

private Set<Order> orders = new HashSet<Order>();  get/set方法。

Hibernate 使用 <set> 元素来映射 set 类型的属性

   <!-- 映射一对多关联关系 -->

                    set类型属性          对应数据表名称           放弃主动维护表关系
  <set name="orders" table="ORDER_TABLE" inverse="true">

                                            数据表关联列名称
           <key><column name="CUSTOMER_ID"></column></key>

                                属性对应持久化类
           <one-to-many class="Order"/>
  </set>

<set>元素的name属性用来设定待映射的持久化类的属性。

<key> 元素设定与所关联的持久化类对应的表的外键,其column属性用来指定关联表的外键名。

<one-to-many> 元素设定集合属性中所关联的持久化类,其 class属性用来指定关联的持久化类的类名。

<set> 元素的 inverse 属性

在hibernate中通过对 inverse 属性来决定是由双向关联的哪一方来维护表和表之间的关系。 inverse = false 的为主动方,inverse = true 的为被动方, 由主动方负责维护关联关

。在没有设置 inverse=true 的情况下,两边都维护表和表之间关系,即默认情况。

在 1 - N 关系中,将 n 方设为主控方将有助于性能改善(譬如校长记住所有学生的名字,不太可能,但要让所有学生记住校长的名字,就容易的多)。

在 1 - N 关系中,若将 1 方设为主控方会额外多出 update 语句。

插入数据时无法同时插入外键列,因而无法为外键列添加非空约束。

<set> 元素有一个 order-by 属性, 如果设置了该属性, 当 Hibernate 通过 select 语句到数据库中检索集合对象时, 利用 order by 子句进行排序。

<set> 元素的 cascade 属性:

在对象 – 关系映射文件中, 用于映射持久化类之间关联关系的元素,<set>、<many-to-one> 和 <one-to-one> 都有一个 cascade 属性,,

它用于指定如何操纵与当前对象关联的其他对象。

 

 

对于映射双向多对一关联关系的理解

在映射双向一对多的关联关系时,只需在单向的基础上添加如下操作即可:

1、在'一'这一端的持久化类中添加另一端持久化类的Set<E>集合属性,并为其提供get、set方法。private Set<Order> orders = new HashSet<Order>();
2、在'一'这一端的持久化类映射文件中使用<set>标签映射该属性。

  <set name="orders" table="ORDER_TABLE" inverse="true">
   <key column="CUST_ID"></key>
   <one-to-many class="Order"/>
  </set>
 其中name属性用来指定被映射的属性名称,table属性用来指定被映射属性变化时将反映或者作用到表的名称,
inverse="true"表示'一'这一端放弃主动维护表关系,用来提高hibernate效率。
<key column="CUST_ID"></key>用来设定与所关联的持久化类对应数据表的外键,column属性用来指定外键的名称。
该名称与另一端映射文件中<many-to-one>标签的column属性值保持一致,<many-to-one name="customer" class="Customer" column="CUST_ID" />

 


映射一对一关联关系 

 

对于使用外键的方式映射一对一关联关系的理解

在映射一对一关联关系时,如果采用外键的方式进行映射,需做如下操作:

1、在两端持久化类中分别添加对方类型的关联属性,并为其提供get、set方法。

 Department类:private Manager manager;
 Manager类:private Manager department;

2、选择任意一端存放外键,在存放外键的这一端使用<many-to-one>标签映射属性,并添加unique="true"属性来表示为 1-1 关联。

 <many-to-one name="department" class="Department" column="DEPT_ID" unique="true"/>

 其中,name属性用来指定关联属性名称,class属性指定关联属性对应的持久化类名,

column属性指定关联外键的名称。引用的是class属性指定持久化类对应数据表的主键作为外键。

3、在不存放外键的一端使用one-to-one元素映射属性,该元素使用 property-ref 属性指定使用

被关联实体主键以外的字段作为关联字段(查询时关联表之间关联时使用的关联键)。

 <one-to-one name="manager" class="Manager" property-ref="department"/>  其中,name属性用来指定关联属性名称,class属性指定关联属性对应的持久化类名。

基于主键的映射策略:指一端的主键生成器使用 foreign 策略,表明根据“对方”的主键来生成自己的主键,自己并不能独立生成主键.。

                                    <param> 子元素指定当前持久化类的那个属性作为 “对方”。

                                   

采用foreign主键生成器策略的一端增加 one-to-one元素映射关联属性,其 one-to-one 属性还应增加 constrained=“true” 属性(用来生成外键约束);

另一端增加 one-to-one 元素来映射关联属性。<one-to-one name="dept" class="Department" ></one-to-one>

constrained(约束):指定为当前持久化类对应的数据库表的主键添加一个外键约束,引用被关联的对象(“对方”)所对应的数据库表主键。

<one-to-one name="mgr" class="Manager" constrained="true"></one-to-one>

对于使用主键方式映射一对一关联关系的理解

在映射一对一关联关系时,如果采用主键的方式进行映射,需做如下操作:

1、在两端持久化类中分别添加对方类型的关联属性,并为其提供get、set方法。

 Department类:private Manager manager;
 Manager类:private Manager department;

2、选择任意一端的主键生成策略采用foreign,在这端使用<one-to-one>标签映射属性(Manager.class),并为其添加constrained="true"属性,用来为该主键生成外键约束。
 <id name="managerId" column="MANAGER_ID">
   <generator class="foreign">
    <param name="property">department</param>
   </generator>
 </id>
 其中<param>子元素用来指定当前持久化类的哪个属性作为对方。hibernate通过反射找到其对应持久化类对应数据表的主键作为当前持久化类对应数据表的主键。
 <one-to-one name="department" class="Department" constrained="true"/>
其中,name属性用来指定关联属性名称,class属性指定关联属性对应的持久化类名,

constrained属性用来通知hibernate为当前映射文件对应的持久化类对应的数据表主键添加外键约束,引用的是class属性指定持久化类对应数据表的主键作为其外键。
 
3、在主键不采用foreign生成策略的一端使用one-to-one元素映射属性(Department.class)

<one-to-one name="manager" class="Manager" ></one-to-one>   其中,name属性用来指定关联属性名称,class属性指定关联属性对应的持久化类名。


映射多对多关联关系 

 单向 N-N

n-n 的关联必须使用连接表(中间表)

与 1-n 映射类似,必须为 set 集合元素添加 key 子元素,指定 CATEGORIES_ITEMS 表中参照 CATEGORIES 表的外键为 CATEGORIY_ID。 与 1-n 关联映射不同的是,

建立 n-n 关联时, 集合中的元素使用 many-to-many. many-to-many 子元素的 class 属性指定 items 集合中存放的是 Item 对象,column 属性指定  CATEGORIES_ITEMS 表

中参照 ITEMS 表的外键为 ITEM_ID。

 对映射单向多对多关联关系的理解

在映射单向多对多的关联关系时,必须使用连接表(中间表),需做如下配置:

1、在其中一端添加Set<E>集合属性,并为其提供get、set方法,其中E是另一端持久化类。private Set<Item> items = new HashSet<Item>();
2、在添加集合属性类对应的映射文件中,使用<set>元素映射上述集合属性。

<set name="items" table="CATEGORY_ITEM_TABLE" order-by="ITEM_ID">
   <key column="CATE_ID"/>
   <many-to-many class="Item" column="ITEM_ID"/>
 </set>
 其中name属性用来指定被映射的属性名称,table属性用来指定中间表的名称,
 <key column="CATE_ID"></key>用来设定中间表参照本映射文件对应数据表的外键作为主键,column属性用来指定中间表主键的名称。

<many-to-many class="Item" column="ITEM_ID"/>用来标识中间表参照class属性值指定的持久化类对应数据表的外键作为主键,并用column属性值作为主键名称。
 class属性值用来指定<set>元素的name属性指定的集合对象中存放的对象类型。

双向多对多

在映射双向多对多的关联关系时,必须使用连接表(中间表),在单向多对多的基础上需做如下配置:

1、在没有Set属性对象的一端添加Set<E>集合属性,并为其提供get、set方法,其中E是另一端持久化类。 private Set<Category> categories = new HashSet<Category>();
2、在添加集合属性类对应的映射文件中,使用<set>元素映射上述集合属性。

<set name="categories" table="CATEGORY_ITEM_TABLE" inverse="true">
   <key column="ITEM_ID"></key>
   <many-to-many class="Category" column="CATE_ID"/>
 </set>
 其中name属性用来指定被映射的属性名称,table属性用来指定中间表的名称,
 <key column="ITEM_ID"></key>用来设定中间表参照本映射文件对应数据表的外键作为主键, column属性用来指定中间表主键的名称。
 <many-to-many class="Category" column="CATE_ID"/>
 用来标识中间表参照class属性值指定的持久化类对应数据表的外键作为主键,并用column属性值作为主键名称。
 class属性值用来指定<set>元素的name属性指定的集合对象中存放的对象类型。
 注意:

 1、在双向 多对多关联的两边都需指定连接表的表名及外键列的列名。两个集合元素 set 的 table 元素的值必须指定,而且必须相同(同一张中间表当然要相同了)。
       set元素的两个子元素:key 和 many-to-many 都必须指定 column 属性,其中,key 和 many-to-many 分别指定本持久化类和关联类在连接表中的外键列名,

       因此两边的    key 与 many-to-many 的column属性交叉相同。也就是说,一边的set元素的key的 cloumn值为a, many-to-many 的 column 为b;    

       则另一边的 set 元素的 key 的 column 值 b,many-to-many的 column 值为 a. 

2、对于双向 n-n 关联,须把其中一端的 inverse 设置为 true,否则可能会造成主键冲突。

 

 

映射继承关联关系(Hibernate支持三种继承映射策略)

1、<subclass>标签的方式

采用 subclass 的继承映射可以实现对于继承关系中父类和子类使用同一张表。

因为父类和子类的实例全部保存在同一个表中,因此需要在该表内增加一列,

使用该列来区分每行记录到低是哪个类的实例----这个列被称为辨别者列(discriminator)。

在这种映射策略下,使用 subclass 来映射子类,使用 class 或 subclass 的 discriminator-value 属性指定辨别者列的值

所有子类定义的字段都不能有非空约束。如果为那些字段添加非空约束,那么父类的实例在那些列其实并没有值,

这将引起数据库完整性冲突,导致父类的实例无法保存到数据库中。

若采用subclass的方式映射继承关系,则需做如下配置:

1、只需为父类添加对应的hbm.xml文件,使用<subclass>标签映射子类,其配置如下:

<class name="Person" table="Person_TABLE" discriminator-value="PERSON">

<id name="personId" column="PERSION_ID">
   <generator class="native"/>
  </id>
  <!-- 添加辨别者列,最好放到id元素下 -->
  <discriminator column="TYPE_VALUE"/>
  <property name="name" column="NAME" length="10"/>
  <subclass name="Student" discriminator-value="STUDENT">
   <property name="school" column="SCHOOL" length="30"/> 
  </subclass>
 </class>
 
其中,<discriminator column="TYPE_VALUE"/>用来添加辨别者列,column属性指定其列名。

discriminator-value="STUDENT"属性用来指定辨别者列的值。

2、<joined-subclass>标签的方式

采用 joined-subclass 元素的继承映射可以实现每个子类一张表。

采用这种映射策略时,父类实例保存在父类表中,子类实例由父类表和子类表共同存储。因为子类实例也是一个特殊的父类实例,因此必然也包含了父类实例的属性。

 于是将子类和父类共有的属性保存在父类表中,子类增加的属性,则保存在子类表中。

在这种映射策略下,无须使用鉴别者列,但需要为每个子类使用 key 元素映射共有主键,该主键必须与父类标识属性的列名相同。但如果继承树的深度很深,可能查询一个子

类实例时,需要跨越多个表,因为子类的数据一次保存在多个父类中。子类增加的属性可以添加非空约束。因为子类的属性和父类的属性没有保存在同一个表中。

若采用joined-subclass的方式映射继承关系,则需做如下配置:

1、只需为父类添加对应的hbm.xml文件,使用<joined-subclass>标签映射子类,其配置如下:

<class name="Person" table="Person_TABLE">

<id name="personId" column="PERSION_ID">
   <generator class="native"/>
  </id>
   <property name="name" column="NAME" length="10"/>
  <joined-subclass name="Student" table="STUDENT_TABLE">
   <!-- 映射和父类共有的主键 -->
   <key column="PER_ID"/>
   <property name="school" column="SCHOOL" length="30"/>
  </joined-subclass>
 </class>
 

其中,<key column="PER_ID"/>用来标识使用父类表的主键作为子类表的主键,column属性指定子类主键列名。

3、<union-subclass>标签的方式(推荐方式)

采用 union-subclass 元素可以实现将每一个实体对象映射到一个独立的表中。

子类增加的属性可以有非空约束 -- 即父类实例的数据保存在父表中,而子类实例的数据保存在子类表中。

子类实例的数据仅保存在子类表中, 而在父类表中没有任何记录。

在这种映射策略下,子类表的字段会比父类表的映射字段要多,因为子类表的字段等于父类表的字段、加子类增加属性的总和。

在这种映射策略下,既不需要使用鉴别者列,也无须使用 key 元素来映射共有主键。使用 union-subclass 映射策略是不可使用 identity 的主键生成策略,

因为同一类继承层次中所有实体类都需要使用同一个主键种子,

即多个持久化实体对应的记录的主键应该是连续的。 受此影响,也不该使用 native 主键生成策略, 因为 native 会根据数据库来选择使用 identity 或 sequence。

对不能使用identity主键生成策略的理解:

子类和父类只有一个hbm.xml映射文件,hibernate的查询可能依赖此文件,只是猜测,有待考证。

若采用union-subclass的方式映射继承关系,则需做如下配置:

1、只需为父类添加对应的hbm.xml文件,使用<union-subclass>标签映射子类,其配置如下:

<class name="Person" table="Person_TABLE">
  <id name="personId" column="PERSION_ID">
   <generator class="hilo"/>
  </id>
  <property name="name" column="NAME" length="10"/>
   <union-subclass name="Student" table="STUDENT_TABLE">
   <!-- 直接映射属性 -->
   <property name="school" column="SCHOOL" length="30"/>
  </union-subclass>
  </class>
 
其中,<discriminator column="TYPE_VALUE"/>用来添加辨别者列,column属性指定其列名。

discriminator-value="STUDENT"属性用来指定辨别者列的值

三种方式的比较

 

 

 

 

 

 

待续。。


 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值