Hibernate介绍与提高

 

 Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。

  Hibernate的核心接口一共有5个,分别为:Session、SessionFactory、Transaction、Query和Configuration。这5个核心接口在任何开发中都会用到。通过这些接口,不仅可以对持久化对象进行存取,还能够进行事务控制。下面对这五个核心接口分别加以介绍。

  ·Session接口:Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句。)。但需要注意的是Session对象是非线程安全的。同时,Hibernate的session不同于JSP应用中的HttpSession。这里当使用session这个术语时,其实指的是Hibernate中的session,而以后会将HttpSesion对象称为用户session。

  ·SessionFactory接口:SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。

  ·Configuration接口:Configuration接口负责配置并启动Hibernate,创建SessionFactory对象。在Hibernate的启动的过程中,Configuration类的实例首先定位映射文档位置、读取配置,然后创建SessionFactory对象。

  ·Transaction接口:Transaction接口负责事务相关的操作。它是可选的,开发人员也可以设计编写自己的底层事务处理代码。

  ·Query和Criteria接口:Query和Criteria接口负责执行各种数据库查询。它可以使用HQL语言或SQL语句两种表达方式。

Hibernate主键介绍

  Assigned

  Assigned方式由程序生成主键值,并且要在save()之前指定否则会抛出异常

  特点:主键的生成值完全由用户决定,与底层数据库无关。用户需要维护主键值,在调用session.save()之前要指定主键值。

  Hilo

  Hilo使用高低位算法生成主键,高低位算法使用一个高位值和一个低位值,然后把算法得到的两个值拼接起来作为数据库中的唯一主键。Hilo方式需要额外的数据库表和字段提供高位值来源。默认请况下使用的表是

  hibernate_unique_key,默认字段叫作next_hi。next_hi必须有一条记录否则会出现错误。

  特点:需要额外的数据库表的支持,能保证同一个数据库中主键的唯一性,但不能保证多个数据库之间主键的唯一性。Hilo主键生成方式由Hibernate 维护,所以Hilo方式与底层数据库无关,但不应该手动修改hi/lo算法使用的表的值,否则会引起主键重复的异常。

  Increment

  Increment方式对主键值采取自动增长的方式生成新的主键值,但要求底层数据库的支持Sequence。如Oracle,DB2等。需要在映射文件xxx.hbm.xml中加入Increment标志符的设置。

  特点:由Hibernate本身维护,适用于所有的数据库,不适合多进程并发更新数据库,适合单一进程访问数据库。不能用于群集环境。

  Identity

  Identity当时根据底层数据库,来支持自动增长,不同的数据库用不同的主键增长方式。

  特点:与底层数据库有关,要求数据库支持Identity,如MySQl中是auto_increment, SQL Server 中是Identity,支持的数据库有MySql、SQL Server、DB2、Sybase和HypersonicSQL。 Identity无需Hibernate和用户的干涉,使用较为方便,但不便于在不同的数据库之间移植程序。

  Sequence

  Sequence需要底层数据库支持Sequence方式,例如Oracle数据库等

  特点:需要底层数据库的支持序列,支持序列的数据库有DB2、PostgreSql、Qracle、SAPDb等在不同数据库之间移植程序,特别从支持序列的数据库移植到不支持序列的数据库需要修改配置文件

  Native

  Native主键生成方式会根据不同的底层数据库自动选择Identity、Sequence、Hilo主键生成方式

  特点:根据不同的底层数据库采用不同的主键生成方式。由于Hibernate会根据底层数据库采用不同的映射方式,因此便于程序移植,项目中如果用到多个数据库时,可以使用这种方式。

  UUID

  UUID使用128位UUID算法生成主键,能够保证网络环境下的主键唯一性,也就能够保证在不同数据库及不同服务器下主键的唯一性。

  特点;能够保证数据库中的主键唯一性,生成的主键占用比较多的存贮空间

  Foreign GUID

  Foreign用于一对一关系中。GUID主键生成方式使用了一种特殊算法,保证生成主键的唯一性,支持SQL Server和MySQL

Hibernate源码中几个包的作用简要介绍

  net.sf.hibernate.*  

  该包的类基本上都是接口类和异常类

  net.sf.hibernate.cache.*  

  JCS的实现类

  net.sf.hibernate.cfg.*  

  配置文件读取类

  net.sf.hibernate.collection.*  

  Hibernate集合接口实现类,例如List,Set,Bag等等,Hibernate之所以要自行编写集合接口实现类是为了支持lazy loading

  net.sf.hibernate.connection.*  

  几个数据库连接池的Provider

  net.sf.hibernate.dialect.*  

  支持多种数据库特性,每个Dialect实现类代表一种数据库,描述了该数据库支持的数据类型和其它特点,例如是否有AutoIncrement,是否有Sequence,是否有分页sql等等

  net.sf.hibernate.eg.*  

  Hibernate文档中用到的例子

  net.sf.hibernate.engine.*  

  这个包的类作用比较散

  net.sf.hibernate.expression.*  

  HQL支持的表达式

  net.sf.hibernate.hq.*  

  HQL实现

  net.sf.hibernate.id.*  

  ID生成器

  net.sf.hibernate.impl.*  

  最核心的包,一些重要接口的实现类,如果Session,SessionFactory,Query等

  net.sf.hibernate.jca.*  

  JCA支持,把Session包装为支持JCA的接口实现类

  net.sf.hibernate.jmx.*  

  我不懂JMX,只知道JMX是用来编写App Server的管理程序的,大概是JMX部分接口的实现,使得App Server可以通过JMX接口管理Hibernate

  net.sf.hibernate.loader.*  

  也是很核心的包,主要是生成sql语句的

  net.sf.hibernate.lob.*  

  Blob和Clob支持

  net.sf.hibernate.mapping.*  

  hbm文件的属性实现

  net.sf.hibernate.metadata.*  

  PO的Meta实现

  net.sf.hibernate.odmg.*  

  ODMG是一个ORM标准,这个包是ODMG标准的实现类

  net.sf.hibernate.persister.*  

  核心包,实现持久对象和表之间的映射

  net.sf.hibernate.proxy.*  

  Proxy和Lazy Loading支持

  net.sf.hibernate.ps.*  

  该包是PreparedStatment Cache

  net.sf.hibernate.sql.*  

  生成JDBC sql语句的包

  net.sf.hibernate.test.*  

  测试类,你可以用junit来测试Hibernate

  net.sf.hibernate.tool.hbm2ddl.*  

  用hbm配置文件生成DDL

  net.sf.hibernate.transaction.*  

  Hibernate Transaction实现类

  net.sf.hibernate.type.*  

  Hibernate中定义的持久对象的属性的数据类型

  net.sf.hibernate.util.*  

  一些工具类,作用比较散

  net.sf.hibernate.xml.*  

  XML数据绑定

缓存管理

  Hibernate 中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的,一般情况下无需进行干预;第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围或群集范围的缓存。这一级别的缓存可以进行配置和更改,并且可以动态加载和卸载。 Hibernate还为查询结果提供了一个查询缓存,它依赖于第二级缓存。

  1. 一级缓存和二级缓存的比较:第一级缓存 第二级缓存 存放数据的形式 相互关联的持久化对象 对象的散装数据 缓存的范围 事务范围,每个事务都有单独的第一级缓存进程范围或集群范围,缓存被同一个进程或集群范围内的所有事务共享 并发访问策略由于每个事务都拥有单独的第一级缓存,不会出现并发问题,无需提供并发访问策略由于多个事务会同时访问第二级缓存中相同数据,因此必须提供适当的并发访问策略,来保证特定的事务隔离级别 数据过期策略没有提供数据过期策略。处于一级缓存中的对象永远不会过期,除非应用程序显式清空缓存或者清除特定的对象必须提供数据过期策略,如基于内存的缓存中的对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处于缓存中的最长空闲时间 物理存储介质内存内存和硬盘。对象的散装数据首先存放在基于内在的缓存中,当内存中对象的数目达到数据过期策略中指定上限时,就会把其余的对象写入基于硬盘的缓存中。缓存的软件实现 在Hibernate的Session的实现中包含了缓存的实现由第三方提供,Hibernate仅提供了缓存适配器(CacheProvider)。用于把特定的缓存插件集成到Hibernate中。启用缓存的方式只要应用程序通过Session接口来执行保存、更新、删除、加载和查询数据库数据的操作,Hibernate就会启用第一级缓存,把数据库中的数据以对象的形式拷贝到缓存中,对于批量更新和批量删除操作,如果不希望启用第一级缓存,可以绕过Hibernate API,直接通过JDBC API来执行指操作。用户可以在单个类或类的单个集合的粒度上配置第二级缓存。如果类的实例被经常读但很少被修改,就可以考虑使用第二级缓存。只有为某个类或集合配置了第二级缓存,Hibernate在运行时才会把它的实例加入到第二级缓存中。 用户管理缓存的方式第一级缓存的物理介质为内存,由于内存容量有限,必须通过恰当的检索策略和检索方式来限制加载对象的数目。Session的evit()方法可以显式清空缓存中特定对象,但这种方法不值得推荐。 第二级缓存的物理介质可以是内存和硬盘,因此第二级缓存可以存放大量的数据,数据过期策略的maxElementsInMemory属性值可以控制内存中的对象数目。管理第二级缓存主要包括两个方面:选择需要使用第二级缓存的持久类,设置合适的并发访问策略:选择缓存适配器,设置合适的数据过期策略。

  2. 一级缓存的管理: 当应用程序调用Session的save()、update()、savaeOrUpdate()、get()或load(),以及调用查询接口的 list()、iterate()或filter()方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。 Session为应用程序提供了两个管理缓存的方法: evict(Object obj):从缓存中清除参数指定的持久化对象。 clear():清空缓存中所有持久化对象。

  3. 二级缓存的管理:

  3.1. Hibernate的二级缓存策略的一般过程如下:

  1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。

  2) 把获得的所有数据对象根据ID放入到第二级缓存中。

  3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。

  4) 删除、更新、增加数据的时候,同时更新缓存。

  Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query Cache。

  3.2. 什么样的数据适合存放到第二级缓存中? 1 很少被修改的数据 2 不是很重要的数据,允许出现偶尔并发的数据 3 不会被并发访问的数据 4 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。

  3.3. 不适合存放到第二级缓存的数据? 1 经常被修改的数据 2 财务数据,绝对不允许出现并发 3 与其他应用共享的数据。

  3.4. 常用的缓存插件 Hibernater 的二级缓存是一个插件,下面是几种常用的缓存插件:

  l EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。

  l OSCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持。

  l SwarmCache:可作为群集范围内的缓存,但不支持Hibernate的查询缓存。

  l JBossCache:可作为群集范围内的缓存,支持事务型并发访问策略,对Hibernate的查询缓存提供了支持。

  3.5. 配置二级缓存的主要步骤:

  1) 选择需要使用二级缓存的持久化类,设置它的命名缓存的并发访问策略。这是最值得认真考虑的步骤。

  2) 选择合适的缓存插件,然后编辑该插件的配置文件。

Hibernate与延迟加载

  Hibernate对象关系映射提供延迟的与非延迟的对象初始化。非延迟加载在读取一个对象的时候会将与这个对象所有相关的其他对象一起读取出来。这有时会导致成百的(如果不是成千的话)select语句在读取对象的时候执行。这个问题有时出现在使用双向关系的时候,经常会导致整个数据库都在初始化的阶段被读出来了。当然,你可以不厌其烦地检查每一个对象与其他对象的关系,并把那些最昂贵的删除,但是到最后,我们可能会因此失去了本想在ORM工具中获得的便利。

  一个明显的解决方法是使用Hibernate提供的延迟加载机制。这种初始化策略只在一个对象调用它的一对多或多对多关系时才将关系对象读取出来。这个过程对开发者来说是透明的,而且只进行了很少的数据库操作请求,因此会得到比较明显的性能提升。这项技术的一个缺陷是延迟加载技术要求一个Hibernate会话要在对象使用的时候一直开着。这会成为通过使用DAO模式将持久层抽象出来时的一个主要问题。为了将持久化机制完全地抽象出来,所有的数据库逻辑,包括打开或关闭会话,都不能在应用层出现。最常见的是,一些实现了简单接口的DAO实现类将数据库逻辑完全封装起来了。一种快速但是笨拙的解决方法是放弃DAO模式,将数据库连接逻辑加到应用层中来。这可能对一些小的应用程序有效,但是在大的系统中,这是一个严重的设计缺陷,妨碍了系统的可扩展性。

  

在Web层进行延迟加载


  

  幸运的是,Spring框架为Hibernate延迟加载与DAO模式的整合提供了一种方便的解决方法。以一个Web应用为例,Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。我们可以随意选择一个类来实现相同的功能。两种方法唯一的不同就在于interceptor在Spring容器中运行并被配置在web应用的上下文中,而Filter在Spring之前运行并被配置在web.xml中。不管用哪个,他们都在请求将当前会话与当前(数据库)线程绑定时打开Hibernate会话。一旦已绑定到线程,这个打开了的Hibernate会话可以在DAO实现类中透明地使用。这个会话会为延迟加载数据库中值对象的视图保持打开状态。一旦这个逻辑视图完成了,Hibernate会话会在Filter的doFilter方法或者Interceptor的postHandle方法中被关闭。

  实现方法在web.xml中加入

  <filter>

  <filter-name>hibernateFilter</filter-name>

  <filter-class>

  org.springframework.orm.hibernate3.support.OpenSessionInViewFilter

  </filter-class>

  </filter

  <filter-mapping>

  <filter-name>hibernateFilter</filter-name>

  <url-pattern>*.do</url-pattern>

  </filter-mapping>

 来自: Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。

  Hibernate的核心接口一共有5个,分别为:Session、SessionFactory、Transaction、Query和Configuration。这5个核心接口在任何开发中都会用到。通过这些接口,不仅可以对持久化对象进行存取,还能够进行事务控制。下面对这五个核心接口分别加以介绍。

  ·Session接口:Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句。)。但需要注意的是Session对象是非线程安全的。同时,Hibernate的session不同于JSP应用中的HttpSession。这里当使用session这个术语时,其实指的是Hibernate中的session,而以后会将HttpSesion对象称为用户session。

  ·SessionFactory接口:SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。

  ·Configuration接口:Configuration接口负责配置并启动Hibernate,创建SessionFactory对象。在Hibernate的启动的过程中,Configuration类的实例首先定位映射文档位置、读取配置,然后创建SessionFactory对象。

  ·Transaction接口:Transaction接口负责事务相关的操作。它是可选的,开发人员也可以设计编写自己的底层事务处理代码。

  ·Query和Criteria接口:Query和Criteria接口负责执行各种数据库查询。它可以使用HQL语言或SQL语句两种表达方式。

Hibernate主键介绍

  Assigned

  Assigned方式由程序生成主键值,并且要在save()之前指定否则会抛出异常

  特点:主键的生成值完全由用户决定,与底层数据库无关。用户需要维护主键值,在调用session.save()之前要指定主键值。

  Hilo

  Hilo使用高低位算法生成主键,高低位算法使用一个高位值和一个低位值,然后把算法得到的两个值拼接起来作为数据库中的唯一主键。Hilo方式需要额外的数据库表和字段提供高位值来源。默认请况下使用的表是

  hibernate_unique_key,默认字段叫作next_hi。next_hi必须有一条记录否则会出现错误。

  特点:需要额外的数据库表的支持,能保证同一个数据库中主键的唯一性,但不能保证多个数据库之间主键的唯一性。Hilo主键生成方式由Hibernate 维护,所以Hilo方式与底层数据库无关,但不应该手动修改hi/lo算法使用的表的值,否则会引起主键重复的异常。

  Increment

  Increment方式对主键值采取自动增长的方式生成新的主键值,但要求底层数据库的支持Sequence。如Oracle,DB2等。需要在映射文件xxx.hbm.xml中加入Increment标志符的设置。

  特点:由Hibernate本身维护,适用于所有的数据库,不适合多进程并发更新数据库,适合单一进程访问数据库。不能用于群集环境。

  Identity

  Identity当时根据底层数据库,来支持自动增长,不同的数据库用不同的主键增长方式。

  特点:与底层数据库有关,要求数据库支持Identity,如MySQl中是auto_increment, SQL Server 中是Identity,支持的数据库有MySql、SQL Server、DB2、Sybase和HypersonicSQL。 Identity无需Hibernate和用户的干涉,使用较为方便,但不便于在不同的数据库之间移植程序。

  Sequence

  Sequence需要底层数据库支持Sequence方式,例如Oracle数据库等

  特点:需要底层数据库的支持序列,支持序列的数据库有DB2、PostgreSql、Qracle、SAPDb等在不同数据库之间移植程序,特别从支持序列的数据库移植到不支持序列的数据库需要修改配置文件

  Native

  Native主键生成方式会根据不同的底层数据库自动选择Identity、Sequence、Hilo主键生成方式

  特点:根据不同的底层数据库采用不同的主键生成方式。由于Hibernate会根据底层数据库采用不同的映射方式,因此便于程序移植,项目中如果用到多个数据库时,可以使用这种方式。

  UUID

  UUID使用128位UUID算法生成主键,能够保证网络环境下的主键唯一性,也就能够保证在不同数据库及不同服务器下主键的唯一性。

  特点;能够保证数据库中的主键唯一性,生成的主键占用比较多的存贮空间

  Foreign GUID

  Foreign用于一对一关系中。GUID主键生成方式使用了一种特殊算法,保证生成主键的唯一性,支持SQL Server和MySQL

Hibernate源码中几个包的作用简要介绍

  net.sf.hibernate.*  

  该包的类基本上都是接口类和异常类

  net.sf.hibernate.cache.*  

  JCS的实现类

  net.sf.hibernate.cfg.*  

  配置文件读取类

  net.sf.hibernate.collection.*  

  Hibernate集合接口实现类,例如List,Set,Bag等等,Hibernate之所以要自行编写集合接口实现类是为了支持lazy loading

  net.sf.hibernate.connection.*  

  几个数据库连接池的Provider

  net.sf.hibernate.dialect.*  

  支持多种数据库特性,每个Dialect实现类代表一种数据库,描述了该数据库支持的数据类型和其它特点,例如是否有AutoIncrement,是否有Sequence,是否有分页sql等等

  net.sf.hibernate.eg.*  

  Hibernate文档中用到的例子

  net.sf.hibernate.engine.*  

  这个包的类作用比较散

  net.sf.hibernate.expression.*  

  HQL支持的表达式

  net.sf.hibernate.hq.*  

  HQL实现

  net.sf.hibernate.id.*  

  ID生成器

  net.sf.hibernate.impl.*  

  最核心的包,一些重要接口的实现类,如果Session,SessionFactory,Query等

  net.sf.hibernate.jca.*  

  JCA支持,把Session包装为支持JCA的接口实现类

  net.sf.hibernate.jmx.*  

  我不懂JMX,只知道JMX是用来编写App Server的管理程序的,大概是JMX部分接口的实现,使得App Server可以通过JMX接口管理Hibernate

  net.sf.hibernate.loader.*  

  也是很核心的包,主要是生成sql语句的

  net.sf.hibernate.lob.*  

  Blob和Clob支持

  net.sf.hibernate.mapping.*  

  hbm文件的属性实现

  net.sf.hibernate.metadata.*  

  PO的Meta实现

  net.sf.hibernate.odmg.*  

  ODMG是一个ORM标准,这个包是ODMG标准的实现类

  net.sf.hibernate.persister.*  

  核心包,实现持久对象和表之间的映射

  net.sf.hibernate.proxy.*  

  Proxy和Lazy Loading支持

  net.sf.hibernate.ps.*  

  该包是PreparedStatment Cache

  net.sf.hibernate.sql.*  

  生成JDBC sql语句的包

  net.sf.hibernate.test.*  

  测试类,你可以用junit来测试Hibernate

  net.sf.hibernate.tool.hbm2ddl.*  

  用hbm配置文件生成DDL

  net.sf.hibernate.transaction.*  

  Hibernate Transaction实现类

  net.sf.hibernate.type.*  

  Hibernate中定义的持久对象的属性的数据类型

  net.sf.hibernate.util.*  

  一些工具类,作用比较散

  net.sf.hibernate.xml.*  

  XML数据绑定

缓存管理

  Hibernate 中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的,一般情况下无需进行干预;第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围或群集范围的缓存。这一级别的缓存可以进行配置和更改,并且可以动态加载和卸载。 Hibernate还为查询结果提供了一个查询缓存,它依赖于第二级缓存。

  1. 一级缓存和二级缓存的比较:第一级缓存 第二级缓存 存放数据的形式 相互关联的持久化对象 对象的散装数据 缓存的范围 事务范围,每个事务都有单独的第一级缓存进程范围或集群范围,缓存被同一个进程或集群范围内的所有事务共享 并发访问策略由于每个事务都拥有单独的第一级缓存,不会出现并发问题,无需提供并发访问策略由于多个事务会同时访问第二级缓存中相同数据,因此必须提供适当的并发访问策略,来保证特定的事务隔离级别 数据过期策略没有提供数据过期策略。处于一级缓存中的对象永远不会过期,除非应用程序显式清空缓存或者清除特定的对象必须提供数据过期策略,如基于内存的缓存中的对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处于缓存中的最长空闲时间 物理存储介质内存内存和硬盘。对象的散装数据首先存放在基于内在的缓存中,当内存中对象的数目达到数据过期策略中指定上限时,就会把其余的对象写入基于硬盘的缓存中。缓存的软件实现 在Hibernate的Session的实现中包含了缓存的实现由第三方提供,Hibernate仅提供了缓存适配器(CacheProvider)。用于把特定的缓存插件集成到Hibernate中。启用缓存的方式只要应用程序通过Session接口来执行保存、更新、删除、加载和查询数据库数据的操作,Hibernate就会启用第一级缓存,把数据库中的数据以对象的形式拷贝到缓存中,对于批量更新和批量删除操作,如果不希望启用第一级缓存,可以绕过Hibernate API,直接通过JDBC API来执行指操作。用户可以在单个类或类的单个集合的粒度上配置第二级缓存。如果类的实例被经常读但很少被修改,就可以考虑使用第二级缓存。只有为某个类或集合配置了第二级缓存,Hibernate在运行时才会把它的实例加入到第二级缓存中。 用户管理缓存的方式第一级缓存的物理介质为内存,由于内存容量有限,必须通过恰当的检索策略和检索方式来限制加载对象的数目。Session的evit()方法可以显式清空缓存中特定对象,但这种方法不值得推荐。 第二级缓存的物理介质可以是内存和硬盘,因此第二级缓存可以存放大量的数据,数据过期策略的maxElementsInMemory属性值可以控制内存中的对象数目。管理第二级缓存主要包括两个方面:选择需要使用第二级缓存的持久类,设置合适的并发访问策略:选择缓存适配器,设置合适的数据过期策略。

  2. 一级缓存的管理: 当应用程序调用Session的save()、update()、savaeOrUpdate()、get()或load(),以及调用查询接口的 list()、iterate()或filter()方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。 Session为应用程序提供了两个管理缓存的方法: evict(Object obj):从缓存中清除参数指定的持久化对象。 clear():清空缓存中所有持久化对象。

  3. 二级缓存的管理:

  3.1. Hibernate的二级缓存策略的一般过程如下:

  1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。

  2) 把获得的所有数据对象根据ID放入到第二级缓存中。

  3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。

  4) 删除、更新、增加数据的时候,同时更新缓存。

  Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query Cache。

  3.2. 什么样的数据适合存放到第二级缓存中? 1 很少被修改的数据 2 不是很重要的数据,允许出现偶尔并发的数据 3 不会被并发访问的数据 4 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。

  3.3. 不适合存放到第二级缓存的数据? 1 经常被修改的数据 2 财务数据,绝对不允许出现并发 3 与其他应用共享的数据。

  3.4. 常用的缓存插件 Hibernater 的二级缓存是一个插件,下面是几种常用的缓存插件:

  l EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。

  l OSCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持。

  l SwarmCache:可作为群集范围内的缓存,但不支持Hibernate的查询缓存。

  l JBossCache:可作为群集范围内的缓存,支持事务型并发访问策略,对Hibernate的查询缓存提供了支持。

  3.5. 配置二级缓存的主要步骤:

  1) 选择需要使用二级缓存的持久化类,设置它的命名缓存的并发访问策略。这是最值得认真考虑的步骤。

  2) 选择合适的缓存插件,然后编辑该插件的配置文件。

Hibernate与延迟加载

  Hibernate对象关系映射提供延迟的与非延迟的对象初始化。非延迟加载在读取一个对象的时候会将与这个对象所有相关的其他对象一起读取出来。这有时会导致成百的(如果不是成千的话)select语句在读取对象的时候执行。这个问题有时出现在使用双向关系的时候,经常会导致整个数据库都在初始化的阶段被读出来了。当然,你可以不厌其烦地检查每一个对象与其他对象的关系,并把那些最昂贵的删除,但是到最后,我们可能会因此失去了本想在ORM工具中获得的便利。

  一个明显的解决方法是使用Hibernate提供的延迟加载机制。这种初始化策略只在一个对象调用它的一对多或多对多关系时才将关系对象读取出来。这个过程对开发者来说是透明的,而且只进行了很少的数据库操作请求,因此会得到比较明显的性能提升。这项技术的一个缺陷是延迟加载技术要求一个Hibernate会话要在对象使用的时候一直开着。这会成为通过使用DAO模式将持久层抽象出来时的一个主要问题。为了将持久化机制完全地抽象出来,所有的数据库逻辑,包括打开或关闭会话,都不能在应用层出现。最常见的是,一些实现了简单接口的DAO实现类将数据库逻辑完全封装起来了。一种快速但是笨拙的解决方法是放弃DAO模式,将数据库连接逻辑加到应用层中来。这可能对一些小的应用程序有效,但是在大的系统中,这是一个严重的设计缺陷,妨碍了系统的可扩展性。

  

在Web层进行延迟加载


  

  幸运的是,Spring框架为Hibernate延迟加载与DAO模式的整合提供了一种方便的解决方法。以一个Web应用为例,Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。我们可以随意选择一个类来实现相同的功能。两种方法唯一的不同就在于interceptor在Spring容器中运行并被配置在web应用的上下文中,而Filter在Spring之前运行并被配置在web.xml中。不管用哪个,他们都在请求将当前会话与当前(数据库)线程绑定时打开Hibernate会话。一旦已绑定到线程,这个打开了的Hibernate会话可以在DAO实现类中透明地使用。这个会话会为延迟加载数据库中值对象的视图保持打开状态。一旦这个逻辑视图完成了,Hibernate会话会在Filter的doFilter方法或者Interceptor的postHandle方法中被关闭。

  实现方法在web.xml中加入

  <filter>

  <filter-name>hibernateFilter</filter-name>

  <filter-class>

  org.springframework.orm.hibernate3.support.OpenSessionInViewFilter

  </filter-class>

  </filter

  <filter-mapping>

  <filter-name>hibernateFilter</filter-name>

  <url-pattern>*.do</url-pattern>

  </filter-mapping>
来自 Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。

  Hibernate的核心接口一共有5个,分别为:Session、SessionFactory、Transaction、Query和Configuration。这5个核心接口在任何开发中都会用到。通过这些接口,不仅可以对持久化对象进行存取,还能够进行事务控制。下面对这五个核心接口分别加以介绍。

  ·Session接口:Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句。)。但需要注意的是Session对象是非线程安全的。同时,Hibernate的session不同于JSP应用中的HttpSession。这里当使用session这个术语时,其实指的是Hibernate中的session,而以后会将HttpSesion对象称为用户session。

  ·SessionFactory接口:SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。

  ·Configuration接口:Configuration接口负责配置并启动Hibernate,创建SessionFactory对象。在Hibernate的启动的过程中,Configuration类的实例首先定位映射文档位置、读取配置,然后创建SessionFactory对象。

  ·Transaction接口:Transaction接口负责事务相关的操作。它是可选的,开发人员也可以设计编写自己的底层事务处理代码。

  ·Query和Criteria接口:Query和Criteria接口负责执行各种数据库查询。它可以使用HQL语言或SQL语句两种表达方式。

Hibernate主键介绍

  Assigned

  Assigned方式由程序生成主键值,并且要在save()之前指定否则会抛出异常

  特点:主键的生成值完全由用户决定,与底层数据库无关。用户需要维护主键值,在调用session.save()之前要指定主键值。

  Hilo

  Hilo使用高低位算法生成主键,高低位算法使用一个高位值和一个低位值,然后把算法得到的两个值拼接起来作为数据库中的唯一主键。Hilo方式需要额外的数据库表和字段提供高位值来源。默认请况下使用的表是

  hibernate_unique_key,默认字段叫作next_hi。next_hi必须有一条记录否则会出现错误。

  特点:需要额外的数据库表的支持,能保证同一个数据库中主键的唯一性,但不能保证多个数据库之间主键的唯一性。Hilo主键生成方式由Hibernate 维护,所以Hilo方式与底层数据库无关,但不应该手动修改hi/lo算法使用的表的值,否则会引起主键重复的异常。

  Increment

  Increment方式对主键值采取自动增长的方式生成新的主键值,但要求底层数据库的支持Sequence。如Oracle,DB2等。需要在映射文件xxx.hbm.xml中加入Increment标志符的设置。

  特点:由Hibernate本身维护,适用于所有的数据库,不适合多进程并发更新数据库,适合单一进程访问数据库。不能用于群集环境。

  Identity

  Identity当时根据底层数据库,来支持自动增长,不同的数据库用不同的主键增长方式。

  特点:与底层数据库有关,要求数据库支持Identity,如MySQl中是auto_increment, SQL Server 中是Identity,支持的数据库有MySql、SQL Server、DB2、Sybase和HypersonicSQL。 Identity无需Hibernate和用户的干涉,使用较为方便,但不便于在不同的数据库之间移植程序。

  Sequence

  Sequence需要底层数据库支持Sequence方式,例如Oracle数据库等

  特点:需要底层数据库的支持序列,支持序列的数据库有DB2、PostgreSql、Qracle、SAPDb等在不同数据库之间移植程序,特别从支持序列的数据库移植到不支持序列的数据库需要修改配置文件

  Native

  Native主键生成方式会根据不同的底层数据库自动选择Identity、Sequence、Hilo主键生成方式

  特点:根据不同的底层数据库采用不同的主键生成方式。由于Hibernate会根据底层数据库采用不同的映射方式,因此便于程序移植,项目中如果用到多个数据库时,可以使用这种方式。

  UUID

  UUID使用128位UUID算法生成主键,能够保证网络环境下的主键唯一性,也就能够保证在不同数据库及不同服务器下主键的唯一性。

  特点;能够保证数据库中的主键唯一性,生成的主键占用比较多的存贮空间

  Foreign GUID

  Foreign用于一对一关系中。GUID主键生成方式使用了一种特殊算法,保证生成主键的唯一性,支持SQL Server和MySQL

Hibernate源码中几个包的作用简要介绍

  net.sf.hibernate.*  

  该包的类基本上都是接口类和异常类

  net.sf.hibernate.cache.*  

  JCS的实现类

  net.sf.hibernate.cfg.*  

  配置文件读取类

  net.sf.hibernate.collection.*  

  Hibernate集合接口实现类,例如List,Set,Bag等等,Hibernate之所以要自行编写集合接口实现类是为了支持lazy loading

  net.sf.hibernate.connection.*  

  几个数据库连接池的Provider

  net.sf.hibernate.dialect.*  

  支持多种数据库特性,每个Dialect实现类代表一种数据库,描述了该数据库支持的数据类型和其它特点,例如是否有AutoIncrement,是否有Sequence,是否有分页sql等等

  net.sf.hibernate.eg.*  

  Hibernate文档中用到的例子

  net.sf.hibernate.engine.*  

  这个包的类作用比较散

  net.sf.hibernate.expression.*  

  HQL支持的表达式

  net.sf.hibernate.hq.*  

  HQL实现

  net.sf.hibernate.id.*  

  ID生成器

  net.sf.hibernate.impl.*  

  最核心的包,一些重要接口的实现类,如果Session,SessionFactory,Query等

  net.sf.hibernate.jca.*  

  JCA支持,把Session包装为支持JCA的接口实现类

  net.sf.hibernate.jmx.*  

  我不懂JMX,只知道JMX是用来编写App Server的管理程序的,大概是JMX部分接口的实现,使得App Server可以通过JMX接口管理Hibernate

  net.sf.hibernate.loader.*  

  也是很核心的包,主要是生成sql语句的

  net.sf.hibernate.lob.*  

  Blob和Clob支持

  net.sf.hibernate.mapping.*  

  hbm文件的属性实现

  net.sf.hibernate.metadata.*  

  PO的Meta实现

  net.sf.hibernate.odmg.*  

  ODMG是一个ORM标准,这个包是ODMG标准的实现类

  net.sf.hibernate.persister.*  

  核心包,实现持久对象和表之间的映射

  net.sf.hibernate.proxy.*  

  Proxy和Lazy Loading支持

  net.sf.hibernate.ps.*  

  该包是PreparedStatment Cache

  net.sf.hibernate.sql.*  

  生成JDBC sql语句的包

  net.sf.hibernate.test.*  

  测试类,你可以用junit来测试Hibernate

  net.sf.hibernate.tool.hbm2ddl.*  

  用hbm配置文件生成DDL

  net.sf.hibernate.transaction.*  

  Hibernate Transaction实现类

  net.sf.hibernate.type.*  

  Hibernate中定义的持久对象的属性的数据类型

  net.sf.hibernate.util.*  

  一些工具类,作用比较散

  net.sf.hibernate.xml.*  

  XML数据绑定

缓存管理

  Hibernate 中提供了两级Cache,第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的,一般情况下无需进行干预;第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围或群集范围的缓存。这一级别的缓存可以进行配置和更改,并且可以动态加载和卸载。 Hibernate还为查询结果提供了一个查询缓存,它依赖于第二级缓存。

  1. 一级缓存和二级缓存的比较:第一级缓存 第二级缓存 存放数据的形式 相互关联的持久化对象 对象的散装数据 缓存的范围 事务范围,每个事务都有单独的第一级缓存进程范围或集群范围,缓存被同一个进程或集群范围内的所有事务共享 并发访问策略由于每个事务都拥有单独的第一级缓存,不会出现并发问题,无需提供并发访问策略由于多个事务会同时访问第二级缓存中相同数据,因此必须提供适当的并发访问策略,来保证特定的事务隔离级别 数据过期策略没有提供数据过期策略。处于一级缓存中的对象永远不会过期,除非应用程序显式清空缓存或者清除特定的对象必须提供数据过期策略,如基于内存的缓存中的对象的最大数目,允许对象处于缓存中的最长时间,以及允许对象处于缓存中的最长空闲时间 物理存储介质内存内存和硬盘。对象的散装数据首先存放在基于内在的缓存中,当内存中对象的数目达到数据过期策略中指定上限时,就会把其余的对象写入基于硬盘的缓存中。缓存的软件实现 在Hibernate的Session的实现中包含了缓存的实现由第三方提供,Hibernate仅提供了缓存适配器(CacheProvider)。用于把特定的缓存插件集成到Hibernate中。启用缓存的方式只要应用程序通过Session接口来执行保存、更新、删除、加载和查询数据库数据的操作,Hibernate就会启用第一级缓存,把数据库中的数据以对象的形式拷贝到缓存中,对于批量更新和批量删除操作,如果不希望启用第一级缓存,可以绕过Hibernate API,直接通过JDBC API来执行指操作。用户可以在单个类或类的单个集合的粒度上配置第二级缓存。如果类的实例被经常读但很少被修改,就可以考虑使用第二级缓存。只有为某个类或集合配置了第二级缓存,Hibernate在运行时才会把它的实例加入到第二级缓存中。 用户管理缓存的方式第一级缓存的物理介质为内存,由于内存容量有限,必须通过恰当的检索策略和检索方式来限制加载对象的数目。Session的evit()方法可以显式清空缓存中特定对象,但这种方法不值得推荐。 第二级缓存的物理介质可以是内存和硬盘,因此第二级缓存可以存放大量的数据,数据过期策略的maxElementsInMemory属性值可以控制内存中的对象数目。管理第二级缓存主要包括两个方面:选择需要使用第二级缓存的持久类,设置合适的并发访问策略:选择缓存适配器,设置合适的数据过期策略。

  2. 一级缓存的管理: 当应用程序调用Session的save()、update()、savaeOrUpdate()、get()或load(),以及调用查询接口的 list()、iterate()或filter()方法时,如果在Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。 Session为应用程序提供了两个管理缓存的方法: evict(Object obj):从缓存中清除参数指定的持久化对象。 clear():清空缓存中所有持久化对象。

  3. 二级缓存的管理:

  3.1. Hibernate的二级缓存策略的一般过程如下:

  1) 条件查询的时候,总是发出一条select * from table_name where …. (选择所有字段)这样的SQL语句查询数据库,一次获得所有的数据对象。

  2) 把获得的所有数据对象根据ID放入到第二级缓存中。

  3) 当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。

  4) 删除、更新、增加数据的时候,同时更新缓存。

  Hibernate的二级缓存策略,是针对于ID查询的缓存策略,对于条件查询则毫无作用。为此,Hibernate提供了针对条件查询的Query Cache。

  3.2. 什么样的数据适合存放到第二级缓存中? 1 很少被修改的数据 2 不是很重要的数据,允许出现偶尔并发的数据 3 不会被并发访问的数据 4 参考数据,指的是供应用参考的常量数据,它的实例数目有限,它的实例会被许多其他类的实例引用,实例极少或者从来不会被修改。

  3.3. 不适合存放到第二级缓存的数据? 1 经常被修改的数据 2 财务数据,绝对不允许出现并发 3 与其他应用共享的数据。

  3.4. 常用的缓存插件 Hibernater 的二级缓存是一个插件,下面是几种常用的缓存插件:

  l EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。

  l OSCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据过期策略,对Hibernate的查询缓存提供了支持。

  l SwarmCache:可作为群集范围内的缓存,但不支持Hibernate的查询缓存。

  l JBossCache:可作为群集范围内的缓存,支持事务型并发访问策略,对Hibernate的查询缓存提供了支持。

  3.5. 配置二级缓存的主要步骤:

  1) 选择需要使用二级缓存的持久化类,设置它的命名缓存的并发访问策略。这是最值得认真考虑的步骤。

  2) 选择合适的缓存插件,然后编辑该插件的配置文件。

Hibernate与延迟加载

  Hibernate对象关系映射提供延迟的与非延迟的对象初始化。非延迟加载在读取一个对象的时候会将与这个对象所有相关的其他对象一起读取出来。这有时会导致成百的(如果不是成千的话)select语句在读取对象的时候执行。这个问题有时出现在使用双向关系的时候,经常会导致整个数据库都在初始化的阶段被读出来了。当然,你可以不厌其烦地检查每一个对象与其他对象的关系,并把那些最昂贵的删除,但是到最后,我们可能会因此失去了本想在ORM工具中获得的便利。

  一个明显的解决方法是使用Hibernate提供的延迟加载机制。这种初始化策略只在一个对象调用它的一对多或多对多关系时才将关系对象读取出来。这个过程对开发者来说是透明的,而且只进行了很少的数据库操作请求,因此会得到比较明显的性能提升。这项技术的一个缺陷是延迟加载技术要求一个Hibernate会话要在对象使用的时候一直开着。这会成为通过使用DAO模式将持久层抽象出来时的一个主要问题。为了将持久化机制完全地抽象出来,所有的数据库逻辑,包括打开或关闭会话,都不能在应用层出现。最常见的是,一些实现了简单接口的DAO实现类将数据库逻辑完全封装起来了。一种快速但是笨拙的解决方法是放弃DAO模式,将数据库连接逻辑加到应用层中来。这可能对一些小的应用程序有效,但是在大的系统中,这是一个严重的设计缺陷,妨碍了系统的可扩展性。

  

在Web层进行延迟加载


  

  幸运的是,Spring框架为Hibernate延迟加载与DAO模式的整合提供了一种方便的解决方法。以一个Web应用为例,Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。我们可以随意选择一个类来实现相同的功能。两种方法唯一的不同就在于interceptor在Spring容器中运行并被配置在web应用的上下文中,而Filter在Spring之前运行并被配置在web.xml中。不管用哪个,他们都在请求将当前会话与当前(数据库)线程绑定时打开Hibernate会话。一旦已绑定到线程,这个打开了的Hibernate会话可以在DAO实现类中透明地使用。这个会话会为延迟加载数据库中值对象的视图保持打开状态。一旦这个逻辑视图完成了,Hibernate会话会在Filter的doFilter方法或者Interceptor的postHandle方法中被关闭。

  实现方法在web.xml中加入

  <filter>

  <filter-name>hibernateFilter</filter-name>

  <filter-class>

  org.springframework.orm.hibernate3.support.OpenSessionInViewFilter

  </filter-class>

  </filter

  <filter-mapping>

  <filter-name>hibernateFilter</filter-name>

  <url-pattern>*.do</url-pattern>

  </filter-mapping>

《一 上 http://baike.baidu.com/view/7291.htm

什么是Hibernate
       说简单点就是对JDBC的封装。我们知道,我们常用的数据库都是关系型的,而我们的编程思维是OO(面向对象)的,而Hibernate就是想使用面向对象的思想来操作数据库。看来,Hibernate也不是非常神秘,只是一个工具。我们最需要适应的是编程思维的改变。

Hibernate的核心接口一共有5个,分别为:Session、SessionFactory、Transaction、Query和Configuration。这5个核心接口在任何开发中都会用到。通过这些接口,不仅可以对持久化对象进行存取,还能够进行事务控制。下面对这五个核心接口分别加以介绍。

  ·Session接口:Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句。)。但需要注意的是Session对象是非线程安全的。同时,Hibernate的session不同于JSP应用中的HttpSession。这里当使用session这个术语时,其实指的是Hibernate中的session,而以后会将HttpSesion对象称为用户session。

  ·SessionFactory接口:SessionFactroy接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。

  ·Configuration接口:Configuration接口负责配置并启动Hibernate,创建SessionFactory对象。在Hibernate的启动的过程中,Configuration类的实例首先定位映射文档位置、读取配置,然后创建SessionFactory对象。

  ·Transaction接口:Transaction接口负责事务相关的操作。它是可选的,开发人员也可以设计编写自己的底层事务处理代码。

  ·Query和Criteria接口:Query和Criteria接口负责执行各种数据库查询。它可以使用HQL语言或SQL语句两种表达方式。

  Assigned

  Assigned方式由程序生成主键值,并且要在save()之前指定否则会抛出异常

  特点:主键的生成值完全由用户决定,与底层数据库无关。用户需要维护主键值,在调用session.save()之前要指定主键值。

  Hilo

  Hilo使用高低位算法生成主键,高低位算法使用一个高位值和一个低位值,然后把算法得到的两个值拼接起来作为数据库中的唯一主键。Hilo方式需要额外的数据库表和字段提供高位值来源。默认请况下使用的表是

  hibernate_unique_key,默认字段叫作next_hi。next_hi必须有一条记录否则会出现错误。

  特点:需要额外的数据库表的支持,能保证同一个数据库中主键的唯一性,但不能保证多个数据库之间主键的唯一性。Hilo主键生成方式由Hibernate 维护,所以Hilo方式与底层数据库无关,但不应该手动修改hi/lo算法使用的表的值,否则会引起主键重复的异常。

  Increment

  Increment方式对主键值采取自动增长的方式生成新的主键值,但要求底层数据库的支持Sequence。如Oracle,DB2等。需要在映射文件xxx.hbm.xml中加入Increment标志符的设置。

  特点:由Hibernate本身维护,适用于所有的数据库,不适合多进程并发更新数据库,适合单一进程访问数据库。不能用于群集环境。

  Identity

  Identity当时根据底层数据库,来支持自动增长,不同的数据库用不同的主键增长方式。

  特点:与底层数据库有关,要求数据库支持Identity,如MySQl中是auto_increment, SQL Server 中是Identity,支持的数据库有MySql、SQL Server、DB2、Sybase和HypersonicSQL。 Identity无需Hibernate和用户的干涉,使用较为方便,但不便于在不同的数据库之间移植程序。

  Sequence

  Sequence需要底层数据库支持Sequence方式,例如Oracle数据库等

  特点:需要底层数据库的支持序列,支持序列的数据库有DB2、PostgreSql、Qracle、SAPDb等在不同数据库之间移植程序,特别从支持序列的数据库移植到不支持序列的数据库需要修改配置文件

  Native

  Native主键生成方式会根据不同的底层数据库自动选择Identity、Sequence、Hilo主键生成方式

  特点:根据不同的底层数据库采用不同的主键生成方式。由于Hibernate会根据底层数据库采用不同的映射方式,因此便于程序移植,项目中如果用到多个数据库时,可以使用这种方式。

  UUID

  UUID使用128位UUID算法生成主键,能够保证网络环境下的主键唯一性,也就能够保证在不同数据库及不同服务器下主键的唯一性。

  特点;能够保证数据库中的主键唯一性,生成的主键占用比较多的存贮空间

  Foreign GUID

  Foreign用于一对一关系中。GUID主键生成方式使用了一种特殊算法,保证生成主键的唯一性,支持SQL Server和MySQL

Hibernate源码中几个包的作用简要介绍  

net.sf.hibernate.*  

  该包的类基本上都是接口类和异常类

  net.sf.hibernate.cache.*  

  JCS的实现类

  net.sf.hibernate.cfg.*  

  配置文件读取类

  net.sf.hibernate.collection.*  

  Hibernate集合接口实现类,例如List,Set,Bag等等,Hibernate之所以要自行编写集合接口实现类是为了支持lazy loading

  net.sf.hibernate.connection.*  

  几个数据库连接池的Provider

  net.sf.hibernate.dialect.*  

  支持多种数据库特性,每个Dialect实现类代表一种数据库,描述了该数据库支持的数据类型和其它特点,例如是否有AutoIncrement,是否有Sequence,是否有分页sql等等

  net.sf.hibernate.eg.*  

  Hibernate文档中用到的例子

  net.sf.hibernate.engine.*  

  这个包的类作用比较散

  net.sf.hibernate.expression.*  

  HQL支持的表达式

  net.sf.hibernate.hq.*  

  HQL实现

  net.sf.hibernate.id.*  

  ID生成器

  net.sf.hibernate.impl.*  

  最核心的包,一些重要接口的实现类,如果Session,SessionFactory,Query等

  net.sf.hibernate.jca.*  

  JCA支持,把Session包装为支持JCA的接口实现类

  net.sf.hibernate.jmx.*  

  我不懂JMX,只知道JMX是用来编写App Server的管理程序的,大概是JMX部分接口的实现,使得App Server可以通过JMX接口管理Hibernate

  net.sf.hibernate.loader.*  

  也是很核心的包,主要是生成sql语句的

  net.sf.hibernate.lob.*  

  Blob和Clob支持

  net.sf.hibernate.mapping.*  

  hbm文件的属性实现

  net.sf.hibernate.metadata.*  

  PO的Meta实现

  net.sf.hibernate.odmg.*  

  ODMG是一个ORM标准,这个包是ODMG标准的实现类

  net.sf.hibernate.persister.*  

  核心包,实现持久对象和表之间的映射

  net.sf.hibernate.proxy.*  

  Proxy和Lazy Loading支持

  net.sf.hibernate.ps.*  

  该包是PreparedStatment Cache

  net.sf.hibernate.sql.*  

  生成JDBC sql语句的包

  net.sf.hibernate.test.*  

  测试类,你可以用junit来测试Hibernate

  net.sf.hibernate.tool.hbm2ddl.*  

  用hbm配置文件生成DDL

  net.sf.hibernate.transaction.*  

  Hibernate Transaction实现类

  net.sf.hibernate.type.*  

  Hibernate中定义的持久对象的属性的数据类型

  net.sf.hibernate.util.*  

  一些工具类,作用比较散

  net.sf.hibernate.xml.*  

  XML数据绑定

来之:http://blog.163.com/imeson/blog/static/111041978200932235957835/

 

 

(二)

Hibernate入门知识

Hibernate基本映射、关系映射

Hibernate数据查询等知识。

HibernateSpring整合的各种方式

HibernateSpring中的事务管理

 

 

Hibernate是目前最流行的ORM框架,其采用非常优雅的方式,将SQL操作完全包装成对象化的操作。其作者Gavin King在持久层设计上极富经验,采用非常少的代码实现了整个框架,同时完全开放源代码,即使偶尔遇到无法理解的情况,可参照源代码来理解其在持久层上灵巧而智能的设计。

目前,Hibernate在国内的开发人员相当多,Hibernate的文档非常丰富,这些都为学习Hibernate铺平了道路,因而Hibernate的学习相对简单一些。下面对比Hibernate和传统JDBC操作数据库持久层差异。

10.2.1 采用传统JDBC操作数据库

先看这样一个需求:向数据库里增加一条新闻,新闻有新闻Id、新闻标题、新闻内容三个属性。在传统的JDBC数据库访问里,完成此功能并不难。本程序采用MySql数据库,我们可采用如下方法来来完成:

import java.sql.*;

public class NewsDao

{

/**

 * @param News 需要保存的新闻实例

 */

public void saveNews(News news)

    {

        Connection conn = null;

        PreparedStatement pstmt = null;

        int newsId = news.getId();

        String title = news.getTitle();

        String content = news.getContent();

        try

        {

            //注册驱动

Class.forName("com.mysql.jdbc.Driver");

/* hibernate:想连接的数据库

  user:接数据库的用户名

  pass:连接数据库的密码

*/

            String url="jdbc:mysql://localhost/hibernate?user=root&password=pass";

            //获取连接

conn= DriverManager.getConnection(url);

//创建预编译的Statement

pstmt=conn.prepareStatement("insert into news_table values(?,?,?)");

//下面语句为预编译Statement传入参数

            pstmt.setInt(1,newsId);

            pstmt.setString(2,title);

pstmt.setString(3,content);

//执行更新

pstmt.executeUpdate();

        }

        catch (ClassNotFoundException cnf)

        {

            cnf.printStackTrace();

        }

        catch (SQLException se)

        {

            se.printStackTrace();

        }

        finally

        {

            try

            {

                //关闭预编译的Statement

if (pstmt != null)pstmt.close();

//关闭连接

                if (conn != null) conn.close();

            }

            catch (SQLException se2)

            {

                se2.printStackTrace();

            }

        }

    }

}

这种操作方式丝毫没有面向对象的优雅和易用,而是一种纯粹的过程式操作,在这种简单的数据库访问里,我们没有过多地感觉到这种方式的复杂与缺陷。但我们还是体会到Hibernate的灵巧。

10.2.2 Hibernate下载和安装

Hibernate目前的最新版本是3.1.2,本章所用的代码也是基于该版本测试通过。安装和使用Hibernate请按如下步骤进行:

1)登录http://www.hibernate.org网站,下载Hibernate的二进制包。windows平台下载zip包,linux平台下载tar包。

2)解压缩刚下载的压缩包,在hibernate-3.1路径下有个hibernate3.jar的压缩文件,该文件是Hibernate的核心类库文件。该路径下还有lib路径,该路径包含Hibernate编译和运行的第三方类库。关于这些类库的使用,请参看该路径下的readme.txt文件。

3)将必需的Hibernate类库添加到JDKCLASSPATH里,或者使用Ant工具。总之,编译和运行时可以找到这些类即可。对于Web应用,则应将这些类库增加到WEB-INF/lib下。

10.2.3 Hibernate初探

在使用Hibernate之前,首先了解一个概念:POPersistent Object)持久化对象。持久化对象的作用是完成持久化操作,简单地说,通过该对象可对数据执行增、删、改的操作——以面向对象的方式操作数据库。

Hibernate里的PO是非常简单的,前面已经说过Hibernate是低侵入式的设计,完全采用普通Java对象来作为持久化对象使用,看下面的POJO(普通Java对象)类

public class News

{

    int id;

    String title;

    String content;

         public void setId(int id)

    {

                   this.id = id;

         }

         public int getId()

{

                   return (this.id);

         }

         ……

}

为了节省篇幅,笔者并未列出该类的title属性,和content属性的settergetter方法,读者可自行增加。这个类与常规的JavaBean没有任何区别,是个非常标准的简单JavaBean

目前,这个普通的JavaBean还不具备持久化操作的能力,为了使其具备持久化操作的能力,Hibernate采用XML映射文件,该映射文件也是非常简单。下面提供该XML文件的全部代码:

<?xml version="1.0" encoding=”gb2312”?>

<!DOCTYPE hibernate-mapping

    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<!--上面四行对所有的hibernate映射文件都相同 -->

<!-- hibernate-mapping是映射文件的根元素 -->

<hibernate-mapping>

<!-- 每个class元素对应一个持久化对象 -->

<class name="News" table=”news_table”>

    <-- id元素定义持久化类的标识属性 -->

        <id name = "id" unsaved-value = "null">

            <generator class="increment"/>

        </id>

        <!--  property元素定义常规属性 -->

        <property name="title"/>

        <property name="content"/>

    </class>

</hibernate-mapping>

对这个文件简单地解释一下:从14行,是该XML文件的文件头部分,定义该文件的XML版本,还有DTD,这四行对于所有Hibernate3.x的映射文件全部相同。hibernate-mapping元素是所有Hibernate映射文件的根元素,这个根元素对所有的映射文件都是相同的。

hibernate-mapping元素下有子元素class元素,每个class元素映射一个PO,更准确地说,应该是持久化类。可以看到:PO = POJO + 映射文件

现在,即可通过这个持久化类完成数据库的访问:插入一条新闻。在插入一条新闻之前,还必须完成接受Hibernate管理的数据库的配置——连接数据库所需的用户名、密码,以及数据库名等等基本信息。配置这些基本信息,可通过.properties的属性文件,或hibernate.cfg.xml配置文件配置。本文采用hibernate.cfg.xml的配置方式。下面是这个配置文件的详细代码:

<?xml version='1.0' encoding='utf-8'?>

<!DOCTYPE hibernate-configuration PUBLIC

        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<!--上面四行对所有的hibernate连接配置文件都相同 -->

<!-- hibernate- configuration是连接配置文件的根元素 -->

<hibernate-configuration>

<session-factory>

    <!-- 指定连接数据库所用的驱动 -->

        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>

        <!-- 指定连接数据库的urlHibernate连接的数据库名 -->

        <property name="connection.url">jdbc:mysql://localhost/hibernate</property>

        <!-- 指定连接数据库的用户名 -->

        <property name="connection.username">root</property>

        <!-- 指定连接数据库的密码 -->

        <property name="connection.password">pass</property>

        <!-- 指定连接池的大小-->

        <property name="connection.pool_size">5</property>

        <!-- 指定数据库方言-->

        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <!-- 根据需要自动创建数据库-->

        <property name="hbm2ddl.auto">create</property>

        <!-- 罗列所有的映射文静-->

        <mapping resource="News.hbm.xml"/>

    </session-factory>

</hibernate-configuration>

该配置文件非常简单,前面四行都是XML的基本定义和DTD声明,所有Hibernate配置文件前面四行都完全相同。Hibernate配置文件的根元素是hibernate-configuration,根元素里有子元素session-factory元素,该元素依次有很多property元素,property元素依次定义连接数据库的驱动、url、用户名、密码、数据库连接池的大小(关于连接池的概念和用法请看9.2节)。还定义一个名为dialect的属性,该属性定义Hibernate连接的数据库类型是MySQL,针对该数据库的特性,Hibernate会在访问时进行优化。最后一行mapping定义持久化类的映射文件。如果有多个持久化映射文件,罗列多个mapping元素在此处即可。下面是完成新闻插入的代码

public class NewsDaoHibernate

{

         Configuration configuration;

         SessionFactory sessionFactory;

         Session session;

    public void saveNews(News news)

    {

//实例化Configuration

configuration=new Configuration().configure();

//实例化SessionFactory

sessionFactory = configuration.buildSessionFactory();

//实例化Session

session = sessionFactory.openSession();

//开始事务

Transaction tx = session.beginTransaction();

//增加新闻

session.save(news);

//提交事务

tx.commit();

//关闭Session

session.close();

    }

}

此时的代码结构非常清晰,保存新闻仅仅只需要个语句:session.save(news);而且是完全对象化的操作方式,可以说是非常简单、明了。代码显示:执行session.save(News)之前,先要获取Session对象。PO只有在Session的管理下,才可完成数据库访问。随POSession的关系,PO可有如下三个状态:瞬态、持久化、脱管。

PO的操作,必须在Session的管理下才能同步到数据库。SessionSessionFactory工厂产生,SessionFactory是数据库编译后的内存镜像,通常,一个应用对应一个SessionFactory对象。SessionFactory对象由Configuration对象生成。Configuration对象用来加载Hibernate配置文件。最后,使用如下方法来完成对新闻的增加:

public static void main(String[] args)

{

News n = new News();

         n.setTitle("新闻标题");

         n.setContent("新闻内容");

         NewsDaoHibernate ndh = new NewsDaoHibernate();

         ndh.saveNews(n);

}

这里仅仅提供一个主方法,相信读者可以将其补充完整,然后完成新闻的添加。

10.2.4 Hibernate的基本映射

在上面的例子里,可看到一个简单的Hibernate映射文件,每个Hibernate映射文件的基本结构都是相同的。映射文件的根元素为hibernate-mapping元素,这个元素下可以拥有多个class子元素,每个class子元素完成一个持久化类的映射。如下是一个映射文件的基本结构,在hibernate-mapping元素下可以有多个class子元素。

<hibernate-mapping>

                   <class/>

                   <class/>

                   ……

</hibernate-mapping>

接下来看class元素,每个class元素对应一个持久化类。首先,必须采用name元素来指定该持久化类的类名,此处的类名应该是全限定的类名。如果不使用全限定的类名,则必须在hibernate-mapping元素里指定package元素,package元素指定持久化类所在的包路径。

持久化类都需要有一个标识属性,该标识属性用来标识该久化类的实例,因此,标识属性通常被映射成数据表主键。标识属性通过id元素来指定。id元素的name属性的值,就是持久化类标识属性名。

通常,标识属性应该指定主键生成策略,Hibernate建议数据表采用逻辑主键,而不要采用有物理含义的实体主键。逻辑主键没有实际意义,仅仅用来标识一行记录,通常由Hibernate负责生成。主键生成器负责生成数据表记录的主键。通常采用如下常见的主键生成器:

q      increment:对longshortint的数据列生成自增长主键

q      identity:对longshortint的数据列,对如SQL ServerMySQL等支持自动增长列的数据库,生成自增长主键。

q      sequence:对longshortint的数据列,对如OracleDB2等支持 Sequence的数据库,生成自增长主键。

q      uuid:对字符串列的数据采用128-UUID算法,生成唯一的字符串主键。

property元素定义持久化类的普通属性,该持久化类有多少个普通属性,就需要有多少个property元素,class元素的结构如下所示:

<class name="News" table=”news_table”>

    <!-- 定义标识属性-->

<id name = "id" unsaved-value = "null">

    <!-- 定义主键生成器-->

        <generator class="increment"/>

</id>

<!-- 用于定义普通属性 -->

    <property />

    <property />

</class>

<property/>元素的定义相对简单,基本property映射只需要name属性,name属性映射持久类的属性名。如果想指定属性在数据表里存储的列名,还可以定义column属性来强制指定列名。列名默认与属性名相同。

10.2.5 Hibernate的关系映射

关系是关系型数据库的最基本特征,也是客观世界最基本,最必须的抽象。关系可分为如下两个类:

q      单向关系

q      双向关系

单向关系里又分为:

q      1 – 1

q      1 – N

q      N – 1

q      N –N

双向关系里也可分为:

q      1 – 1

q      1 – N

q      N – N

双向关系里没有N – 1因为双向关系1 – NN –1是完全相同的。

单向的N – 1

先看如下两个POJO

import java.util.Set;

import java.util.HashSet;

public class Person

{

private int personid;

         private String name;

         private int age;

private Address address;

 

         // 此处省略personid,name,age三个属性的setter.getter方法

/**

 * @return 返回此人对应的地址

 * /

    public Address getAddress()

{

        return address;

}

/**

 * @param address 修改人对应的地址

 * /

    public void setAddress(Address address)

{

        this.address = address;

    }        

}

这是Person对应的POJO,每个Person单向地对应一个Address。无法从Address端来访问Person。下面是AddressPOJO

public class Address

{

         private int addressid;

         private String addressdetail;

         /**

          * 设置该地址的标识属性

          * @param addressid Address标识属性

          */    

         public void setAddressid(int addressid)

{

                   this.addressid = addressid;

         }

         /**

          * 设置该地址的详细地址位置

          * @param addressdetail Address的详细地址位置

          */    

         public void setAddressdetail(String addressdetail)

{

                   this.addressdetail = addressdetail;

         }

         /**

          * 返回该地址的标识属性

          * @return 地址的标识属性

          */    

         public int getAddressid()

{

                   return (this.addressid);

         }

         /**

          * 返回该地址的详细地址位置

          * @return 地址的详细地址位置

          */    

         public String getAddressdetail()

{

                   return (this.addressdetail);

         }       

}

POJO加上其XML配置文件,就成为POPersonAddress两个POJO之间,存在单向N – 1的关系。因此,映射文件在基本映射的基础上,增加关系映射。两个POJO对应的映射文件如下所示:

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping

    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping >

    <!-- 下面class元素映射的是持久化类Person-->

    <class name="Person">

         <!-- 映射标识属性personid -->

<id name="personid" >

             <!-- 定义主键生成器 -->    

<generator class="identity"/>

         </id>

         <property name="name"/>

         <property name="age"/>

             <!-- 用来映射关联的PO column,是Address在该表中的外键列名 –>

<many-to-one name="address" column="addressId" />

</class>

<!--  下面持久化类映射Address -->

<class name="Address">

<!--  映射标识属性 -->

        <id name="addressid">

            <generator class="identity"/>

        </id>

        <property name="addressdetail"/>

    </class>

</hibernate-mapping>

关系映射成功后,对于从前复杂的JDBC多表连接查询,可采用面向对象的方式来访问记录。例如:需要查询id6的人所在地址。以前的SQL查询将是2个表的连接查询。Hibernate允许我们通过如下简单的代码完成。

public class AddressDaoHibernate

{

         Configuration configuration;

         SessionFactory sessionFactory;

         Session session;

         /**

          * @param personid 需要查询的人的id

          * @return 返回idpersonid的人的住址

      */

    public String find(int personid)

    {

//实例化Configuration

configuration=new Configuration().configure();

//实例化SessionFactory

sessionFactory = configuration.buildSessionFactory();

//实例化Session

session = sessionFactory.openSession();

//开始事务

Transaction tx = session.beginTransaction();

//查询到id personidAddress

Person p = (Person)session.load(Person.class,new Integer(personid));

//通过p直接返回地址

Address a = p.getAddress();

//打印出地址信息

return a. getAddressdetail();

//提交事务

tx.commit();

//关闭Session

session.close();

}

}

上面的操作,完全看不出底层数据表之间如何互相连接。这种连接无须程序开发者关心,程序开发者的操作方式完全是面向对象的。

双向N - 1关系映射

在双向映射中,Person这个POJO类无须任何修改,不再赘叙。下面给出Address的源文件:

public class Address

{

         private int addressid;

         private String addressdetail;

         //一个地址可以对应多个人

private List persons = new ArrayList();

/**

 * @param addressid 地址id

 */

         public void setAddressid(int addressid)

         {

                   this.addressid = addressid;

         }

/**

 * @param addressdetail地址详细信息

         */

         public void setAddressdetail(String addressdetail)

         {

                   this.addressdetail = addressdetail;

         }

/**

 * @return 地址id

         */

         public int getAddressid()

         {

                   return addressid;

         }

/**

 * @return 地址详细信息

         */

         public String getAddressdetail()

         {

                   return addressdetail;

         }

/**

 * @return 住在该地址全部的人

         */

    public List getPersons()

         {

        return persons;

    }

/**

 * @param 住在该地址全部的人

         */

    public void setPersons(List persons)

         {

        this.persons = persons;

    }

}

Person的映射文件无须修改,修改后的Address映射文件如下所示:

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping

    PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="lee">

<class name="Address">

   <!--  映射标识属性 -->

        <id name="addressid">

            <generator class="identity"/>

        </id>

            <!--  映射普通属性 -->

        <property name="addressdetail"/>

<!--  映射关系持久化类 -->

                   <list name="persons">

                            <!--  key 元素的column值,应与person表中的外键列名相同-->

                            <key column="addressId"/>

                            <!--  list元素必须的索引列-->

                            <list-index column="addressorder"/>

                            <!--  映射到持久化类Person -->

                            <one-to-many class="Person"/>

                   </list>

         </class>

</hibernate-mapping>

此文件与之前的Address的映射非常相似,只是增加了list元素。list元素用来映射持久化类的集合属性。如果集合属性对于其他持久类,则为list元素增加one-to-manymany-to-many元素,用来映射持久化类。list是有序集合,所以list元素增加list-index元素,该元素映射集合里元素的索引值。

双向N – N关系映射

与双向的N – 1的关系映射相比,Person端做出简单修改。Person可对应多个Address。修改后后的Person POJO如下:

public class Person

{

         private int personid;

         private String name;

         private int age;

private List addresses = new ArrayList();

 

         //此处省略了personid,name,age三个属性的setter,getter方法,读者应该自行补齐

 

         /**

          * @return 此人的所有住址

          */

public List getAddresses()

{

        return addresses;

}

         /**

          * @return 此人的所有住址的集合

          */

public void setAddresses(List addresses)

{

        this.addresses = addresses;

    }        

}

AddressPOJO不需要任何修改。N – N的关系映射,需要无采用过主、外键的约束限定,必须使用连接表。因此,在配置文件中需要指定连接表的表名,并且,两个持久化类的连接表的表名必须一致。下面是两个POJO的映射文件

<hibernate-mapping package="lee">

         <!--  映射持久化类Person-->

<class name="Person">

           <!--  映射标识属性-->

        <id name="personid" >

            <generator class="identity"/>

        </id>

<!--  映射普通属性-->

        <property name="name"/>

        <property name="age"/>

 

<!--  映射关系持久化类,table属性的值必须与另一个持久化类的table属性值相同-->

        <list name="addresses" table="jointable">

                      <!--  key 元素的column值是连接表中的列名-->

<key column="personId"/>

<!--  list元素必须的索引列-->

                      <list-index column="addressOrder"/>

                      <!--  映射到持久化类Address -->

            <many-to-many column="addressId" class="Address"/>

        </list>

</class>

<!--  映射持久化类Address-->

<class name="Address">

<!--  key 元素的column值是连接中的列名-->

        <id name="addressid">

            <generator class="identity"/>

        </id>

<!--  映射普通属性-->

        <property name="addressdetail"/>

<!--  映射关系持久化类,table属性的值必须与另一个持久化类的table属性值相同-->

<!--  inverse属性为true表示关系由对方控制-->

        <list name="persons" inverse="true" table="jointable">

<!--  key 元素的column值是连接表中的列名-->

<key column="addressId"/>

<!--  list元素必须的索引列-->

                            <list-index column="personOrder"/>

<!--  映射到持久化类Person -->

            <many-to-many column="personId"  class="Person"/>

        </list>

    </class>

</hibernate-mapping>

本节提供三种关系映射的范例,但由于篇幅原因,其他的关系映射不可能一一列出,请读者参考以上三种关系映射自行理解。

10.2.6 HibernateHQL查询

数据查询指根据给定条件来选择记录。数据查询是持久化层必需的操作,也是DAO对象必需的基本功能。Hibernate配备功能强大的查询语言,这种查询语言被称为Hibernate Query LanguageHQL),也就是Hibernate查询语言。

HQL的关键字不区分大小写,但类名、属性名和各种类型的值对象都是区分大小写的。HQL的语法看起来与SQL非常接近,但不要忘记Hibernate是面向对象的,因此HQL被设计成面向对象。所以,使用HQL查询过程中,操作对象是类、实例或属性;而不是表、列、行等概念。同时,HQL完全可以理解如继承、多态和关联等面向对象的概念。

10.2.6.1  from语句

Hibernate最简单的查询语句如下:

from lee.Person;

该语句将查询到Person的全部实例。请注意:lee.Person不是表名,而是持久化类名,POJOXML映射文件的代码不再提供,请读者参考上面内容。from或的类名,还可以指定别名,如下所示:

from lee.Person as p

下面代码演示简单的from查询:

public class HqlQuery

{

    public static void main(String[] args)throws Exception

         {

                   //实例化HqlQuery对象

           HqlQuery mgr = new HqlQuery();

                   //调用HQL查询方法

           mgr.findPersons();

           mgr.findPersonsByHappenDate();

    }

         private void findPersons()

         {

                   //通过工具类HibernateUtilcurrentSession方法开始Session

                   Session sess = HibernateUtil.currentSession();

                   //开始事务

                  Transaction tx = sess.beginTransaction();

                   //使用SessioncreateQuery方法创建查询对象

           Query q = sess.createQuery("from Person p")

                   //执行Query对象的list方法,返回查询的全部实例

                   q.list();

                   //遍历结果集,返回全部的查询记录

           for (Iterator pit = pl.iterator() ; pit.hasNext(); )

           {

                Person p = ( Person )pit.next();

                            System.out.println(p.getName());

           }

                   //提交事务

                   tx.commit();

                   //调用工具类的关闭Session的方法。

                   HibernateUtil.closeSession();

         }

}

这段代码将可查询出Person的全部实例。正如前面所讲,所有的持久化操作都应在Session的管理下进行,QuerySession执行createQuery的返回值。createQuery的参数为HQL语句字符串。

Hibernate执行持久化操作总需要打开Session,因此,采用工具类HibernateUtil将打开Session和关闭Session封装起来。同时,由于Session不是线程安全对象,程序中采用ThreadLocal变量来保存Session,保证每次操作的Session不会相互影响。下面给出HibernateUtil的源代码:

public class HibernateUtil

{

public static final SessionFactory sessionFactory;

//静态初始化块,在执行该类静态方法、构造类实例之前执行

    static

         {

        try

                   {

              //采用默认的hibernate.cfg.xml来启动一个Configuration的实例

                            Configuration configuration=new Configuration().configure();

                            //Configuration的实例来创建一个SessionFactory实例

                sessionFactory = configuration.buildSessionFactory();

           }

                   catch (Throwable ex)

                   {

                //打印异常

                System.err.println("Initial SessionFactory creation failed." + ex);

                            //抛出新的业务异常

                throw new ExceptionInInitializerError(ex);

           }

    }

         //ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源, 因此不再需要对线程同步   

         public static final ThreadLocal session = new ThreadLocal();

         /**

          * 该方法用来获Session对象,如果该线程已有Session对象,则直接返回。        

 * 否则创建新的Session对象返回。

          * @ return 返回绑定到该线程Session对象

          */

    public static Session currentSession() throws HibernateException

         {

                   //直接从ThreadLocal里取得该线程的Session

           Session s = (Session) session.get();

           //如果该线程还没有Session, 则创建一个新的Session

           if (s == null)

                   {

                s = sessionFactory.openSession();

                //将获得的Session变量存储在ThreadLocal变量里

                session.set(s);

           }

           return s;

    }

         /**

          * 该方法用来关闭Session对象。 

          */

    public static void closeSession() throws HibernateException

{

         //获取绑定到该线程的Session对象。

           Session s = (Session) session.get();

                  //如果该Session存在,关闭它。

           if (s != null) s.close();

                   //同时将ThreadLocal值清空。

           session.set(null);

    }

}

10.2.6.2  where子句确定条件

where子句允许缩小返回的实例列表的范围。如果from后的持久化没有指定别名,可以使用属性名来直接引用属性。如果指定了别名,则必须使用完整的属性名,也就是采用别名限制属性名。看下面两行HQL语句:

from lee.Person where age > 6;

from lee.Person as p where p.age >6;

上面两行代码的效果完全相同。JDBC里的PreparedStatement支持预编译,然后接受参数,通过这种方式,可以使用相同的Statement执行不同的SQL操作,提高执行的效率。Query也支持预编译的方式,预编译的方式对HQL中无法确定的参数采用占位符,HQL中的占位符可以指定名字,采用冒号(英文:)后加占位符名的方式来指定占位符名字。如下所示:

from lee.Person as p where p.age >:age;

Query提供系列的setXXX方法,用来为不确定的参数传入实际的值。setXXX方法提供两个版本,一种支持按参数索引传值,一种支持按参数名传值。如下所示:

q      setXXX(int parameterIndex , Object parameterValue)

q      setXXX(String parameterName , Object parameterValue)

给出带where字句,HQL带占位符的查询示例:

public class HqlQuery

{

    public static void main(String[] args)throws Exception

         {

                   //实例化HqlQuery对象

           HqlQuery mgr = new HqlQuery();

                   //调用HQL查询方法

           mgr.findPersons();

           mgr.findPersonsByHappenDate();

    }

         private void findPersons()

         {

                   //通过工具类HibernateUtilcurrentSession方法开始Session

                   Session sess = HibernateUtil.currentSession();

                   //开始事务

                  Transaction tx = sess.beginTransaction();

                   //使用SessioncreateQuery方法创建查询对象

           Query q = sess.createQuery("from Person as p where p.age > :age ")

                   //执行Query对象的list方法,返回查询的全部实例

                   q.setInt(“age”,20);

                   q.list();

                   //遍历结果集,返回全部的查询记录

           for (Iterator pit = pl.iterator() ; pit.hasNext(); )

           {

                Person p = ( Person )pit.next();

                            System.out.println(p.getName());

           }

                   //提交事务

                   tx.commit();

                   //调用工具类的关闭Session的方法。

                   HibernateUtil.closeSession();

         }

}

10.2.6.3  select子句

select子句,用于选择将哪些对象和属性返回到结果集中,考虑如下情况:

//选所有人的儿子的另一种表达

select s

from Person as p

inner join p.son as s

该语句将选出所有人的儿子。事实上,可以用如下更简单的语句来表示

//选所有人的儿子的另一种表达

select p.son from Person p

select语句可返回数据类型为任何类型的值,包括返回类型为组件的属性,甚至返回组件属性的属性值。如下所显示:

//选出人名字以Ja开头的人

select p.name from Person p

where p.name like 'Ja%'

//选择所有人的儿子的名字

//其中son属性为组件属性

select p.son.name from Person as p

select语句可以返回多个对象和(或)属性,直接存放在List对象中

//将选择的人及其儿子放入一个集合

select new list(p,s)

from Person as p

join p.son as s

也可能直接返回一个类型安全的Java对象,

//以选择的人及其儿子构造Family实例

//前提是Family类有构造函数Family(Person p , Son s)

select new Family(p,s)

from Person as p

join p.son as s

10.2.7 HibernateCriteria查询

Hibernate带一种更对象化的查询方式。这种查询方式更直观,而且可扩展。Criteria查询也称为条件查询,条件查询使用org.hibernate.Criteria接口表示持久化类的一个查询,Session是产生Criteria实例的工厂,创建Criteria时,需以持久化类的class作为参数。如下所示:

//Session为工厂,创建持久化类Person的条件查询(Criteria)对象

Session.createCriteria(“Person.class”)

Criteria使用组合器模式来增加查询条件,每条查询条件以org.hibernate.criterion.Criterion接口的一个实例表示,org.hibernate.criterion.Restrictions类则作为Criterion实例的静态工厂类,该类提供如下系列静态方法来产生查询条件

q      in(String propertyName, Collection values):属性值在集合值里

q      like(String propertyName, Object value):属性值与目标值满足模式匹配

q      lt(String propertyName, Object value):属性值小于目标值

q      le(String propertyName, Object value):属性值小于等于目标值

q      gt(String propertyName, Object value):属性值大于目标值

q      ge(String propertyName, Object value):属性值大于等于目标值

看如下查询:

//Session为工厂,创建Person类条件查询对象cri

Criteria cri = Session.createCriteria(Person.class);

//为条件查询增加查询:nameFritz开始

cri.add(Restrictions.like("name", "Fritz%"));

//条件查询增加条件:age大于6

cri.add(Restrictions.gt("age", 6));

//查询出条件查询的所有实例,以List返回。

List personList = cri.list();

上文的查询将查询出nameFritz开头,age大于6的全部实例。上面的代码比HQL的查询更加直观,更加对象化。

 

  sunsj@51cto.com  TEL:(010)68476636-8007)

10.3 整合Hibernate

HibernateSpring 提供很多IoC的特性的支持,方便处理大部分典型的Hibernate整合的问题。所有的这些,都遵守Spring通用的事务和DAO异常体系。Spring整合Hibernatejiang,使持久层的访问更加容易,Spring管理Hibernate持久层有如下优势:

q      通用的资源管理:SpringApplicationContext能管理SessionFactory,使得配置值很容易被管理和修改。无须使用Hibernate的配置文件。

q      有效的Session管理:Spring提供了有效,简单和安全的Hibernate Session处理。

q      方便的事务管理:Hibernate的事务管理处理不好,会限制Hibernate的表现,而Spring的声明式事务管理粒度是方法级。

q      异常包装。Spring能够包装Hibernate异常,把它们从checked exception变为runtime exception。开发者可选择在恰当的层处理数据库的不可恢复异常,从而避免繁琐的catch/throw以及异常声明。

 

10.4管理SessionFactory

SessionFactory:单个数据库映射关系编译后的内存镜像。大部分情况下,一个J2EE应用对应一个数据库。Spring通过ApplicationContext管理SessionFactory,无须采用单独Hiberate应用必需的hibernate.cfg.xml文件。

SessionFactory与数据库的连接,都由Spring的配置管理。实际的J2EE应用,通常使用数据源,数据源会采用依赖注入的方式,传给HibernateSessionFactory。具体配置如下所示:

<?xml version="1.0" encoding="gb2312"?>

<!--  Spring配置文件的DTD定义-->

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

    "http://www.springframework.org/dtd/spring-beans.dtd">

<!--  Spring配置文件的根元素是beans-->

<beans>

         <!--定义数据源, beanIDdataSource-->

         <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

                   <!--  指定数据库驱动-->

           <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>

                   <!--  指定连接数据库的URL-->                 

           <property name="url"><value>jdbc:mysql://wonder:3306/j2ee</value></property>

                   <!--  root为数据库的用户名-->

           <property name="username"><value>root</value></property>

                   <!--  pass为数据库密码-->

           <property name="password"><value>pass</value></property>

    </bean>

         <!--定义HibernateSessionFactory-->

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

           <!--  依赖注入数据源,注入正是上文定义的dataSource>

           <property name="dataSource"><ref local="dataSource"/></property>

                   <!--  mappingResouces属性用来列出全部映射文件>

           <property name="mappingResources">

                <list>

                     <!--以下用来列出所有的PO映射文件-->

                                     <value>lee/MyTest.hbm.xml</value>

                </list>

           </property>

          <!--定义HibernateSessionFactory的属性 -->

           <property name="hibernateProperties">

                    <props>

                                     <!--  指定Hibernate的连接方言-->

                                     <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>

                                     <!--  不同数据库连接,启动时选择create,update,create-drop-->

                         <prop key="hibernate.hbm2ddl.auto">update</prop>

                    </props>

        </property>

    </bean>

</beans>

SessionFactoryApplicationContext管理,会随着应用启动时候自动加载。SessionFactory可以被处于ApplicaionContext管理的任意一个bean引用,比如DAOHibernate的数据库访问需要在Session管理下,而SessionFactorySession的工厂。Spring采用依赖注入为DAO对象注入SessionFactory的引用。

Spring更提供Hibernate的简化访问方式,Spring采用模板设计模式,提供Hibernate访问与其他持久层访问的一致性。如果需要使用容器管理的数据源,则无须提供数据驱动等信息,只需要提供数据源的JNDI即可。对上文的SessionFactory无须任何修改,只需将dataSource的配置替换成JNDI数据源,将原有的dataSource Bean替换成如下所示:

<!--  此处配置JNDI数据源-->

<bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">

         <property name="jndiName">

                   <!--  指定数据源的JNDI-->

                   <value>java:comp/env/jdbc/myds</value>

         </property>

</bean>

 

10.5  SpringHibernate的简化

Hibernate的持久层访问必须按如下步骤进行:

1)创建Configuration实例

2)创建SessionFactory实例

3)创建Session实例

4)打开事务

5)开始持久化访问

6)提交事务

7)如果遇到异常,回滚事务

8)关闭Session

HQL查询一节,已经采用HibernateUtils工具类封装部分过程。但依然不够简洁,需要通过代码显式地打开Session,显式地开始事务,然后关闭事务,关闭Session。而Hibernate提供更简单的方式操作持久层,无须显式地打开Session,无须在代码中执行任何的事务操作语句。

Hibernate的简化,还基于Spring对异常处理的简化。底层数据库异常几乎都不可恢复,强制处理底层数据库几乎没有任何意义,但传统JDBC数据库访问的异常都是checked异常,必须使用try...catch块处理。

Spring包装了Hibernate异常,转换到DataAccessException继承树内,所有DataAccessException全部是runtime异常,并不强制捕捉。归纳起来,SpringHibernate的简化主要有如下几个方面:

q      基于依赖注入的SessionFactory管理机制。SessionFactory是执行持久化操作的核心组件。传统Hibernate应用中,SessionFactory必须手动创建;通过依赖注入,代码无须关心SessionFactorySessionFactory的创建,维护由BeanFactory负责管理。

q      更优秀的Session管理机制。Spring提供“每事务一次Session”的机制,该机制能大大提高系统性能,而且SpringSession的管理是透明的,无须在代码中操作Session

q      统一的事务管理。无论是编程式事务,还是声明式事务,Spring都提供一致的编程模型,无须繁琐的开始事务,显式提交、回滚。如果使用声明式事务管理,事务管理逻辑与代码分离,事务可在全局事务和局部事务之间切换。

q      统一的异常处理机制。不再强制开发者在持久层捕捉异常,持久层异常被包装成DataAccessException异常的子类,开发者可以自己决定在合适的层处理异常,将底层数据库异常包装成业务异常。

q      HibernateTemplate支持类。HibernateTempate能完成大量Hibernate持久层操作,这些操作大多只需一行代码,非常简洁。

 

10.6 使用HbernateTemplate

HibernateTemplate提供持久层访问模板化,使用HibernateTemplate无须实现特定接口,它只需要提供一个SessionFactory的引用,就可执行持久化操作。SessionFactoyr对象可通过构造参数传入,或通过设值方式传入。如下:

//获取Spring上下文

ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");

//通过上下文获得SessionFactory

SessionFactory sessionFactory = (SessionFactory) ctx.getBean(“sessionFactory”);

然后创建HibernateTemplate实例。HibernateTemplate提供如下三个构造函数

q      HibernateTemplate()

q      HibernateTemplate(org.hibernate.SessionFactory sessionFactory)

q      HibernateTemplate(org.hibernate.SessionFactory sessionFactory, boolean allowCreate)

第一个构造函数,构造一个默认的HibernateTemplate实例,因此,使用HibernateTemplate实例之前,还必须使用方法setSessionFactory(SessionFactory sessionFactory)来为HibernateTemplate传入SessionFactory的引用。

第二个构造函数,在构造时已经传入SessionFactory引用。

第三个构造函数,其boolean型参数表明:如果当前线程已经存在一个非事务性的Session,是否直接返回此非事务性的Session

对于在Web应用,通常启动时自动加载ApplicationContextSessionFactoryDAO对象都处在Spring上下文管理下,因此无须在代码中显式设置,可采用依赖注入解耦SessionFactoryDAO,依赖关系通过配置文件来设置,如下所示:

<?xml version="1.0" encoding="gb2312"?>

<!--  Spring配置文件的DTD定义-->

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

    "http://www.springframework.org/dtd/spring-beans.dtd">

<!--  Spring配置文件的根元素是beans-->

<beans>

         <!--定义数据源,beanIDdataSource-->

         <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

                   <!--  指定数据库驱动-->

           <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>

                   <!--  指定连接数据库的URL-->                 

           <property name="url"><value>jdbc:mysql://wonder:3306/j2ee</value></property>

                   <!--  root为数据库的用户名-->

           <property name="username"><value>root</value></property>

                   <!--  pass为数据库密码-->

           <property name="password"><value>pass</value></property>

    </bean>

         <!--定义HibernateSessionFactory-->

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

           <!--  依赖注入数据源,注入正是上文定义的dataSource>

           <property name="dataSource"><ref local="dataSource"/></property>

                   <!--  mappingResouces属性用来列出全部映射文件>

           <property name="mappingResources">

                <list>

                     <!--以下用来列出所有的PO映射文件-->

                                     <value>lee/Person.hbm.xml</value>

                </list>

           </property>

          <!--定义HibernateSessionFactory的属性 -->

           <property name="hibernateProperties">

                    <props>

                                     <!--  指定Hibernate的连接方言-->

                                     <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>

                                     <!--  不同数据库连接,启动时选择create,update,create-drop-->

                         <prop key="hibernate.hbm2ddl.auto">update</prop>

                    </props>

        </property>

</bean>

<!--  配置Person持久化类的DAO bean-->

<bean id="personDao" class="lee.PersonDaoImpl">

           <!--  采用依赖注入来传入SessionFactory的引用>

           <property name="sessionFactory"><ref local="sessionFactory"/></property>

</bean>

</beans>

DAO实现类中,可采用更简单的方式来取得HibernateTemplate的实例。代码如下:

public class PersnDAOImpl implements PersonDAO

{

    //以私有的成员变量来保存SessionFactory

private SessionFactory sessionFactory;

         //设值注入SessionFactory必需的setter方法

    public void setSessionFactory(SessionFactory sessionFactory)

{

           this.sessionFactory = sessionFactory;

    }

 

    public List loadPersonByName(final String name)

{

           HibernateTemplate hibernateTemplate =

                    new HibernateTemplate(this.sessionFactory);

                   //此处采用HibernateTemplate完成数据库访问

    }

}

10.6.1 HibernateTemplate的常规用法

HibernateTemplate提供非常多的常用方法来完成基本的操作,比如通常的增加、删除、修改、查询等操作,Spring 2.0更增加对命名SQL查询的支持,也增加对分页的支持。大部分情况下,使用Hibernate的常规用法,就可完成大多数DAO对象的CRUD操作。下面是HibernateTemplate的常用方法简介:

q      void delete(Object entity):删除指定持久化实例

q      deleteAll(Collection entities):删除集合内全部持久化类实例

q      find(String queryString):根据HQL查询字符串来返回实例集合

q      findByNamedQuery(String queryName):根据命名查询返回实例集合

q      get(Class entityClass, Serializable id):根据主键加载特定持久化类的实例

q      save(Object entity):保存新的实例

q      saveOrUpdate(Object entity):根据实例状态,选择保存或者更新

q      update(Object entity):更新实例的状态,要求entity是持久状态

q      setMaxResults(int maxResults):设置分页的大小

下面是一个完整DAO类的源代码:

public class PersonDAOHibernate implements PersonDAO

{

    //采用log4j来完成调试时的日志功能

private static Log log = LogFactory.getLog(NewsDAOHibernate.class);

    //以私有的成员变量来保存SessionFactory

private SessionFactory sessionFactory;

//以私有变量的方式保存HibernateTemplate

private HibernateTemplate hibernateTemplate = null;

         //设值注入SessionFactory必需的setter方法

    public void setSessionFactory(SessionFactory sessionFactory)

{

           this.sessionFactory = sessionFactory;

    }

         //初始化本DAO所需的HibernateTemplate

public HIbernateTemplate getHibernateTemplate()

{

         //首先,检查原来的hibernateTemplate实例是否还存在

         if ( hibernateTemplate == null)

         {

                   //如果不存在,新建一个HibernateTemplate实例

                   hibernateTemplate = new HibernateTemplate(sessionFactory);

         }

         return hibernateTemplate;

}

         //返回全部的人的实例

    public List getPersons()

         {       

                   //通过HibernateTemplatefind方法返回Person的全部实例

           return getHibernateTemplate().find("from Person");

    }

         /**

          * 根据主键返回特定实例

          * @ return 特定主键对应的Person实例

          * @ param 主键值

    public News getNews(int personid)

         {

           return (Person)getHibernateTemplate().get(Person.class, new Integer(personid));

    }

         /**

          * @ person 需要保存的Person实例

          */

    public void savePerson(Person person)

         {                

                   getHibernateTemplate().saveOrUpdate(person);

    }

         /**

          * @ param personid 需要删除Person实例的主键

          * /

    public void removePerson(int personid)

         {

                   //先加载特定实例

           Object p = getHibernateTemplate().load(Person.class, new Integer(personid));

                   //删除特定实例

           getHibernateTemplate().delete(p);

    }

}

10.6.2 Hibernate的复杂用法HibernateCallback

HibernateTemplate还提供一种更加灵活的方式来操作数据库,通过这种方式可以完全使用Hibernate的操作方式。HibernateTemplate的灵活访问方式是通过如下两个方法完成:

q      Object execute(HibernateCallback action)

q      List execute(HibernateCallback action)

这两个方法都需要一个HibernateCallback的实例,HibernateCallback实例可在任何有效的Hibernate数据访问中使用。程序开发者通过HibernateCallback,可以完全使用Hibernate灵活的方式来访问数据库,解决Spring封装Hibernate后灵活性不足的缺陷。HibernateCallback是一个接口,该接口只有一个方法doInHibernate(org.hibernate.Session session),该方法只有一个参数Session

通常,程序中采用实现HibernateCallback的匿名内部类来获取HibernateCallback的实例,方法doInHibernate的方法体就是Spring执行的持久化操作。具体代码如下:

public class PersonDaoImpl implements PersonDao

{

    //私有实例变量保存SessionFactory

         private SessionFactory sessionFactory;

         //依赖注入必须的setter方法

    public void setSessionFactory(SessionFactory sessionFactory)

         {

           this.sessionFactory = sessionFactory;

    }

         /**

          * 通过人名查找所有匹配该名的Person实例

          * @param name 匹配的人名

          * @return 匹配该任命的全部Person集合

          */

    public List findPersonsByName(final String name)

         {

           //创建HibernateTemplate实例

                   HibernateTemplate hibernateTemplate =

                         new HibernateTemplate(this.sessionFactory);

           //返回HibernateTemplateexecute的结果

                   return (List) hibernateTemplate.execute(

                //创建匿名内部类

                   new HibernateCallback()

                   {

              public Object doInHibernate(Session session) throws HibernateException

                            {

                   //使用条件查询的方法返回

                                     List result = session.createCriteria(Person.class)

                                                                     .add(Restrictions.like(“name”, name+”%”)

                                                                           .list();

                                    return result;

                     }

                });

    }

}

注意:方法doInHibernate方法内可以访问Session,该Session对象是绑定到该线程的Session实例。该方法内的持久层操作,与不使用Spring时的持久层操作完全相同。这保证对于复杂的持久层访问,依然可以使用Hibernate的访问方式。

 

10.7  HibernateDAO实现

DAO对象是模块化的数据库访问组件,DAO对象通常包括:对持久化类的基本CRUD操作(插入、查询、更新、删除)操作。SpringHibernateDAO实现提供了良好的支持。主要有如下两种方式的DAO实现:

q      继承HibernateDaoSupport的实现DAO

q      基于Hibernate3.0实现DAO

不管采用哪一种实现,这种DAO对象都极好地融合到SpringApplicationContext中,遵循依赖注入模式,提高解耦。

10.7.1 继承HibernateDaoSupport实现DAO

SpringHibernateDAO提供工具类:HibernateDaoSupport。该类主要提供如下两个方法,方便DAO的实现:

q      public final HibernateTemplate getHibernateTemplate()

q      public final void setSessionFactory(SessionFactory sessionFactory)

其中,setSessionFactory方法用来接收SpringApplicationContext的依赖注入,可接收配置在SpringSessionFactory实例,getHibernateTemplate方法则用来根据刚才的SessionFactory产生Session,最后生成HibernateTemplate来完成数据库访问。

典型的继承HibernateDaoSupportDAO实现的代码如下:

public class PersonDAOHibernate extends HibernateDaoSupport implements PersonDAO

{

    //采用log4j来完成调试时的日志功能

private static Log log = LogFactory.getLog(NewsDAOHibernate.class);

         //返回全部的人的实例

    public List getPersons()

         {       

                   //通过HibernateTemplatefind方法返回Person的全部实例

           return getHibernateTemplate().find("from Person");

    }

         /**

          * 根据主键返回特定实例

          * @ return 特定主键对应的Person实例

          * @ param 主键值

    public News getPerson(int personid)

         {

           return (Person)getHibernateTemplate().get(Person.class, new Integer(personid));

    }

         /**

          * @ person 需要保存的Person实例

          */

    public void savePerson(Person person)

         {                

                   getHibernateTemplate().saveOrUpdate(person);

    }

         /**

          * @ param personid 需要删除Person实例的主键

          * /

    public void removePerson(int personid)

         {

                   //先加载特定实例

           Object p = getHibernateTemplate().load(Person.class, new Integer(personid));

                   //删除特定实例

           getHibernateTemplate().delete(p);

    }

}

可以与前面的PersonDAOHibernate对比,会发现代码量大大减少。事实上,DAO的实现依然借助于HibernateTemplate的模板访问方式,只是,HibernateDaoSupport将依赖注入SessionFactory的工作已经完成,获取HibernateTemplate的工作也已完成。该DAO的配置必须依赖于SessionFactory,具体的配置如下:

<?xml version="1.0" encoding="gb2312"?>

<!--  Spring配置文件的DTD定义-->

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

    "http://www.springframework.org/dtd/spring-beans.dtd">

<!--  Spring配置文件的根元素是beans-->

<beans>

         <!--定义数据源,beanIDdataSource-->

         <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

                   <!--  指定数据库驱动-->

           <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>

                  <!--  指定连接数据库的URL-->                 

           <property name="url"><value>jdbc:mysql://wonder:3306/j2ee</value></property>

                   <!--  root为数据库的用户名-->

           <property name="username"><value>root</value></property>

                   <!--  pass为数据库密码-->

           <property name="password"><value>pass</value></property>

    </bean>

         <!--定义HibernateSessionFactory-->

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

           <!--  依赖注入数据源,注入正是上文定义的dataSource>

           <property name="dataSource"><ref local="dataSource"/></property>

                   <!--  mappingResouces属性用来列出全部映射文件>

           <property name="mappingResources">

                <list>

                     <!--以下用来列出所有的PO映射文件-->

                                     <value>lee/Person.hbm.xml</value>

                </list>

           </property>

          <!--定义HibernateSessionFactory的属性 -->

           <property name="hibernateProperties">

                    <props>

                                     <!--  指定Hibernate的连接方言-->

                                     <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>

                                     <!--  不同数据库连接,启动时选择create,update,create-drop-->

                         <prop key="hibernate.hbm2ddl.auto">update</prop>

                    </props>

        </property>

</bean>

<!--  配置Person持久化类的DAO bean-->

<bean id="personDAO" class="lee. PersonDAOHibernate">

           <!--  采用依赖注入来传入SessionFactory的引用>

           <property name="sessionFactory"><ref local="sessionFactory"/></property>

</bean>

</beans>

程序中可以通过显式的编码来获得personDAO bean,然后执行CRUD操作。也可通过依赖注入,将personDAO的实例注入其他bean属性,再执行CRUD操作。

在继承HibrnateDaoSupportDAO实现里,Hibernate Session的管理完全不需要Hibernate代码打开,而由Spring来管理。Spring会根据实际的操作,采用“每次事务打开一次session”的策略,自动提高数据库访问的性能。

10.7.1基于Hibernate3.0实现DAO

Hibernate3.0.1提供了一种新的技术:"contextual Sessions"。通过此机制,Hibernate可以自己管理Session,从而保证每次事务一个Session。该机制类似于Spring的每次事务一次Hibernate Session的同步策略。

Hibernate"contextual Sessions",是通过SessionFactorygetCurrentSession()方法实现的,该方法会返回由当前JTA事务保持的Session,如果当前JTA事务关联的Session不存在,系统打开一次新的Session,并关联到当前的JTA事务,如果当前JTA事务关联的Session已经存在,则直接返回该Session即可。执行该操作的前提是Hibernate处于事务管理下。通常,SpringHibernate提供事务管理。

基于Hibernate 3.0DAO的实现,只需Spring注入SessionFactory,然后由Hibernate自己管理Session。即:通过SessionFactorygetCurrentSession方法,返回当前事务关联的Session。持久化操作在Session管理如常进行。完整的基于Hibernate3.0DAO实现的代码如下

public class PersonDaoImpl implements PersonDao

{

         //私有成员变量保存SessionFactory

private SessionFactory sessionFactory;

/**

 * 依赖注入SessionFactory必须的setter方法

 * @ sessionFactory

 */

         public void setSessionFactory(SessionFactory sessionFactory)

         {

                   this.sessionFactory = sessionFactory;

    }

        

        /**

          * 根据名字查找Person的实例。

          * @param name需要查找的 Person的名字

          * @return 匹配名字的Person实例的集合

          */

    public Collection findPersonsByName(String name)

         {

           return this.sessionFactory.getCurrentSession()

                                                            .createQuery("from lee.Person p where p.name=?")

                                                            .setParameter(0, name)

                                            .list();

    }

         /**

          * 根据Person id加再Person实例。

          * @param id需要load Person实例

          * @return 特定idPerson实例。

          */

    public Person findPersonsById(int id)

         {

           return (Person)this.sessionFactory.getCurrentSession()

                   .load(Person.class,new Integer(id))

    }

}

DAO的数据库访问方式,类似于传统的Hibernate的访问,区别在于获取Session的方式不同。传统的HibernateSessionFactory,采用工具类HibernateUtils来保存成静态成员变量,每次采用HibernateUtils打开Session

传统的Session访问方式,很容易造成“每次数据库操作打开一次Session”的方式,该方式效率低下,也是Hibernate不推荐采用的策略。Hibernate推荐采用“每次事务打开一次Session”。基于该原因,Hibernate3.0.1提供"contextual Sessions”的技术,最终达到与继承HibernateDaoSupportDAO实现相同的目的。

同样,此DAO bean也需要配置在Spring的上下文中,需要依赖于SessionFactory beanSessionFactory beanSpring在运行时动态为DAO bean注入。具体的配置文件,读者可参考上文的配置文件写出。

 

10.8 事务管理

Hibernate建议所有的数据库访问都应放在事务内进行,即使只进行只读操作。事务又应该尽可能地短,长事务会导致系统长时间无法释放,因而降低系统并发的负载。Spring同时支持编程式事务和声明式事务。尽量考虑使用声明式事务,声明式事务管理可分离业务逻辑和事务管理逻辑,具备良好的适应性。

10.8.1 编程式的事务管理

编程式事务管理建议使用TransactionTemplate来完成事务操作,TransactionTemplate采用Callback避免让开发者重复书写打开事务,提交事务,回滚事务等代码,同时,TransactionTemplate无须书写大量的try...catch块。

关于使用TransactionTemplate的示例,请参阅7.4.1使用TransactionTemplate。需要是使用Spring的事务管理首先需配置一个PlatformTransactionManager bean。配置该bean时,应根据事务环境选择合适的实现类。下面示例是Hibernate局部事务管理的配置示例:

<?xml version="1.0" encoding="gb2312"?>

<!--  Spring配置文件的DTD定义-->

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

    "http://www.springframework.org/dtd/spring-beans.dtd">

<!--  Spring配置文件的根元素是beans-->

<beans>

         <!--定义数据源,beanIDdataSource-->

         <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

                   <!--  指定数据库驱动-->

           <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>

                   <!--  指定连接数据库的URL-->                 

           <property name="url"><value>jdbc:mysql://wonder:3306/j2ee</value></property>

                   <!--  root为数据库的用户名-->

           <property name="username"><value>root</value></property>

                   <!--  pass为数据库密码-->

           <property name="password"><value>pass</value></property>

    </bean>

         <!--定义HibernateSessionFactory-->

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

           <!--  依赖注入数据源,注入正是上文定义的dataSource>

           <property name="dataSource"><ref local="dataSource"/></property>

                   <!--  mappingResouces属性用来列出全部映射文件>

           <property name="mappingResources">

                <list>

                     <!--以下用来列出所有的PO映射文件-->

                                     <value>lee/MyTest.hbm.xml</value>

                </list>

           </property>

          <!--定义HibernateSessionFactory的属性 -->

           <property name="hibernateProperties">

                    <props>

                                     <!--  指定Hibernate的连接方言-->

                                     <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>

                                     <!--  不同数据库连接,启动时选择create,update,create-drop-->

                         <prop key="hibernate.hbm2ddl.auto">update</prop>

                    </props>

        </property>

</bean>

<!--  配置Hibernate的事务管理器 -->

<!--  使用HibernateTransactionManager类,该类是PlatformTransactionManager接口

  针对采用Hibernate持久化连接的特定实现。-->

<bean id="transactionManager"

     class="org.springframework.orm.hibernate3.HibernateTransactionManager">

<!--  HibernateTransactionManager  bean需要依赖注入一个SessionFactory bean的引用-->

       <property name="sessionFactory"><ref local="sessionFactory"/></property>

</bean>

</beans>

下面是采用TransactionTemplateHibernateTemplate的事务操作代码:

public class TransactionTest

{

    public static void main(String[] args)

    {

                   //因为并未在Web应用中测试,故需要手动创建Spring的上下文

           final ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");

                   //获得Spring上下文的事务管理器

         PlatformTransactionManager  transactionManager=

                            (PlatformTransactionManager)ctx.getBean("transactionManager");

         final SessionFactory sessionFactory  = (SessionFactory)ctx.getBean("sessionFactory ");

                            (PlatformTransactionManager)ctx.getBean("transactionManager");

                   //以事务管理器实例为参数,创建TransactionTemplate对象

                   TransactionTemplate tt = new TransactionTemplate(transactionManager);

                   //设置TransactionTemplate的事务传播属性

                   transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

                   //执行TransactionTemplateexecute方法,该方法需要TransactionCallback实例。

                   tt.execute(new TransactionCallbackWithoutResult()

                            //采用TransactionCallbackWithoutResult匿名内部类的形式执行

                            {

                                     protected void doInTransactionWithoutResult(TransactionStatus ts)

                                     {

                                               try

                                               {

                                                        SessionFactory实例为参数创建HibernateTemplate

                                                HibernateTemplate hibernateTemplate  =

                                                                 new HibernateTemplate(this.sessionFactory);

                                                        Person p1 = new Person(“Jack”);

                                                        //保存第一个实例

                                                        hibernateTemplate.save(p1);

                                                        //让下面的数据库操抛出异常即可看出事务效果。前面的操作也不会生效

                                                        Person p2 = new Person(“Black”);

                                                        //保存第二个实例,可将Personname属性设为标识属性,并引起主键重复的异

//常,可看出前一条记录也不会进数据库

                                                        hibernateTemplate.save(p2);

 

                                               }

                                               catch (Exception e)

                                               {

                                                        ts.setRollbackOnly();

                                               }

                                     }

                            });

         }

}

10.8.2声明式的事务管理

通常建议采用声明式事务管理。

交叉参考:

声明式事务的配置,请参考7.5 声明式事务的介绍。

关于声明式事务管理的配置方式,通常有如下三种:

q      使用TransactionProxyFactoryBean为目标bean生成事务代理的配置。此方式是最传统,配置文件最臃肿、难以阅读的方式。

q      采用bean继承的事务代理配置方式,比较简洁,但依然是增量式配置。

q      使用BeanNameAutoProxyCreator,根据bean name自动生成事务代理的方式。

建议采用第三种配置方式,详细的配置代码如下:

<?xml version="1.0" encoding="gb2312"?>

<!--  Spring配置文件的DTD定义-->

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"

    "http://www.springframework.org/dtd/spring-beans.dtd">

<!--  Spring配置文件的根元素是beans-->

<beans>

         <!--定义数据源,beanIDdataSource-->

         <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

                   <!--  指定数据库驱动-->

           <property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property>

                   <!--  指定连接数据库的URL-->                 

           <property name="url"><value>jdbc:mysql://wonder:3306/j2ee</value></property>

                   <!--  root为数据库的用户名-->

           <property name="username"><value>root</value></property>

                   <!--  pass为数据库密码-->

           <property name="password"><value>pass</value></property>

    </bean>

         <!--定义HibernateSessionFactory-->

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

           <!--  依赖注入数据源,注入上面定义的dataSource>

           <property name="dataSource"><ref local="dataSource"/></property>

                   <!--  mappingResouces属性用来列出全部映射文件>

           <property name="mappingResources">

                <list>

                     <!--以下用来列出所有的PO映射文件-->

                                     <value>lee/Person.hbm.xml</value>

                </list>

           </property>

          <!--定义HibernateSessionFactory的属性 -->

           <property name="hibernateProperties">

                    <props>

                                     <!--  指定Hibernate的连接方言-->

                                     <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>

                                     <!--  不同数据库连接,启动时选择create,update,create-drop-->

                         <prop key="hibernate.hbm2ddl.auto">update</prop>

                    </props>

           </property>

    </bean>

<!--  配置Hibernate的事务管理器 -->

<!--  使用HibernateTransactionManager该类是PlatformTransactionManager接口。

       针对采用Hibernate持久化连接的特定实现。-->

<bean id="transactionManager"

    class="org.springframework.orm.hibernate3.HibernateTransactionManager">

<!--  HibernateTransactionManager  bean需要依赖注入一个SessionFactory bean的引用-->

       <property name="sessionFactory"><ref local="sessionFactory"/></property>

</bean>

<!--  定义事务拦截器bean>

         <bean id="transactionInterceptor"

        class="org.springframework.transaction.interceptor.TransactionInterceptor">

                  <!--  事务拦截器bean需要依赖注入一个事务管理器>

         <property name="transactionManager" ref="transactionManager"/>

                   <property name="transactionAttributes">

                            <!--  下面定义事务传播属性-->

                            <props>

                                     <prop key="insert*">PROPAGATION_REQUIRED </prop>

                                     <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

                                     <prop key="*">PROPAGATION_REQUIRED</prop>

                            </props>

                   </property>

         </bean>

<!—定义一个BeanPostProcessor bean

Spring提供BeanPostProcessor的实现类BeanNameAutoProxyCreator-->

         <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

                   <!--  指定对满足哪些bean namebean自动生成业务代理 -->

                   <property name="beanNames">

                            <!--  此处还可以列出更多的需要生成事务代理的目标bean-->

                            <ref local=" personDAO "/>

                            <!--  有一个需要生成事务代理的目标bean, 此处就增加一行-->

                   </property>

                   <!--  下面定义BeanNameAutoProxyCreator所需要的事务拦截器-->

                   <property name="interceptorNames">

                            <list>

                                     <value>transactionInterceptor</value>

                  <!-- 此处还增加其他新的Interceptor -->

                            </list>

                   </property>

         </bean>

<bean id="personDAO" class="lee.PersonDAOImpl" />

<!—此处还可增加更多bean定义-->               

</beans>

TranscationInterceptor是一个事务拦截器bean,需要传入一个TransactionManager的引用。配置中使用Spring依赖注入该属性,事务拦截器的事务属性通过transactionAttributes来指定,该属性有props子元素,配置文件中定义了三个事务传播规则:

所有以insert开始的方法,采用PROPAGATION_REQUIRED的事务传播规则。程序抛出MyException异常及其子异常时,自动回滚事务。所有以find开头的方法,采用PROPAGATION_REQUIRED事务传播规则,并且只读。其他方法,则采用PROPAGATION_REQUIRED的事务传播规则。

BeanNameAutoProxyCreator是个根据bean名生成自动代理的代理创建器,该bean通常需要接受两个参数。第一个是beanNames属性,该属性用来设置哪些bean需要自动生成代理。另一个属性是interceptorNames,该属性则指定事务拦截器,自动创建事务代理时,系统会根据这些事务拦截器的属性来生成对应的事务代理。

10.8.3 事务策略的思考

考虑采用Spring的事务管理机制。Spring的事务管理都是通过PlatformTransactionManager完成。在Hibernate应用中,PlatformTransactionManager可能是Hibernat SessionFactory,也可能是 JtaTransactionManager。前者是采用局部事务管理的实现,后者是采用基于JTA支持的全局事务管理的实现。因此,即使应用运行于支持JTA事务的应用服务器环境,考虑使用Spring的声明式事务管理也是个不错的选择。

 

 

10.9  小结

本章简单介绍了ORM相关知识。详细介绍了Hibernate的基本使用、简单的配置,以及关系映射配置。也详细介绍了Hibernate的两种常用的查询方法:HQL查询和条件查询。重点介绍SpringHibernate的简化和支持,以及Spring整合Hibernate的各种方式。最后,给出了HibernateSpring中的两种事务管理示例程序及相关说明。

上来自:http://book.csdn.net/bookfiles/126/index.html

Hibernate是目前最流行的开源对象关系映射(ORM)框架。Hibernate采用低侵入式的设计,也即完全采用普通的Java对象(POJO),而不必继承Hibernate的某个基类,或实现Hibernate的某个接口。Hibernate是面向对象的程序设计语言和关系数据库之间的桥梁,Hibernate允许程序开发者采用面向对象的方式来操作关系数据库。

 

10.1 ORM介绍

ORM的全称是Object/Relation Mapping,即对象/关系映射。ORM也可理解是一种规范,具体的ORM框架可作为应用程序和数据库的桥梁。目前ORM的产品非常多,比如Apache组织下的OJBOracleTopLinkJDO等等。

10.1.1 什么是ORM

ORM并不是一种具体的产品,而是一类框架的总称,它概述了这类框架的基本特征:完成面向对象的程序设计语言到关系数据库的映射。基于ORM框架完成映射后,既可利用面向对象程序设计语言的简单易用性,又可利用关系数据库的技术优势。

面向对象程序设计语言与关系数据库发展不同步时,需要一种中间解决方案,ORM框架就是这样的解决方案。笔者认为,随着面向对象数据库的发展,其理论逐步完善,最终会取代关系数据库。只是这个过程不可一蹴而就,ORM框架在此期间内会蓬勃发展。但随着面向对象数据库的出现,ORM工具会自动消亡。

10.1.2 为什么需要ORM

在上一节已经基本回答了这个问题,面向对象的程序设计语言,代表了目前程序设计语言的主流和趋势,其具备非常多的优势,比如:

q      面向对象的建模、操作。

q      多态、继承。

q      摒弃难以理解的过程。

q      简单易用,易理解性。

但数据库的发展并未与程序设计语言同步,而且,关系数据库系统的某些优势,也是面向对象的语言目前无法解决的。比如:

q      大量数据操作查找、排序。

q      集合数据连接操作、映射。

q      数据库访问的并发、事务。

q      数据库的约束、隔离。

面对这种面向对象语言与关系数据库系统并存的局面,采用ORM就变成一种必然。

10.1.3 流行的ORM框架简介

目前ORM框架的产品非常多,除了各大著名公司、组织的产品外,甚至,其他一些小团队也都有推出自己的ORM框架。目前流行的ORM框架有如下这些产品。

q      大名鼎鼎的Hibernate:出自Gavin King的手笔,目前最流行的开源ORM框架,其灵巧的设计,优秀的性能,还有丰富的文档,都是其迅速风靡全球的重要因素。

q      传统的Entity EJBEntity EJB实质上也是一种ORM技术,这是一种备受争议的组件技术,很多人说它非常优秀,也有人说它一钱不值。事实上,EJBJ2EE的蓬勃发展赢得了极高的声誉,就笔者的实际开发经验而言,EJB作为一种重量级、高花费的ORM技术上,具有不可比拟的优势。但由于其必须运行在EJB容器内,而且学习曲线陡峭,开发周期、成本相对较高,因而限制EJB的广泛使用。

q      IBATISApache软件基金组织的子项目。与其称它是一种ORM框架,不如称它是一种 Sql Mapping”框架。相对Hibernate的完全对象化封装,iBATIS更加灵活,但开发过程中开发人员需要完成的代码量更大,而且需要直接编写SQL语句。

q      OracleTopLink:作为一个遵循OTN协议的商业产品,TopLink在开发过程中可以自由下载和使用,但一旦作为商业产品使用,则需要收取费用。可能正是这一点,导致了TopLink的市场占有率。

q      OJBApache软件基金组织的子项目。开源的ORM框架,但由于开发文档不是太多,而且OJB的规范一直并不稳定,因此并未在开发者中赢得广泛的支持。

本章要点

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值