第5章Hibernate的基本的用法5.5深入理解持久化对象

来源:互联网 发布:雅思网课推荐 知乎 编辑:程序博客网 时间:2024/04/27 21:36

5.5.1 持久化类的要求

虽然Hibernate对持久化类没有太多的要求,但是我们一个遵守如下规则:

*提供一个无参数的构造器

*提供一个标识属性:标识属性通常映射数据库表的主键字段

*尽量避免使用基本数据类型

*为持久化类的每个属性提供setter和getter方法

*使用非final类

*重写equals()和hashCode()方法:如果需要把持久化类的实例放入Set中,则应该为该持久化类重写equals()和hashCode()方法。

5.5.2 持久化对象的状态

瞬态:对象由new操作符创建,且尚未与Hibernate Session 关联的对象被认为处于瞬态。

持久化:持久化实例在数据库中有对应的记录,并拥有一个持久化标识。

脱管:某个实例曾经处于持久化状态,但随着与之关联的Session被关闭,该对象就变成托管状态。

5.5.3 改变持久化对象状态的方法

5.6 深入Hibernate的映射文件

5.6.1 映射文件结构

<hibernate-mapping>元素下可以拥有多个<class.../>子元素,每个<class.../>子元素对应一个持久化类的映射。

<span style="font-size:18px;"><hibernate-mappping>      <class/>      <class/>      ......</hibernate-mapping></span>
<hibernate-mapping.../>元素可以指定如下几个可选属性:

属性备注schema指定所映射的数据库的Schema名catalog指定所映射的数据库的catalog名default-cascade设置Hibernate默认的级联风格,该属性默认的值为nonedefault-access指定Hibernate默认的属性访问策略,默认值为property,即使用getter/setter方法来访问属性。default-lazy设置Hibernate默认的延迟加载策略,该属性值默认为true,即启用延迟加载策略。auto-import设置是否允许在查询语言中使用非全限定的类名。该属性默认是truepackage该属性指定一个包前缀,对于映射文件中没有指定全限定的类名,则默认使用该包前缀
<class.../>元素对应一个持久化类。

除name属性之外,<class.../>元素也可指定schema、catalog、lazy来覆盖<hibernate-mapping.../>元素指定的默认行为。除此之外,还可指定如下可选的属性:

属性备注table指定该持久化类映射的表名discriminator-value指定区分不同子类的值,当使用<subclass…/>元素来定义持久化类的继承关系映射时需要使用该属性mutable用于指定持久化类的实例是可变对象还是不可变对象proxy指定一个接口,在延迟装载时作为代理使用,也可以在这里指定该类自己的名字。dynamic-update指定用于更新记录的update语句是否在运行时动态生成,并且只更新那些改变过的字段。dynamic-insert指定用于插入记录的insert语句是否在运行时动态生成,并且只插入那些非空字段select-bofore-update指定Hibernate在更新某个持久化对象之前是否需要先进行一个查询polymorphism当采用<union-subclass…/>元素来配置继承映射时,该元素指定是否需要采用隐式多态查询。该属性的默认值为implicitwhere指定一个附加的SQL语句中过滤条件persister指定一个定制的ClassPersisterbatch-size指定根据标识符(identifier)来抓取实例时每批抓取的实例数。该属性默认是1optimistic-lock该属性指定乐观锁定策略。该属性的默认值是versioncheck指定一个SQL表达式,用于为该持久化类所对应的表指定一个多行的Check约束subselect该属性用于映射不可变的、只读实体如果需要采用继承映射,则class元素下还会增加<subclass.../>元素、<joined-subclass.../>或<union-subclass.../> 元素,这些元素分别用于定义子类。

5.6.2 映射主键

Hibernate建议为持久化类定义一个标识属性,用于唯一标识某个持久化实例,而标识属性则需要映射到底层数据表的主键。

标识属性通过<id.../>元素来指定。<id.../> 元素的name属性的值就是持久化类标识属性名。

<id.../>元素还可指定如下几个可选属性:

属性备注type指定该标识属性的数据类型column设置标识属性所映射的数据列的列名access指定Hibernate访问该标识属性的访问策略主键生成器负责生成数据表记录的主键。通常有如下常见的主键生成器:

主键生成器备注increment为long、short或者int类型主键生成唯一标识。只有在没有其他进程往同一张表中插入数据时才能使用。在集群下不要使用identity在DB2、MySQL、Microsoft SQL Server等提供identity(自增长)主键支持的数据表中适用。返回的标识属性是long、short或int类型的。sequence在DB2、Oracle等提供sequence支持的数据表中适用。返回的标识属性值是long、short或int类型的hilo使用一个高、低位算法高效的生成long\short或int类型的标识符seqhilo使用一个高/低位算法来高效地生成long、short或int类型的标识符,需要给定一个数据库 sequence名uuid用一个128位的UUID算法生成字符串类型的标识符,这在一个网络中是唯一的。(UUID算法会根据ip地址,JVM的启动时间,系统时间和一个计数器值来生成一个32位的字符串,因为通常UUID生成的字符串在一个网络中是唯一的)guid在Microsoft SQL Server 和MySQL中使用数据库生成的GUID字符串native根据底层数据库的能力选择identity\sequence或者hilo中的一个assigned让应用程序在save()之前为对象分配一个标识符。这相当于不指定<generator…/>元素时所采用的默认策略select通过数据库触发器选择某个唯一主键的行,并返回其主键值作为标识属性值foreign表明直接使用另一个关联的对象的标识属性值Hibernate映射文件的示例片段:

<span style="font-size:18px;"><class name="News" table="news_table"><!--定义标识属性-->         <id name="id" column="news_id">                    <!--定义主键生成器-->                     <generator class = "identity"/>          </id>          <!--用于定义普通属性-->           <property.../>           <property.../></class></span>

5.6.3 映射普通属性

Hibernate使用<property.../>元素来映射普通属性,配置<property .../>元素时只需要指定一个name属性,name属性映射持久类的属性名。如果想指定属性在数据表里存储的列名,还可以定义column属性来强制指定列名,列名默认与属性名相同。

除此之外,<property.../>元素还支持如下几个可选属性:

属性备注type指定该普通属性的数据类型update、insert用于设置Hibernate生成的update或insert语句中是否需要包含该字段,这两个属性的默认值都是trueformula该属性指定一个SQL表达式,指定该属性的值将根据表达式来计算,计算属性没有和它对应的数据列access定义Hibernate访问该属性的访问策略,默认是propertylazy指定该实例属性第一个被访问时,是否启动延迟加载。unique设置是否为该属性所映射的数据列添加唯一约束。not-nulll设置是否为该属性所映射的数据列添加not null约束optimistic-look设置该属性在进行更新时是否需要使用乐观锁定,该属性值默认是truegenerated设置该属性值映射的数据列的值是否由数据库生成index指定一个字符串的索引名称unique_key 指定一个唯一键的名称length指定该属性所映射数据列的字段长度precision指定该属性所映射数据列的有效数字位的位数,对数值型的数据有效scale指定该属性所映射数据列的小数位数

5.6.4 映射集合属性

映射集合属性时通常需要指定一个name属性,用于表明该集合属性的名称。除此之外,集合元素有如下可选属性。

属性备注table指定保存集合属性的表名schema指定保存集合属性的数据表的Schema的名称lazy设置是否启动延迟加载,该属性默认是trueinverse指定该集合关联的实体在双向关联关系中不控制关联关系cascade指定对持久化对象的持久化操作是否会级联到它所关联的子实体order-by该属性用于设置数据库对集合元素排序sort指定集合的排序顺序where指定任意的SQL语句中where条件batch-size定义延迟加载时每批抓取集合元素的数量access指定Hibernate访问集合属性的访问策略mutable指定集合中的元素是否可变

因为集合属性都需要保存到另一个数据表中,所以保存集合属性的数据表必须包含一个外键列,用于参照到主键列。该外键列通过在<set.../>、<list.../>等集合元素中使用<key.../>子元素来映射。

指定<key.../>元素时可以指定如下可选选项:

属性备注column指定外键字段的列名on-delete指定外键约束是否打开数据库级别的级联删除property-ref指定外键引用的字段是否为原表的主键not-null指定外键列是否具有非空约束update指定外键列是否可更新unique指定外键列是否具有唯一约束

在所有的集合映射中,除了<set.../>和<bag.../>元素外,都需要为集合元素的数据表指定一个索引列——用于保存数组索引,或者List的索引,或者Map集合的key索引。

用于映射索引列的元素有如下几个:

