随笔 - 4, 文章 - 2, 评论 - 36, 引用 - 0
数据加载中……

Hibernate API(转载)

提供访问数据库操作的接口:Session接口,Transation接口,Query接口

用于配置Hibernate的接口:Configuration接口

Hibernate核心接口

Configuration接口:配置Hibernate,Hibernate应用通过Configuration实例来指定对象-关系映射文件的位置或动态配置Hibernate的属性,然后创建SessionFactory对象实例。

SessionFactory接口:初始化Hibernate,充当数据存储源的代理、创建Session对象。

一个SessionFactory实例对应一个数据存储源,Hibernate应用从SessionFactory中获得Session对象实例。

特定:它是线程安全的,意味着同一个实例可以被应用的多个线程共享,它是重量级的,意味着不能随意创建或销毁实例,一个数据库访问只需创建一个实例。

Session接口:负责保存、更新、删除、加载、查询对象

Session接口被称做持久化管理器,每个Session实例都有自已的缓存,用来存放被当前工作单元加载的对象且只能被当前工作单元访问。

特性,它不是线程安全的,应该避免多个线程共享同一个Session实例,它是轻量级的,意味着创建或销毁实例不需要消耗太多的资源,可以为每个请求分配单独的Session实例,或者为每个工作单元分配单独的Session实例。

Transation接口:Hibernate的数据库事务接口,对底层的事务接口做了封装。

封装的底层事务接口:

JDBC API、JTA(Java Transation)API、CORBA(Common Object Request Broker Architecture)API

通过一致的Transation接口来声明事务边界,有利于应用在不同环境或容器中移植。

Query接口和Criteria接口:执行数据库查询,控制执行查询的过程。

Query接口封装了一个面向对象的查询语句(Hibernate Query Language, HQL)

Criteria接口封装了基于字符串形式的查询语句,擅长于执行动态查询。

回调接口:Interceptor接口、Lifecycle接口、Validatable接口

当一个对象发生了特定的事件(如加载、保存、更新、删除),Hibernate应用可以通过回调来响应事件。

回调接口按实现方式可分为:

Lifecycle接口和Validatable接口:由持久化类来实现接口。

Lifecycle接口,使持久化类的实例能响应被加载、保存、删除的事件。

Validatable接口,使持久化类的实例在被保存前进行数据验证。

Interceptor接口:不必由持久化类来实现接口。Interceptor实现类负责响应持久化类的实例被加载、保存、更新、删除的事件。

映射类型接口:Type接口、UserType接口、CompositeUserType接口

Type接口,表示Hibernate映射类型,用于把域对象映射为数据库的关系数据。

Type接口的实现类:PrimitiveType类,映射Java基本类型。DateType类,映射Java日期类型。

扩展接口:

定制主键的生成策略:IndentifierGenerator接口

定制本地SQL方言:Dialect抽象类

定制缓存机制:Cache接口、CacheProvider接口

定制JDBC连接管理:ConnectionProvider接口

定制事务管理:TransationFactory接口、Transation接口、TransationManagerLookup接口

定制ORM策略:ClassPersister接口及其子接口

定制属性访问策略:PropertyAccesser接口

创建代理:ProxyFactory接口

定制客户化映射类型:UserType接口、CompositeUserType接口。

在Hibernate中处理批量更新和批量删除

批量更新是指在一个事务中更新大批量数据,批量删除是指在一个事务中删除大批量数据,以下程序直接通过Hibernate API批量更新

tx = session.beginTransaction();
Iterator customers=session.find("from Customer c where c.age>0").iterator();
while(customers.hasNext()){
Customer customer=(Customer)customers.next();
customer.setAge(customer.getAge()+1);
}
tx.commit();
session.close();
如果CUSTOMERS表中有1万条年龄大于零的记录,那么Session的find()方法会一下子加载1万个Customer对象到内存。当执行tx.commit()方法时,会清理缓存,Hibernate执行1万条更新CUSTOMERS表的update语句:

update CUSTOMERS set AGE=? …. where ID=i;
update CUSTOMERS set AGE=? …. where ID=j;
……
update CUSTOMERS set AGE=? …. where ID=k;
以上批量更新方式有两个缺点:

(1) 占用大量内存,必须把1万个Customer对象先加载到内存,然后一一更新它们。

(2) 执行的update语句的数目太多,每个update语句只能更新一个Customer对象,必须通过1万条update语句才能更新一万个Customer对象,频繁的访问数据库,会大大降低应用的性能。

为了迅速释放1万个Customer对象占用的内存,可以在更新每个Customer对象后,就调用Session的evict()方法立即释放它的内存:

tx = session.beginTransaction();
Iterator customers=session.find("from Customer c where c.age>0").iterator();
while(customers.hasNext()){
Customer customer=(Customer)customers.next();
customer.setAge(customer.getAge()+1);
session.flush();
session.evict(customer);
}
tx.commit();
session.close();

