Hibernate O/R映射(实体映射) 实体映射作为类与表之间的联系纽带。 1.实体映射基础(Hibernate中类/表映射、属性/字段映射的基本技术) 类表映射主要包括: a)表名——类名映射:<class name="" table="">...</class> b)主键映射:<id name="" column="" type=""><generator class=""/></id> ①. id标签,标识符,自动增加,一般不会对其操作(包括删除、修改等)。 name="id" 声明了Java属性的名字 - Hibernate会使用getId()和setId()来访问它。 column属性则告诉Hibernate, 我们使用EVENTS表的哪 个字段作为主键。 嵌套标签generator说明id生成策略。一般为native。 ②. generator 元素用来设定标识符生成器,hibernate提供了多种内置的实现。
increment | 由Hibernate自动以递增的方式生成标识符,每次增量为1. |
identity | 由底层数据库生成标识符,前提是底层数据库支持自动增长字段类型。 |
sequence | hibernate根据底层数据库的序列生成标识符,前提是底层数据库支持序列。 |
hilo | |
native | 根据底层数据库对自动生成标识符的支持能力,选择identity、sequence或hilo。 |
uuid.hex | hibernate采用128位的UUID算法来生成标识符。 |
assigned | 由java应用程序来生成标识符。 |
c)字段映射:<property name="" type="" column=""/> ①.是除id外声明其他属性所需标签,对于一些非保留关键字属性可以直接映射成为相同名称的字段。type有时可省略,此时Hibernate会试着去确定转换类型和映射类型,但可能出现转换错误。
Java类型 | Hibernate类型 | SQL类型 |
---|---|---|
java.lang.String | string | Varchar |
int | int | int |
char | character | char(1) |
boolean | boolean | boolean |
java.lang.String | text | text |
byte[] | binary | blob |
java.sql.Date | date | date |
java.sql.Timestamp | timstamp | timstamp |
数据库提供的主键生成机制,一般是通过一个内部表保存当前主键状态(如对自增型主键,保存的是当前的最大值和递增量),操作时,先读取最大值,然后加上递增量,作为新的主键,更新到内部表中。 2.高级映射技术(自定义数据类型、复合主键,特殊字段的相关映射技术) a) 自定义数据类型(????) b)复合主键(???) c)Blob、Clob字段的映射:(???) 3.实体映射策略?? 4.延迟加载 对于Query接口的list()方法与iterator()方法来说,都可以实现查询,但是list()返回的每个对象都是完整的(对象中的每个属性都被表中的字段填充了),而iterator()方法返回的对象中仅仅包含了主键值,只有当对iterator()中的对象进行操作时,Hibernate才会向数据库再次发送查询语句获取该对象的属性值,所有list()方法的效率更高。所有iterator()这种形式叫做延迟加载。
cascade属性值 | 描述 |
none | 在保存、更新或删除当前对象时,忽略其他关联对象。是默认值 |
save-update | 当通过Session的save()、update()以及saveOrUpdate()方法来保存或更新当前对象时,级联保存所有关联的新建的临时对象,并级联更新所有关联的游离对象。 |
delete | 当通过Session的delete()方法删除当前对象时,级联删除所有关联对象。 |
all | 包含save-update及delete的行为。对当前对象执行evict()或lock()操作时,对关联的持久化对象也会执行相同的操作。 |
delete-orphan | 删除所有和当前对象解除关联关系的对象。 |
all-delete-orphan | 包含all和delete-orphan的行为。 |
5.Hibernate检索策略 5.1.立即检索策略: 即lazy=''false" 在一对多关联级别中一般使用延迟检索 默认立即检索策略的缺点 1).select语句的的数目太多,需要频繁的访问数据库,会影响检索性能。 如需查询n个所需对象,则需执行n+1次select查询语句。这种检索策略没有利用SQL的连接查询。在一对多中,若无需访问关联对象,则加载关联对象会耗费内存。
5.2.延迟检索策略: 即lazy=''true"
优点:由应用程序决定需要加载那些对象,可避免执行多余的select语句,以及避免加载应用程序不需要访问的对象,提高了检索性能,并节省了内存空间。
缺点:应用程序若想访问游离状态的代理类实例,必须保证在持久化状态时已被初始化。
适用范围:
一对多或者多对多关联
5.3左外连接检索策略
默认情况下,多对一关联级别使用左外连接检索策略
优点:a.对应用程序完全透明,不管对象处于持久化还是游离状态,应用程序都可以方便的从一个对象导航到与他关联的对象。
b.使用了外连接,select语句数目少。
缺点:a.可能回加载应用程序不需访问的对象,浪费内存; b.复杂的数据库表也会影响检索性能。
适用范围: a.多对一或者一对一关联。
b. 应用程序需立即访问的对象; c.数据库系统具有良好的表连接性能。
6.数据关联 6.1一对一关联<one-to-one> (1)主键关联:两张关联表通过主键形成一对一映射关系。cascade 级联,默认使用了左外连接的策略(即fetch=“join”, fetch=“select”表示使用立即检索,即默认是立即加载,两条单独的select语句,若要修改为延迟加载,则需为<one-to-one>标签添加属性constrained=“”,在主控方添加lazy=“true”)(待加载一方)。
主控方:
<class name="A" table="t_a">
<id name="" column="" type="">
<generator class="native"/>
</id>
<property name=""/>
<one-to-one name="b" class="B" cascade="all"/>
</class>
被控方:
<class name="B" table="t_b">
<id name="" column="" type="">
<generator class="foreign"> <param name="property">a</param>
</generator> foreign主键生成
</id> <property name=""/> <one-to-one name="a" class="A" constrained="true"/>constrained约束 </class>
由于采用了主键关联方式,则通过主键关联的两张表,其关联记录的主键值须保持同步。 (2)唯一外键关联:由<many-to-one>节点定义,唯一外键关联的一对一关系只是多对一关系的一个特例。
//主控方:
<class name="A" table="t_a">
<id name="" column="" type="">
<generator class="native"/>
</id>
<property name=""/>
<many-to-one name="b" class="B" column="" unique="true"/>
</class>
//被控方:
<class name="B" table="t_b">
<id name="" column="" type="">
<generator class="native">
<param name="property">a</param>
</generator>
</id>
<property name=""/>
<one-to-one name="a" class="A" property-ref="b"/>
</class>
当只有主控方的时候是单向关系,当被控方中也有一个属性是主控方类型的,则形成了双向关联。 6.2一对多关联 (1)单向一对多关系:只需在“一”方进行配置。
主控方:
<class name="A" table="t_a" dynamic-update=“true” dynamic-insert=“true”> dynamic级联更新
<id name="" column="" type="">
<generator class="native"/>
</id>
<property name=""/>
<set name="b" table="t_b" cascade="all' order-by="">
<key column=""/>
<one-to-many class="B"/>
</set>
</class>
由于是单向关联,为了保持关联关系,只能通过主控方对被控方进行级联更新。 但更新时会存在id空问题。 (2)双向一对多关系:需要在关联双方均加以配置。 Hibernate中的延迟加载,当我们在程序中获取到了“一”的一方,但是不需要多的一方,则可以使用延迟加载。
是“一对多”与“多对一”关联的组合,在主控方配置单向一对多关系的基础上,在被控方配置与其对应的多对一关系。
主控方:
<class name="A" table="t_a" dynamic-update=“true” dynamic-insert=“true”>
<id name="" column="" type="">
<generator class="native"/>
</id>
<property name=""/>
<set name="b" table="t_b" inverse="true" inverse控制方向反转
cascade="all' lazy="false" order-by="">
<key column=""/>
<one-to-many class="B"/>
</set>
</class>
被控方:
<class name="B" table="t_b" dynamic-update=“false” dynamic-insert=“false>
<id name="" column="" type="">
<generator class="native">
<param name="property">a</param>
</generator>
</id>
<property name=""/>
<many-to-one name="a" class="A" update="true" insert="true" cascade="none".../>
</class>
主控方的inverse被设为“true”,将不是主控方,将关联关系的维护工作交给了管理对象B来完成。即关联关系中inverse=“false”的为主控方。
6 .3多对多关联 需要借助中间表完成多对多映射信息的保存。应避免使用,根据情况采取延迟加载来避免无谓的性能开销。
<class name="com.xx.xxx.Student" table="tb_student">
<id name="id" column="id" type="string">
<generator class="uuid"/>
</id>
<property name="name" column="name" type="string"/>
<set name"courses" table="student_course" cascade="save-update">
<key column="student_id"/>
<many-to-many class="com.xx.xxx.Course" column="course_id"/>
</set>
</class>
注意:其中set标签中table表示中间表自动生成,cascade表示级联,key标签表示中间表中与当前表关联的列,<many-to-many> 中class表示当前表与另一个表的多对多关系,column表示中间表中与另一个表关联的列
6.4.Map 映射 6.4.1.Map键值对——值为简单类型变量
<class name="com.xx.xxx.Team" table="team">
<id name="id" column="id" type="string">
<generator class="uuid"/>
</id>
<property name="name" column="name" type="string"/>
<map name"students" table="student">
<key column="team_id"/>
<index column="name" type="string">key值</index>//指定的是map中的key值
<element column="description" type="string">value值</element>//指定的是map中的value值
</map> </class>
6.4.2.Map键值对——值为对象类型变量
<class name="com.xx.xxx.Team" table="team">
<id name="id" column="id" type="string">
<generator class="uuid"/>
</id>
<property name="name" column="name" type="string"/>
<map name"students" table="student" cascade="all">
<key column="team_id"/>
<index column="card_id" type="string">key值</index>
<one-to-many class="com.xx.xxx.Student" />
</map>
</class>
<class name="com.xx.xxx.Student" table="student">
<id name="id" column="id" type="string">
<generator class="uuid"/>
</id>
<property name="name" column="name" type="string"/> <property name="age" column="age" type="int"/>
<property name="cardId" column="card_id" type="string"/>
<many-to-one name="team" column="team_id" class="com.xx.xxx.Team"/>
</class>
6.5.Set 映射
<class name="com.xx.xxx.Team" table="team">
<id name="id" column="id" type="string">
<generator class="uuid"/>
</id>
<property name="name" column="name" type="string"/>
<set name"students" table="student">
<key column="team_id"/>
<element column="name" type="string" />
</set>
</class>
Map与Set标签中的elecment子标签映射的是原子类型,即能够直接映射到数据库表字段上的类型,而one-to-many映射的是实体类型。
6.6.List映射
<class name="com.xx.xxx.Team" table="team">
<id name="id" column="id" type="string">
<generator class="uuid"/>
</id>
<property name="name" column="name" type="string"/>
<list name"students" table="student" cascade="all">
<key column="team_id"/>
<index column="index" ></index>
<ont-to-many calss="com.xx.xxx.Student" />
</list>
</class>
其中<index>标签指定list中的顺序,<list>标签中“一”的一方不能添加invalse=“true”,因为在<index>标签有序,不能将维护交给关联方。
6.7.Bag映射 结合了List和Set,可以重复且没有顺序的一种集合,是由Hibernate提供的。
Hibernate使用LIst来模拟Bag,相对于List,少了<index>标签
<class name="com.xx.xxx.Team" table="team">
<id name="id" column="id" type="string">
<generator class="uuid"/>
</id>
<property name="name" column="name" type="string"/>
<bag name"students" table="student" cascade="all" inverse="true">
<key column="team_id"/>
<ont-to-many calss="com.xx.xxx.Student" />
</bag>
</class>
6.8.联合主键映射规则 两种实现方式分别为:将联合主键与其他属性放在同一类中;或者是将联合主键提取放在单独类中
1).实体类:类中的每个主键属性都对应数据表中的主键列,Hibernate要求具有联合主键的实体类实现Serializable接口,并重写hashCode()方法和equals()方法,重写这两个方法的原因在于Hibernate要根据数据库的联合主键来判断某两行记录是否是一样的,若一样,则为同一个对象,若不同,则为两个对象,这反映到程序中就是根据haseCode与equals方法来判断某两个对象是否能够放到诸如Set这样的集合中。实现Serializable接口的原因在于使用get或load方法的时候需要先构建出来该实体的对象,并且将查询依据(联合主键)设置进去,然后作为get或load方法的第二个参数传递进去即可。
2).映射文件:
<class name="com.xx.xxx.Student" table="tb_student">
<composite-id>
<key-property name="cardId" column="card_id" type="string" />
<key-property name="name" column="name" type="string" />
</composite-id>
<property name="age" column="age" type="int"/>
</class>
<class name="com.xx.xxx.Student" table="tb_student">
<composite-id name="studentPrimaryKey" class="com.xx.xxx.studentPrimaryKey">
<key-property name="cardId" column="card_id" type="string" />
<key-property name="name" column="name" type="string" />
</composite-id>
<property name="age" column="age" type="int"/>
</class>
6.9.组件映射 在一个类中包含另一个类
<class name="com.xx.xxx.Student" table="tb_student">
<id name="id" column="id" type="string">
<generator class="uuid"/>
</id>
<property name="name" column="name" type="string"/>
<component name"address" class="com.xx.xxx.Adress">
<property name="homeAddress" type="string"/>
<property name="schoolAddress" type="string"/>
</component>
</class>
6.10.继承映射 1).每个子类一张表:每个子类一个hbm文件,父类没有hbm映射信息。即父类没有对应的表,父类中的字段相应的加在了每个子类中。 2).一张表存储继承体系中的所有类的信息:即将继承体系中的所有字段放在一张表中。
需要在hbm文件中增加<discriminator column="" type=""></ discriminator >,区分子类等信息。
<class name="com.xx.xxx.Person" table="person">
<id name="id" column="id" type="string">
<generator class="uuid"/>
</id>
<discriminator column="personType" type="String"/>
<property name="name" column="name" type="string"/>
<subclass name"com.xx.xxx.Student" discriminator-value="student">
<property name="cardId" type="string"/>
</subclass>
<subclass name"com.xx.xxx.Teacher" discriminator-value="teacher">
<property name="salary" type="int"/>
</subclass>
</class>
3).公共信息放在父类表中,独有信息放在了子类表中,每个子类对应一张表。查询等操作需涉及两张表。
<class name="com.xx.xxx.Person" table="person">
<id name="id" column="id" type="string">
<generator class="uuid"/>
</id>
<property name="name" type="string"/>
<joined-subclass name"com.xx.xxx.Student" discriminator-value="student">
<key column="id"/>
<property name="cardId" type="string"/>
</joined-subclass>
<joined-subclass name"com.xx.xxx.Teacher" discriminator-value="teacher">
<key column="id"/>
<property name="salary" type="int"/>
</joined-subclass>
</class>
6.10.关联映射 要考虑到关联方向(directionality),阶数(multiplicity)和集合(collection)的行为
6.10.1.单向Set-based的关联
用Java的集合类(collection):Set,因为set 不包含重复的元素及与我们无关的排序。
对于多对多关联(或叫n:m实体关系), 需要一个关联表(association table)。
<set name="events" table="person_event">
<key column="person_id"/>
<many-to-many column="event_id" class="events.Event"/>
</set>
其中name是该类中的一个属性,对应于一个关联表,表名是由set元素的table属性配置的。前者用<key>,后者用<many-to-many>元素的column属性定义。同时告知其所在的类。
值类型的集合
<set name="emailAddresses" table="person_email_addr">
<key column="person_id"/>
<element column="email_addr" type="string"/>
</set>
比较这次和此前映射的差别,主要在于element部分,这次并没有包含对其它实体引用的集合,而是元素类型为String的集合(在映射中使用小写的名字”string“是向你表明它是一个Hibernate的映射类型或者类型转换器)。和之前一样,set元素的table属性决定了用于集合的表名。key元素定义了在集合表中外键的字段名。element元素的column属性定义用于实际保存String值的字段名。
6.10.2.双向关联
首先把集合加入到相应的实体类中。然后再映射文件中修改配置;
<set name="participants" table="person_event" inverse="true">
<key column="person_id"/>
<many-to-many column="person_id" class="events.Person"/>
</set>
与单向关联的区别是射文件里增加了set元素的inverse="true"属性
所有的双向关联需要有一端被设置为inverse。在一对多关联中它必须是代表多(many)的那端。而在多对多(many-to-many)关联中,你可以任意选取一端,因为两端之间并没有差别。
</class>
6.7.Bag映射