元素备注<list-index…/>用于映射List集合、数组的索引列<map-key>用于映射Map集合、基本数据类型的索引列<map-key-many-to-many>用于映射Map集合、实体引用类型的索引列<composite-map-key>用于映射Map集合、复合数据类型的索引列综合所有情形,用于映射集合元素的大致包括如下几种元素:

元素备注<element…/>当集合元素的基本类型及其包装类、字符串类型和日期类型时使用该元素<composite-element…/>当集合元素是复合类型时使用该元素<one-to-many…/>和<many-to-many…/>当集合元素是其他持久化对象的引用时使用它们。也就是说,这两个元素主要用于进行关联关系映射。

5.6.4.1 List集合属性

例子:

1)Person.java

public class Person{//标识属性private Integer id;//普通属性nameprivate String name;//普通属性ageprivate int age;//集合属性,保留该对象关联的学校private List<String> schools = new ArrayList<String>();//...//schools属性的setter和getter方法public void setSchools(List<String> schools){this.schools = schools;}public List<String> getSchools(){return this.schools;}}
2)Person.hbm.xml

<?xml version="1.0" encoding="GBK"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="org.crazyit.app.domain"><class name="Person" table="person_inf"><!-- 映射标识属性 --><id name="id" column="person_id"><!-- 指定主键生成器策略 --><generator class="identity"/></id><!-- 映射普通属性 --><property name="name" type="string"/><property name="age" type="int"/><!-- 映射List集合属性 --><list name="schools" table="school"><!-- 映射集合属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射集合属性数据表的集合索引列 --><list-index column="list_order"/><!-- 映射保存集合元素的数据列 --><element type="string" column="school_name"/></list></class></hibernate-mapping>
3)PersonManager.java

//创建并保存Person对象private void createAndStorePerson(){//打开线程安全的session对象Session session = HibernateUtil.currentSession();//打开事务Transaction tx = session.beginTransaction();//创建Person对象Person yeeku = new Person();//为Person对象设置属性yeeku.setAge(29);yeeku.setName("crazyit.org");//创建List集合List<String> schools = new ArrayList<String>();schools.add("小学");schools.add("中学");//设置List集合属性yeeku.setSchools(schools);session.save(yeeku);tx.commit();HibernateUtil.closeSession();}

5.6.4.2 数组属性

数组的集合属性,应该使用<array.../>元素完成映射,而<array.../>元素的子元素、属性等与<list.../>元素的用法完全一样。

例子:

<!-- 映射数组属性 --><array name="schools" table="school"><!-- 映射数组属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射数组属性数据表的数组索引列 --><list-index column="list_order"/><!-- 映射保存数组元素的数据列 --><element type="string" column="school_name"/></array>

5.6.4.3 Set集合属性

Set集合属性的映射与List有点不同,但因为Set是无序、不可重复的集合,因此<set.../>元素无须使用<list-index.../>子元素来映射集合元素的索引列。

例子:

<!-- 映射Set集合属性 --><set name="schools" table="school"><!-- 映射集合属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射保存集合元素的数据列,增加非空约束 --><element type="string" column="school_name"not-null="true"/></set>

5.6.4.4 bag元素映射

<bag../>元素即可以映射List集合属性,也可以映射Set集合属性,甚至可以映射Collection集合属性。不管是哪种集合属性,使用<bag.../>元素都将映射成无序集合。集合属性对应的表没有主键。

<bag.../>元素只需要<key.../>元素来映射关联的外键列,而使用<element.../>元素来映射集合属性的元素列。

<!-- 使用bag元素映射集合属性 --><bag name="schools" table="school"><!-- 映射集合属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射保存集合元素的数据列 --><element type="string" column="school_name"not-null="true"/></bag>

5.6.4.5 Map集合属性

Map集合属性需要使用<map.../>元素进行映射,当配置<map.../>元素时也需要使用<key.../>子元素映射外键列。除此之外,Map集合属性还需要映射Map key.Hibernate将以外键列和key列作为联合主键。

例子:

1)Person.java

public class Person{//标识属性private Integer id;//普通属性nameprivate String name;//普通属性ageprivate int age;//集合属性,保留该对象关联的考试成绩private Map<String ,Float> scores= new HashMap<String ,Float>();//... //scores属性的setter和getter方法public void setScores(Map<String ,Float> scores){this.scores = scores;}public Map<String ,Float> getScores(){return this.scores;}}
2)Person.hbm.xml