在以上程序中,修改了一个Customer对象的age属性后,就立即调用Session的flush()方法和evict()方法,flush()方法使Hibernate立刻根据这个Customer对象的状态变化同步更新数据库,从而立即执行相关的update语句;evict()方法用于把这个Customer对象从缓存中清除出去,从而及时释放它占用的内存。

但evict()方法只能稍微提高批量操作的性能,因为不管有没有使用evict()方法,Hibernate都必须执行1万条update语句,才能更新1万个Customer对象,这是影响批量操作性能的重要因素。假如Hibernate能直接执行如下SQL语句:

update CUSTOMERS set AGE=AGE+1 where AGE>0;

那么以上一条update语句就能更新CUSTOMERS表中的1万条记录。但是Hibernate并没有直接提供执行这种update语句的接口。应用程序必须绕过Hibernate API,直接通过JDBC API来执行该SQL语句:

tx = session.beginTransaction();
Connection con=session.connection();
PreparedStatement stmt=con.prepareStatement("update CUSTOMERS set AGE=AGE+1 "
+"where AGE>0 ");
stmt.executeUpdate();
tx.commit();
以上程序演示了绕过Hibernate API,直接通过JDBC API访问数据库的过程。应用程序通过Session的connection()方法获得该Session使用的数据库连接,然后通过它创建PreparedStatement对象并执行SQL语句。值得注意的是,应用程序仍然通过Hibernate的Transaction接口来声明事务边界。

如果底层数据库(如Oracle)支持存储过程,也可以通过存储过程来执行批量更新。存储过程直接在数据库中运行,速度更加快。在Oracle数据库中可以定义一个名为batchUpdateCustomer()的存储过程,代码如下:

create or replace procedure batchUpdateCustomer(p_age in number) as
begin
update CUSTOMERS set AGE=AGE+1 where AGE>p_age;
end;
以上存储过程有一个参数p_age,代表客户的年龄,应用程序可按照以下方式调用存储过程:

tx = session.beginTransaction();
Connection con=session.connection();
String procedure = "{call batchUpdateCustomer(?) }";
CallableStatement cstmt = con.prepareCall(procedure);
cstmt.setInt(1,0); //把年龄参数设为0
cstmt.executeUpdate();
tx.commit();
从上面程序看出,应用程序也必须绕过Hibernate API,直接通过JDBC API来调用存储过程。

Session的各种重载形式的update()方法都一次只能更新一个对象,而delete()方法的有些重载形式允许以HQL语句作为参数,例如:

session.delete("from Customer c where c.age>0");
如果CUSTOMERS表中有1万条年龄大于零的记录,那么以上代码能删除一万条记录。但是Session的delete()方法并没有执行以下delete语句:

delete from CUSTOMERS where AGE>0;

Session的delete()方法先通过以下select语句把1万个Customer对象加载到内存中:

select * from CUSTOMERS where AGE>0;
接下来执行一万条delete语句,逐个删除Customer对象:

delete from CUSTOMERS where ID=i;
delete from CUSTOMERS where ID=j;
……
delete from CUSTOMERS where ID=k;
由此可见,直接通过Hibernate API进行批量更新和批量删除都不值得推荐。而直接通过JDBC API执行相关的SQL语句或调用相关的存储过程,是批量更新和批量删除的最佳方式,这两种方式都有以下优点:

(1) 无需把数据库中的大批量数据先加载到内存中,然后逐个更新或修改它们,因此不会消耗大量内存。

(2) 能在一条SQL语句中更新或删除大批量的数据。

在hibernate中,最核心的概念就是对PO的状态管理,一个PO有三种状态:

未被持久化的VO

此时就是一个内存对象VO,由JVM管理生命周期

已被持久化的PO,并且在Session使命周期内

此时映射数据库数据,由数据库管理生命周期

曾被持久化过,但现在和Session已经detached了,以VO的身份在运行。

这种和Session已经detached的PO还能够进入别一个Session,继续进行PO状态状管,此时它就成为PO的第二种状态了。

这种PO实际上是跨了Session进行了状态维护的。

在传统的JDO1.x中,PO只有前面两种状态,一个PO一旦脱离PM,就丧失了状态了,不再和数据库数据关联,成为一个纯粹的内存VO,它即使进入一个新的PM,也不能恢复它的状态了。

Hibernate强的地方就在于,一个PO脱离Session之后,还能保持状态,再进入一个新的Session之后,就恢复状态管理的能力,但此时状态管理需要使用session.update或者session.saveOrUpdate,这就是Hibernate Reference中提到的“requires a slightly different programming model ”

posted on 2009-01-08 12:04 幽梦新影 阅读(1473) 评论(0)  编辑  收藏 所属分类: hibernate


只有注册用户登录后才能发表评论。


网站导航: