hibernate笔记

Java是面向对象语言,对象模型,其主要概念有:继承、关联、多态等;关系型
数据库中的概念有:表、主键、外键等。当使用Jdbc去操作关系型数据库时,因
为他们对应不起来,所以要做转换(转换的过程就叫做ORM, Object Relation
Mapping, 对象关系映射)。除了手工转换外,还可以使用ORM框架来解决,当前
主流ORM框架有Hibernate和TopLink等,我们学习的是Hibernate(3.2.6)。
1,以对象模式来访问数据库内容。
2,支持多种数所库。

* HelloWorld
有实体User,他有两个属性id:int和name:String,用Hibernate实现对User实体
的增删改查。

1,准备环境:
   a) 导入jar包,最基本的包为:hibernate3.jar,antlr-2.7.6.jar,
      asm.jar,asm-attrs.jar,cglib-2.1.3.jar,dom4j-1.6.1.jar,
      commons-collections-2.1.1.jar,commons-logging-1.0.4.jar,jta.jar,
      所用的数据库的JDBC驱动,可以选择加入log4j-1.2.11.jar。
   b) 拷贝配置文件hibernate.cfg.xml到classpath的根中,并修改其中的数据
      库连接信息;选择拷贝log4j.properties文件。可以到doc/tutorial/src中去
      拷贝。
2,写出实体User,有属性id:int和name:String,并写出映射文件User.hbm.xml。
   映射文件的模板可以到eg/目录下找或到文档中拷贝。
3,写两个方法分别用于保存一个用户到数据库和从数据库中取出已有的一个用户。


1,写UserDao,完成对User的save,delete,update,get(id),find(first,max)。
2,使用Junit测试UserDao。
3,练习UserDao(增删改查)。 提示:
   a)可以拷贝 lib/ 中所有的jar包到工程中去。
   b)所用的 hibernate 配置文件去 doc/tutorial/src 中拷贝;
   c)所用的映射文件去 doc/tutorial/src/events 中拷贝。


1,通过调用Configuration.configure(String resource)读取hibernate配置文
   件,并指定配置文件的路径(配置文件要在classpath中);也可以使用无参
   的configure()方法,表示使用默认的路径 "/hibernate.cfg.xml"。
2,可以让hibernate自动生成数据库表,需要配置以下属性:
      <property name="hibernate.hbm2ddl.auto">update</property>
   可取的值有:create-drop,create,update,validate。
   会在Configuration.buildSessionFactory()被调用时更新表结构。
3,也可以使用Hibernate提供的工具(SchemaExport)生成数据库表:
      Configuration cfg = new Configuration().configure();
      SchemaExport schemaExport = new SchemaExport(cfg);
      schemaExport.create(true, true);

* 映射文件
property元素是映射普通属性的。name;column,以及一些说明列的性质的属性,
也可以用子元素column来说明。

property元素的type可用的值,并给User增加如下属性:
1,用户的生日birthday:Date:date类型。
2,用户的头像avatar:byte[]:binary类型。
3,用户的说明resume:String:text类型。

如果没有指定属性对应的列表,并且属性名刚好和数据库中的关键字冲突,在执
行时就会抛一个sql语法错误的异常。有两个解决方法:1,用column指定一个与
关键字不冲突的列名;2,用column指定的列名加上反引号(`),Hibernate会根据
使用的SQLDialect来生成正确的引号风格)。

<!-- 显示sql语句 -->
<property name="show_sql">true</property>


Hibernate映射文件就是用于说明Java对象与数据库表中的记录的对应关系的。不
同性质的属性(例如主键和普通属性)用不同的标签来映射,如果Java对象中的
某个属性不需要存储到数据库中,那么就不需要在映射文件中配置这个属性。

** Hibernate对实体(POJO)的要求:
    1,有默认的构造方法;
    2,推荐提供一个标识属性(与业务无关的id);
    3,推荐此类不是final的(对懒加载有影响);
    4,推荐重写hashCode与equals方法。

hibernate-mapping元素的属性:package:指定一个包前缀,如果在映射文档中
没有指定全限定的类名,就使用这个作为包名;auto-import:指定是否可以在查
询语言中使用非全限定名(仅限于本映射文件中的类),默认为true。

在hibernate-mapping元素中可以嵌套多个class元素来映射多个实体类。但是最
好是一个实体类(或一个类的继承层次)对应一个映射文件,并以实体类的名称
(或超类名称)命名。

class元素的属性:name:实体类的全限定名(如果在hibernate-mapping指定了
包名的话,可以写非全限定名);table:对应的数据库表名,默认是类的非全限
定名。

id元素,与主键生成策略:increment,由Hibernate维护主键值自动增长,是在
插入数据前先select max(id)得到当前最大的id值,再进行计算新插入对象的id
的值。identity;hilo;native;uuid;


* Hibernate对象实例的三种状态与Session的方法:
  三种状态:自由(瞬时)、持久、游离;

瞬时(transient):数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器
    回收,一般是new出来且与session没有关联的对象。

持久(persistent):数据库中有数据与之对应,当前与session有关联,拥有持久
    化标识(相当于主键值),并且相关联的session没有关闭,事务没有提交;
    持久对象状态发生改变,最终会同步到数据库中。

游离(detached):数据库中有数据与之对应,但当前没有session与之关联;游离
    对象状态发生改变,hibernate不能检测到。

这里没有使用"脱管"这个概念,因为容易和"托管"混淆;前者代表"游离",而后
者的代表"持久"。

对照着图讲解session的主要方法与在各个状态之间的转换,并且试验各个方法会
生成什么样的sql语句,及在什么时候生成(马上生成或是在flush()或是
commit()时生成。应是说:sql语句都应是在flush()时产生的,tx.commit()会先
flush(),在主键策略为identity时,执行save()方法会马上生成一条sql语句并
且执行,这是因为hibernate要求持久状态一定要拥有持久化标识,而这个标识是
由数据库生成的(自动增长的主键值),所以只有执行了插入才能知道主键值是
多少)。

试验saveOrUpdate()时讲解id元素的unsaved-value的作用:指定当主键值为什么
时表示这个对象是自由状态的。如果果自由状态,就会生成insert语句,否则生
成update语句。

对一个对象在同一个session中多次使用save(),update()等等没有意义,跟调用
一次的结果是一样的。如调用两次save()只生成一条insert语句。


* 集合映射
如果实体类有一个集合类型的属性,就需要一张额外的表,即所谓的集合表,这
个表有一个外键引用实体类对应的表的主键。

根据集合中存放的元素类型可以分为两种:1,值类型集合,元素是可识别的数据
库类型,如基本数据类型或字符串类型等;2,实体类型集合,元素是其他的实体。

** 对集合做映射时一般需要指定:
1)集合表的表名,在相应元素中使用属性table指定;
2)集合外键,是在子元素key中用属性column属性指定一个列名,他是一个外键
   引用了实体表的主键;
3)集合元素,对于值类型集合,使用元素element指定;对于实体类型集合,使
   用元素one-to-many或many-to-many。
4)如果是索引集合,如List,数组或Map(bag不是),还需要指定一个集合表中
   的索引字段,用于对应到数组索引或List的索引,或者Map的key。
   a) 如果是List或数组,需要指定(集合表中的)一个用于保存索引的列,用
      元素list-index指定。
   b) 如果是Map:key用map-key元素指定;如果key是一个实体引用,则是通过
      map-key-many-to-many来配置。

JAVA的实体类中集合只能定义成接口不能定义成具体类,因为在运行时集合会被
替换成Hibernate的实现。

在实体类中声明集合时,可进行初始化。比如Set addresses = new HashSet();
这样做的目的是方便往集合中添加元素,调用getAddresses().add(element)就可
以了,不需再做是否为null的判断。  注意,我们只在把addresses初始化为一个
HashSet的实例,在运行时,集合会被Hibernate替换为自已的实现。

用于映射集合类的元素是由集合接口的类型决定的:
  <set>  元素用来映射 java.util.Set  类型的属性;
  <list> 元素用来映射 java.util.List 类型的属性;
  <array>元素用来映射 对象类型的数组;
  <primitive-array>元素用来映射 原始数据类型的数组;
  <bag>  元素用来映射 java.util.List 类型的属性(允许重复但没有顺序);
  <map>  元素用来映射 java.util.Map  类型的属性;

1,Set,一个用户(User)有多个地址,地址是字符串类型的。
    <set name="addresses" table="itcast_useraddresses">
     <key column="userId"></key>
     <element type="string" column="address"></element>
    </set>

2,List,与Set相比,需要在表中增加一列用于保存索引(元素在List中保存的
   顺序,不对应实体的任何属性)。用<list-index>元素指定:
    <list name="phoneNumbers" table="itcast_user_phonenumbers">
     <key column="userId"></key>
     <list-index column="idx"></list-index>
     <element type="string" column="phoneNumber"></element>
    </list>

3,Map,需要映射key的value:
    <map name="phoneNumbers3" table="itcast_user_phonenumbers3">
     <key column="userId"></key>
     <map-key type="string" column="idx"></map-key>
     <element type="string" column="phoneNumber"></element>
    </map>

* 一对多与多对一映射:User与Group
在User.hbm.xml中增加:
    <many-to-one name="group" column="groupId"></many-to-one>
在Group.hbm.xml中增加:
    <set name="users">
        <key column="groupId"></key>
        <one-to-many class="User"/>
    </set> 

这是做的双向关联,即从任何一方都可以得到另一方(User.getGroup得到所属的
组或Group.getUsers得到组中所有的用户);双方也都可以维护关系,维护关系
是指更新外键这一列的的值; 如果只有一方进行映射,就是单向关联。 如果去
掉了User.hbm.xml中的many-to-one映射,只有Group.hbm.xml中有一个set的映射,
这就是单向一对多关联,这时只能由Group维护关系,并且只能从Group得到User。
同样,如果只去掉Group.hbm.xml中的set映射,就是单向多对一关联。

树形结构,就是自己和自己是一个 一对多/多对一 的关系。如Group有父组和子组,
映射文件中为:
    <many-to-one name="parent" column="parentId"/>
    <set name="children">
        <key column="parentId"/>
        <!-- 集合中的元素还是Group -->
        <one-to-many class="Group"/>
    </set>

 

 

 

* 集合映射
如果实体类有一个集合类型的属性,就需要一张额外的表,即所谓的集合表,这
个表有一个外键引用实体类对应的表的主键。

如果是有序集合(List),顺序是指元素在集合中的顺序,即索引,所以还需要
一个列来存放这个索引。

根据集合中存放的元素类型可以分为两种:1,值类型集合,元素是可识别的数据
库类型,如基本数据类型或字符串类型等;2,实体类型集合,元素是其他的实体。

** 对集合做映射时一般需要指定:
1)集合表的表名,在相应元素中使用属性table指定;
2)集合外键,是在子元素key中用属性column属性指定一个列名,他是一个外键
   引用了实体表的主键;
3)集合元素,对于值类型集合,使用元素element指定;对于实体类型集合,使
   用元素one-to-many或many-to-many。
4)如果是索引集合,如List,数组或Map(bag不是),还需要指定一个集合表中
   的索引字段,用于对应到数组索引或List的索引,或者Map的key。
   a) 如果是List或数组,需要指定(集合表中的)一个用于保存索引的列,用
      元素list-index指定。
   b) 如果是Map:key用map-key元素指定;如果key是一个实体引用,则是通过
      map-key-many-to-many来配置。

*** 注意
JAVA的实体类中集合只能定义成接口不能定义成具体类,因为在运行时集合会被
替换成Hibernate的实现。

在实体类中声明集合时,可进行初始化。比如Set addresses = new HashSet();
这样做的目的是方便往集合中添加元素,调用getAddresses().add(element)就可
以了,不需再做是否为null的判断。  注意:我们只在把addresses初始化为一个
HashSet的实例,在运行时,集合会被Hibernate替换为自已的实现。

用于映射集合类的元素是由集合接口的类型决定的:
  <set>  元素用来映射 java.util.Set  类型的属性;
  <list> 元素用来映射 java.util.List 类型的属性;
  <array>元素用来映射 对象类型的数组;
  <primitive-array>元素用来映射 原始数据类型的数组;
  <bag>  元素用来映射 java.util.List 类型的属性(允许重复但没有顺序);
  <map>  元素用来映射 java.util.Map  类型的属性;

1,Set,一个用户(User)有多个地址,地址是字符串类型的。
    <set name="addresses" table="itcast_useraddresses">
     <key column="userId"></key>
     <element type="string" column="address"></element>
    </set>

2,List,与Set相比,需要在表中增加一列用于保存索引(元素在List中保存的
   顺序,不对应实体的任何属性)。用<list-index>元素指定:
    <list name="phoneNumbers" table="itcast_user_phonenumbers">
     <key column="userId"></key>
     <list-index column="idx"></list-index>
     <element type="string" column="phoneNumber"></element>
    </list>

3,Map,需要映射key的value:
    <map name="phoneNumbers3" table="itcast_user_phonenumbers3">
     <key column="userId"></key>
     <map-key type="string" column="idx"></map-key>
     <element type="string" column="phoneNumber"></element>
    </map>

* 一对多与多对一映射:User与Group
在User.hbm.xml中增加:
    <many-to-one name="group" column="groupId"></many-to-one>
在Group.hbm.xml中增加:
    <set name="users">
        <key column="groupId"></key>
        <one-to-many class="User"/>
    </set> 

这是做的双向关联,即从任何一方都可以得到另一方(通过User.getGroup得到所
属的组或通过Group.getUsers得到组中所有的用户);双方也都可以维护关系,
维护关系是指更新外键这一列的的值。

如果只有一方进行映射,就是单向关联。 例如只有Group.hbm.xml中有一个set的
映射,这就是单向一对多关联,这时只能由Group维护关系,并且只能从Group 得
到 User。同样,如果只去掉Group.hbm.xml中的set映射,就是单向多对一关联。

** 自关联的组(无限级)
树形结构,就是自己和自己是一个 一对多/多对一 的关系。如Group有父组和子组,
映射文件中为:
    <many-to-one name="parent" column="parentId"/>
    <set name="children">
        <key column="parentId"/>
        <!-- 集合中的元素还是Group -->
        <one-to-many class="Group"/>
    </set>


* 排序集合
例如Group中的users希望以user的score降序排列。可以在取出Group后,把其中
的所有用户放到一个TreeSet中,然后用这个TreeSet替换Group中原先的用户集合,
这是由自已完成的在内存中进行排序。

如果用Hibernate,有两种方式实现:
  a) 使用sort属性实现在内存中排序;
  b) 使用order-by属性实现: 在使用sql进行查询时使用order by子句指定顺序。

有序集合对于List或数组无效,因为列表元素的顺序由列表索引指明。

** sort属性
可以用于set或map映射,默认为unsorted,即不排序;可以设为natural,要求实
体要实现java.lang.Comparable接口。分类集合的行为象TreeSet或者TreeMap,
这是从数据库中取出记录后再在内存中进行排序。(在查询时有效)

** order-by属性
在set、bag或map映射中使用order-by属性,指定查询时生成的sql的order by子
句,这是在执行sql查询时指定排序(推荐)。如果使用了order-by,则返回的就
是可以保存顺序的集合实现类。

*** 注意
如果在映射集合Set或Map时指定了 sort 属性以实现排序,并且在实体中对这个
集合属性进行了初始化,则初始值应是 SortedSet 或 SortedMap 实现类的实例
(TreeSet或TreeMap)。


* 一对一映射:User与IdCard
有两种映射方式:基于主键和一对一和基于外键和一对一(先分析对象中应怎么
表示(各有一个属性),再用分析数据库表应怎么设计)。不管哪种方式,都是有
外键的那个表对应的实体(IdCard)来维护关系;因为只有IdCard能维护关系,所
以如果要做单向关联,只能做从IdCard到User的单向关联。

不管是多对一还是一对一,有外键的那个表所对应的实体始终能够维护关系。

** 基于主键的一对一
在IdCard.hbm.xml中把主键生成策略改为foreign,并设置one-to-one:
    ...
    <generator class="foreign">
        <param name="property">user</param>
    </generator>
 ...
    one-to-one name="user" constrained="true"></one-to-one>
 
    属性constrained="true"表示该类对应的表的的主键同时作为外键引用User
    表的主键,默认为false。

在User.hbm.xml中增加:
    <one-to-one name="idCard"></one-to-one>

** 基于外键的一对一
在IdCard.hbm.xml中增加:
    <many-to-one name="user" column="userId" unique="true"/>
   
    其中属性unique="true"说明这一列的值是唯一的,不能重复。

在User.hbm.xml中增加:
    <one-to-one name="idCard" property-ref="user"></one-to-one>

    !!(文档中的,不好理解)其中property-ref属性用来指定关联类的一个
    属性,这个属性将会和外键相对应,如果没有指定,会使用对方关联类的主
    键(所以上面的基于主键的一对一时不用指定)。

 !!(这样好理解)one-to-one也说明有一个表跟他关联,就像set元素一样。
 set元素需要一个子元素key来指定集合外键,而one-to-one也要指定关联类
 对应表的外键,如果这儿是让指定这个外键应该就好理解了(就像映射集合
 时指定的集合外键一样),只不过这儿让指定的是这个列对应的对象中的属
 性而已,这个属性就是那个外键列对应的属性。

1,不管哪种方式,都是有外键的那个表对应的实体(IdCard)来维护关系;
2,因为只有IdCard能维护关系,所以如果要做单向关联,只能做从IdCard到
   User的单向关联。

* 多对多映射:Group与Privilege
在Group.hbm.xml中增加:
    <set name="privileges" table="itcast_groups_privileges">
        <key column="groupId"></key>
        <many-to-many class="Privilege" column="privilegeId"></many-to-many>
    </set>
在Privilege.hbm.xml中增加:
    <set name="groups" table="itcast_groups_privileges">
        <key column="privilegeId"></key>
        <many-to-many class="Group" column="groupId"></many-to-many>
    </set>

双方一定要在set(或其它相应的元素,如list)元素中指定相同的表名(这是指
定的中间表的名称)。元素many-to-many 的class 属性用于指定关联类(集合中
的实体元素)的名称;因为是多对多,关联类在中间表中也是用一个外键指定的,
many-to-many中的column 属性用于指定这个外键的列名。

这是做的双向关联,此时双方都可以维护关系。多对多中的维护关系是指操作中
间表(增加与删除)的记录。去掉任何一方的映射,就是单向的多对多关联。

如果想要清空Group中含有的Role,可以使用:
    group.getRoles().clear();
这样在提交事务时会删除相应的中间表记录。


*** 维护关联关系总结:
  a) 一对多/多对一:多的一方始终可以维护关系;一的一方可以设置是否维护
    关联关系。
  b) 一对一:只能由 有外键的表对应的实体 来维护关联关系。
  c) 多对多:双方都可以设置是否维护关联关系。


* inverse
此属性表示 "是否放弃维护关联关系",在one-to-many和many-to-many中使用,
默认值是"false"。  维护关联关系是指在Java里两个对象关联时,对数据库表中
的数据产生影响。在一对多/多对一中,是指设置外键列的值;在多对多中是指在
中间表是增减记录。

设置inverse=true表示放弃维护关联关系,即由对方来维护。在使用时要注意:
  a) 在索引(有序)集合中,不要设置inverse=true,否则不能生成索引值。
  b) 在多对多映射中,不要双方都设置为inverse=true,否则都不维护关系。


* cascade
Casade用来说明当对主对象进行某种操作时是否对其关联的从对象也作类似的操
作,常用的cascade值有:
  none(默认值,无级连操作);
  all(所有级连操作);
  save-update(对主对象调用save()或update()或saveOrUpdate()方法时对从对
      象调用saveOrUpdate());
  delete(delete()方法);
其他还有lock,refresh,evict,replicate,persist,merge,delete-orphan等。

同时使用多种级联风格时使用逗号分隔,如 cascade="save-update,delete"。

通常在many-to-one或many-to-many关系中应用级联没有意义。如删除一个用户不
应该删除他所属的组(组中的其他用户怎么办);或删除了一个组不应该把他对
应的权限实体也删除掉(其他组还要用呢)。 一般在one-to-one和one-to-many
中设置级联比较有用。


* 在Set中添加重复的未保存状态的实体
在一对多中,可以通过设置级联 cascade="save-update" 来实现在save()一方的
实体时级联的插入多方的数据,但这时如果集合如果使用的是Set,就会出现只插
入一个记录的现象,这是因为未保存的对象的主键值相等,hashCode()相同并且
equals()为true。

在Set中不能添加重复的元素,是否重复是通过hashCode和equals方法决定的。所
以可以通过重写hashCode与equals方法实现在Set中可以添加多个未保存的实体对
象(有相同的主键值): hashCode:IF id==null THEN return super.hashCode。

 

* 集合的lazy属性
延迟加载,默认值为true,即集合的属性值默认是不加载的。强制加载可以通过
在session环境中使用这个集合属性或者使用:Hibernate.initialize(proxy)。
当相关联的session关闭后,再访问懒加载的对象将出现异常。


 

 

 

* 映射文件中的几个重要的属性

** inverse
只在 实体类型集合 的配置中使用。

1,此属性表示 "是否放弃维护关联关系",在one-to-many和many-to-many中使用,
   默认值是"false"。
2,维护关联关系是指在Java里两个对象关联时,对数据库表中的数据产生影响。
3,在一对多/多对一中,是指设置外键列的值;在多对多中是指在中间表是增减
   记录。

设置inverse=true表示放弃维护关联关系,即由对方来维护。在使用时要注意:
  a) 在索引集合中,不要设置inverse=true,否则不能生成索引值。
  b) 在多对多映射中,不要双方都设置为inverse=true,否则都不维护关系。


** cascade
1,在对实体关联的配置中可以使用。
2,用来说明当对 "主对象" 进行某种操作时,是否对其关联的 "从对象" 也作类
   似的操作,例如:
   添加投票时,希望通过 cascade 级联插入关联的投票项。

常用的cascade值有:
  none:默认值,无级连操作;
  all :所有级连操作;
  save-update:
        对主对象调用save()或update()或saveOrUpdate()方法时对从对
        象调用saveOrUpdate();
  delete:delete()方法;

同时使用多种级联风格时使用逗号分隔,如 cascade="save-update,delete"。

1,通常在many-to-one或many-to-many关系中应用级联没有意义。如:
   a)删除一个用户不应该删除他所属的组(组中的其他用户怎么办);或
   b)删除了一个组不应该把他对应的权限实体也删除掉(其他组还要用呢)。
2,一般在one-to-one和one-to-many中设置级联比较有用。如:
   a)删除用户时也删除他的身份证;或
   b)删除一个投票时,也删除他所有的投票项。

** 在HashSet中添加多个的未保存状态的实体
在一对多中,可以通过设置级联为 "save-update" 来实现在save() "一" 方的实
体时级联的插入 "多" 方的数据。这时如果集合如果使用的是 HashXxx 集合,就
注意要重写 hashCode() 与 equals() 方法。 可以只比较id;也可以比较关键的
业务数据。


** lazy
1,在对实体关联的配置中可以使用,指定是否延迟加载。
2,实体类型集合的配置中默认值为true,即默认是不加载的。
3,强制加载可以通过在session环境中调用:Hibernate.initialize(proxy)。

当相关联的session关闭后,再访问懒加载的对象将出现异常。

** get 与 load 方法
get 方法:立即加载。
  1,会马上执行一条select语句。
load方法:延迟加载。
  1,会返回一个代理,不会执行select语句;
  2,会在第一次使用这个对象时,执行一条select语句,即从数据库中获取对象;
  3,调用了除getId()(获取主键的方法)之外的方法,就是使用了这个对象。

区别:
  1,get立即加载,load延迟加载;
  2,如果指定主键的记录不存在:
     a)get 方法返回null;
     b)load方法在加载时抛异常:org.hibernate.ObjectNotFoundException:
        No row with the given identifier exists.


* 继承映射
论坛中的文章有主题和回复两种:Topic和Reply,他们都是文章,都有content与
author属性,即继承自同一个父类。

可以使用两种方式映射:
  a) 每个继承结构一张表,需要一个字段用于区分子类的具体类型;
  b) 每个类一张表,这是就有三张表,各自都只包含自己的属性,子类对应的表
     的主键也做为外键引用超类对应的表的主键。

** 每个继承结构一张表 (subclass)
<class name="Article" table="itcast_article" discriminator-value="A">
...
<!-- 指定一个列,用于区分子类的具体类型(鉴别器) -->
<discriminator column="class" type="string"></discriminator>
...
<subclass name="Topic" discriminator-value="T">
    <property name="title"></property>
</subclass>
<subclass name="Reply" discriminator-value="R">
    <property name="floor"></property>
</subclass>

1,指定一个列,用于存放代码类型的标志。
   a)使用discriminator元素配置:
   b)他的type属性指定这个列的类型,如果不指定,默认为string;
   c)这个元素要写在id元素的后面,不要把顺序弄错了。
2,为此继承结构中的每一个实体类都指定一个唯一的标志:
   a)在 class 或 subclass 元素中通过 discriminator-value 属性配置;
   b)这个值要是唯一的;
   c)如果不指定,默认值是这个子类的全限定名。


** 每个类一张表 (joned-subclass)
...
<joined-subclass name="Topic" table="itcast_topic">
    <key column="id"></key>
    <property name="title"></property>
</joined-subclass>
...

其中key指定的是子类表的主键,同时也会做为外键引用父类表的主键。


** 获取实体
使用 session.get(Article.class, 1); 即可返回相应类型的对象实例。


* 查询
Hibernate提供的查询方式有:HQL或Criteria。

** HQL
1,HQL是面向对象的查询语言,他查的是对象而不是表;
2,HQL中的对象名和属性名是区分大小写的(他部分不区分大小写);
3,HQL主要通过Query来操作,Query的创建方式:session.createQuery(hql);

*** 1,from 子句
例:查询所有User实体:
    from cn.itcast.domain.User    
或  from User  (当auto-import="true"时)

1,from后指定的是要查询的实体的名字;
   a)实体的名字是映射文件中的class元素的entity-name属性指定的;
   b)如果没有指定entity-name,则默认是类的全限定名。
   c)这样可以让一个类映射多个表,指定不同的entity-name就可以了。

2,如果没有指定entity-name,在from后应写类的全限定名;
   a)当映射文件中的 hibernate-mapping 元素的属性 auto-import="true" 时,
      也可以使用非全限定名,其默认值为true。
   b)如果存在 多个 非全限定名相同 的类,都应设置 auto-import="false"。

3,可以为查询的实体指定一个别名,使用as关键字,但as关键字也可以省略。例:
   from User u

*** 2,select 子句
例:查询所有User实体:
    select u from User as u

1,以上是查询整个实体(所有属性,返回的是实体对象);
2,也可以只查询几个属性,指定要查询的属性即可,多个属性之间用逗号隔开,
   a)例:select u.id,u.name from User u
   b)这时返回的每一结果都是一个Object数组,数组中依次存放着id和name属
      性的值。

3,可以让查询部分属性的语句返回结果是一个个相应对象实例,只填充了查询中
   出现的属性,
   a)应使用如下语句:select new User(u.id,u.name) from User u ;
   b)这样要求User类有相应的构造方法:public User(int id, String name)。

小问题:
  1,执行的语句 "select new Group(g.id,g.name) from Group g",抛如下异常:
  2,java.lang.ClassCastException: org.hibernate.hql.ast.tree.SqlNode,
  3,是在 session.createQuery() 时报的错。
问题原因:
  Group为关键字. 应改为如下语句(使用全限定名):
  select new cn.itcast.entities.Group(g.id,g.name) from Group g

*** 3,where 子句
例,查询所有id值小于10的User:
    from User u where u.id < 10

1,其中的 u.id 是指的实体的属性;
2,如果属性名与关键字冲突的话,就需在前面加上实体的别名,如u.order或
   u.desc等。

*** 4, order by
例:from User u order by u.id desc

1,升序:asc;降序:desc ;
2,多个排序属性之间用逗号隔开。

*** 5.聚集函数
avg(),count(),max(),min(),sum()

例,查询所有用户的总数:
    select count(u) from User u

执行后,返回的结果可能是Integer类型,也可能是Long型,这是由jdbc驱动决定
的,所以使用时要注意。可以把返回值转为Number,然后根据需要取intValue或
longValue,代码为:((Number) result).intValue();

 

1,大多数sql的表达式在where子句中都允许使用;
2,Hibernate还提供有操作集合的函数,如:
   a)size(),集合的大小;
   b)maxelement()和minelement(),引用一个基本数据类型的集合中的最大与
      最小的元素;
   c)minindex()与maxindex(),引用有序集合的最小与最大的索引;
   d)elements(),返回集合的元素;
   e)他们都需要一个集合类型的参数。

例1:查询用户数量大于2的所有的组,hql如下:
     select g from Group g where size(g.users) < 2

例2:查询含有某个权限的组:
     from Group g where ? in elements(g.privileges)
     还需要设置一个参数,这个参数是Privilege的一个对象实例。

*** 使用参数
有两种方式:

1,使用?占位,例:from User where id > ?,指定参数值使用
Query.setParameter(int position, Object val),位置索引从0开始;

2, 或使用变量名占位,例:from User where id > :id,指定参数值是使用
Query.setParameter(String name, Object val);

两种方法不能同时使用。

*** Query的分页支持和查询的方法
Query.list()          是返回一个List集合;
Query.iterate()       返回一个Iterator,这是先查询出所有的id,再一条一条
       的查询记录;
Query.uniqueResult()  是返回唯一的结果或null(无匹配时),如果有多条记录
       匹配,则抛异常NonUniqueResultException。
分页是通过Query.setFirstResult() 与Query.setMaxResults() 实现。

*** 练习
1, 根据用户名和密码查询一个用户.
2, 查询有指定权限的组.
3, 树形的Group中, 方法 findGroups(Group parent) 查询指定Group的所有子组,
   如果parent=null, 则返回所有的顶级Group.


* Criteria 与 DetachedCriteria
Criteria是一种比HQL更面向对象的查询方式。

*** Criteria的创建方式:
    Criteria c = session.createCriteria(User.class);

*** 添加过滤条件:
    c.add(Restrictions.gt("id",3)); // id的值要大于3
    c.add(Restrictions.isNull("desc")); // desc不能为空
   
    这样添加的条件之间是 and 的关系。添加or关系的条件使用:
    c.add(Restrictions.or(Restrictions.eq("name", "xx"),
          Restrictions.eq("name", "yy")));
    这是查询名字为'xx'或'yy'的组。

*** 指定排序:
    c.addOrder(Order.desc("id")); // 按id的值降序排列

*** 分页:
    Criteria.setFirstResult(0);
    Criteria.setMaxResults(10);

*** 练习
1, 查找指定名字的用户(测试用户名是否已被使用).
2, 查找所有属于 名字为'xx'的组的 用户:
    Criteria c = session.createCriteria(User.class);
    c.createAlias("group", "g"); // 创建连接查询
    c.add(Restrictions.eq("g.name", "xx"));

(不能写成c.add(Restrictions.eq("group.name", "itcast"));这样会把
  group.name当成是一个属性)

** DetachedCriteria
DetachedCriteria可在session外创建(在其他层创建比如在Service中创建)然
后用getExecutableCriteria(session)方法创建Criteria对象来完成查询。

*** 练习
DetachedCriteria 可以在 QueryArgs.buildCriteria方法中构建查询.

 

* 缓存
缓存的作用主要用来提高性能,可以简单的理解成一个Map;

一级缓存,Session级共享。每个Session会在内部维护一个数据缓存,此缓存随
着Session的创建/销毁而创建/销毁。save, update, saveOrUpdate, load,
get, list, iterate等这些方法都会将对象放在一级缓存中,一级缓存不能控制
缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用evict,
clear方法清除缓存中的内容。

二级缓存:配置:二级缓存是SessionFactory级别的全局缓存,它底下可以使用
不同的缓存类库,hibernate内置了对EhCache, OSCache, TreeCache,
SwarmCache的支持,可以通过实现CacheProvider和Cache接口来加入Hibernate
不支持的缓存实现。使用时要在hibernate.cfg.xml中增加配置:
    <property name="cache.provider_class">
        org.hibernate.cache.HashtableCacheProvider
    </property>
其中内置的缓存实现在文档19.2. The Second Level Cache中有说明。

以上只是说明可以使用二级缓存,具体的使用是每个类的缓存单独存放,也需要
单独配置。  使用方式为:在hibernate.cfg.xml中增加:
    <class-cache class="className" usage="read-only"/>
或在映射文件的class元素加入子元素:
    <cache usage="read-write"/>

其中usage:read-only,read-write,nonstrict-read-write,transactional。如果
应用程序只需读取一个持久化类的实例,而无需对其修改,应可以对其进行只读
(read-only)缓存;如果应用程序需要更新数据,那么使用读/写缓存(read-write)
比较合适。

* 自定义值类型(enum)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值