<?xml version="1.0" encoding="GBK"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="org.crazyit.app.domain"><class name="Person" table="person_inf"><!-- 映射标识属性 --><id name="id" column="person_id"><!-- 指定主键生成器策略 --><generator class="identity"/></id><!-- 映射普通属性 --><property name="name" type="string"/><property name="age" type="int"/><!-- 映射Map集合属性 --><map name="scores" table="score"><!-- 映射集合属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射集合属性数据表的Map key列 --><map-key column="subject" type="string"/><!-- 映射保存集合元素的数据列 --><element column="grade" type="float"/></map></class></hibernate-mapping>

5.6.5 集合属性的性能分析

对于集合属性,通常推荐使用延迟加载策略。所谓延迟加载策略就是等系统需要使用集合属性时才从数据库装载关联的数据。

Hibernate对集合属性默认采用延迟加载,在某些特殊的情况下,为<set.../>、<list.../>、<map.../>等元素设置lazy="false"属性来取消延迟加载。

5.6.6 有序集合映射

Hibernate还支持使用SortedSet和SortedMap两个有序集合,当我们需要映射这种有序集合时,应该为<map.../>元素或<set.../>元素指定sort属性,该属性值可以是如下三个值:

*unsorted:映射有序集合时不排序

*natural:映射有序集合时使用自然排序

*java.util.Comparator实现类的类名:使用排序器对有序集合元素进行定制排序

例子:

1)Person.java

public class Person{//标识属性private Integer id;//普通属性nameprivate String name;//普通属性ageprivate int age;//有序集合属性,保留该对象参与的培训private SortedSet<String> trainings= new TreeSet<String>();//...//trainings属性的setter和getter方法public void setTrainings(SortedSet<String> trainings){this.trainings = trainings;}public SortedSet<String> getTrainings(){return this.trainings;}}
2)Person.hbm.xml

<?xml version="1.0" encoding="GBK"?><!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN""http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"><hibernate-mapping package="org.crazyit.app.domain"><class name="Person" table="person_inf"><!-- 映射标识属性 --><id name="id" column="person_id"><!-- 指定主键生成器策略 --><generator class="identity"/></id><!-- 映射普通属性 --><property name="name" type="string"/><property name="age" type="int"/><!-- 映射SortedSet集合属性,使用自然排序增加sort="natural"定义--><set name="trainings" table="training"sort="natural"><!-- 映射集合属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射保存集合元素的数据列,增加非空约束 --><element type="string" column="training_name"not-null="true"/></set></class></hibernate-mapping>


如果希望数据库查询自己对集合元素排序,则可以使用<set.../>、<bag.../>或者 <map.../>元素的order-by属性,它会在SQL查询中完成排序,而不是在内存中完成排序。

<!-- 映射Set集合属性,指定在内存中排序 --><set name="trainings" table="training"order-by="training desc"><!-- 映射集合属性数据表的外键列 --><key column="person_id" not-null="true"/><!-- 映射保存集合元素的数据列,增加非空约束 --><element type="string" column="training"not-null="true"/></set>

5.6.7 映射数据库对象

在映射文件中创建和删除触发器、存储过程等数据库对象,Hibernate提供了<database-object.../>元素来满足这种需求。

使用<database-object.../>元素只有如下两种形式。

1.在映射文件中显示声明create和drop命令:

<span style="font-size:18px;"><hibernate-mapping>       ...       <database-object>              <create>create trigger f_full_content_gen ...</create>              <drop>create trigger t_full_content_gen</drop>       </database-object></hibernate-mapping></span>
2.提供一个类,这个类知道如何组织create和drop命令。这个特别类必须实现org.hibernate.mapping.AuxiliaryDatabaseObject接口。

<span style="font-size:18px;"><hibernate-mapping>      ...      <database-object>             <definition class="MyTriggerDefinition"/>      </database-object></hibernate-mapping></span>

如果我们想指定某些数据库对象仅在特定的方言中才可使用,还可在<databae-object.../>元素里使用<dialect-scope.../>子元素来进行配置。

如果仅仅为了根据映射文件来生成数据库对象,则可以使用Hibernate提供的SchemaExport工具,该工具可根据映射文件来生成数据库对象。

0 0