这一章包括:
n
使用
DAO
模式创建抽象层。
n
使用层的父型模式简化
resource cleanup
(资源清除)代码
n
用
spring
组织你的项目
理解
Hibernate
的底层将需要很长一段时间才能在你的项目上使用。但是在
Hibernate
库的基础之外,如
SessionFactory
(会话工厂),
Session
(会话),
mapping files
(
映射文件)和
hibernate Query Language(HQL)
(
Hibernate
查询语言),在一定高度组织一个应用程序并不是总是那么清楚的。 你能在你的项目中应用一些模式和好的经验。一些好的经验来自于社区;而另外一些来自应用于持久化的Java企业模式。这一章的目的就是带给你这些。
写程序有点像用小朋友的
alphabet blocks
(译者注:一种表面有字母的积木)建塔。如果你是建个小塔,那么你不需要仔细关心是怎么堆的。但如果你建一个大的塔,那么你可能要站得更高,你需要稍微不同的技术设置。多一点计划,多一点怎样堆的技术和可能使用一些超强的粘合剂。
无论是建立玩具塔还是写软件都要使用好的工具和技术。因此这一章就是怎样建立一座高塔。我们将讨论一些普通的模式:
Data Access Object(DAO)
(
封装底层数据操作
)和层的父型模式。另外,
spring
,
流行的开源项目将为简化你的代码提供组织的工具。总之,这一章将给这个工具一个总的看法和怎样简化你的
Hibernate
项目。
章节目标:
在这一章,你将完成一些几点:
n
创建抽象层,使用
DAO
模式集中
SQL
语句,因此简化客户端对象使用。
n
使用层的父型模式改善
DAO
对象,简化
resource
cleanup
(资源清除)代码。
n
使用
spring
组织和简化你的
DAO
代码。
前提条件:
假设你已经完成以下几点:
n
熟悉模式的概念。
n
理解
session
(会话)和
transaction
(事务)的工作原理,特别是怎样使用持久化被打开
session
(会话)链接的对象。
n
你正在寻找一种能组织大型项目的技术。也就是说,使用好的框架代码将利于项目的以后的扩展。
7.1
无处不在的
DAO
模式
大多数
Java/J2EE
开发者都或多或少熟悉
DAO
模式。它是
Sun
公司特地在
Java
蓝图中强调的核心模式之一,在许多
Java
书籍中都提到过。它是在使用持久化数据存储的应用程序中的采取的第一个首要模式。一般使用在使用
SQL
的应用程序中,同样也适用于
Hibernate
的应用程序。
7.1.1
集中
HQL
DAO
模式的目的能在在一个简单问题的答案中找到:你的数据操作代码放在哪里?如果你面对一个遗留程序而苦恼,因为这个程序中
SQL
代码就像鸟枪发射那样四处都是,而你又没有这方面的经验。其实这并没什么。需要从新命名表中的列吗?准备好手中的枪,在整个程序中修改,以确保你没有遗漏任何
SQL
语句。
DAO
模式鼓励开发者集中
SQL
语句。那么怎样才是好的
SQL
和
HQL
呢?在应用程序中将
HQL
放在一个地方以便以后更容易维护和修改。新手可能不能很好的确定将
HQL
放在哪?他们将
HQL
放在DAO中。
Figure 7.1
显示了
Event
和
EventDao
怎样和数据库互动。
Figure 7.1
Event
和
EventDao
与数据库互动的图
通过前面章节知道,一个健壮的对象查询语句需要有错误处理管理,事务和
resource cleanup
(资源清除)。在余下的程序中最好隐藏以上那些。例如接下来的
Hibernate
程序,使程序更容易改变
ORM
(
object/relational mapping
)
(
对象关系映射
)实现(例如:改变成
JDO
)1。更重要的是,这个策略简化了客户端怎样和持久化层互动;他们不需要知道
session
(会话),
transaction
(事务)的边界,或者在他们使用之后是否被清除。
DAO
也有类型
你能使用
DAO
两种基本类型中的一种:
n
应用级
DAO(DAO per application)
:在一个应用中有一个中心的
DAO
对所有的实体对象添、删、改、查。
n
类级
DAO(DAO per class)
:每个实体类都有自己的
DAO
,用于自身实例的添、删、改、查。
Event
对象由
EventDao
负责。
你也可以应用其他镜像变量,例如使用模块级
DAO(DAO per module)
,但是最终的选择还是依赖于你有少个持久化对象。在有许多类的情况下,我们偏爱使用类级DAO策略。应用级
DAO
策略会出现“
bloatware
”类。第二,与类级
DAO
命名对称能更好的记忆。如果你需要找到
Event
类,你就能够记得起它的
DAO
是
EventDao
。最后,它满足开-关原则,原则规定类将因为扩充而打开,因为修改而关闭。你能够添加一个新的持久化类,而不需要修改中心的
DAO
。因此,让我们在示例中使用类级
DAO
。
简单的
DAO
Listing 7.1
显示了一个简单的
DAO
,它能处理满足大多数实体需要的基本的CRUD(添、删、改、查)业务。除了这些功能,它还有一些功能,因此客户端对象不用担心。
n
包括业务级
session
(
session per operation
)
;每个
session
(会话)都有添、删、改、查方法。
n
提供一个业务级
transaction
(事务)
(transaction
per operation)
。客户端对象不用担心开始和提交事务。
n
在
Hibernate2.1
中,处理捕获和
Hibernate
抛出的预期异常,将它们变为非预期异常,将不会使客户端代码混乱。如果你使用
Hibernate3.x
,你能够让没有从新抛出的非预期异常通过。(
In Hibernate 2.x, handles catching and handling
the checked exceptions that Hibernate throws, turning them into unchecked
exceptions, which won’t clutter up client code. If you use Hibernate 3.x, you
can just let the unchecked
HibernateException
s go without rethrowing
.
)
n
输入特性鲜明和明确的
DAO
;
EventDat
仅为
Event
工作,意思就是客户端代码不必执行手动的操作。(
Features strongly typed and
explicit DAOs; the
EventDao
only
works with
Event
s, meaning client code
doesn’t have to perform manual casting.
)
Listing 7.1
一个简单的有
添、删、改、查方法的
EventDao
---------------------------------------------------------------------------------------------
package
com.manning.hq.ch07;
import
org.hibernate.HibernateException;
import
org.hibernate.Session;
import
org.hibernate.Transaction;
import
org.apache.commons.logging.Log;
import
org.apache.commons.logging.LogFactory;
import
com.manning.hq.ch07.Event;
import
com.manning.hq.ch07.HibernateFactory;
import
java.util.List;
/**
* DAO管理持久Evnet
*/
public
class
SimpleEventDao {
Log log
=
LogFactory.getLog(SimpleEventDao.
class
);
private
Session session;
private
Transaction tx;
public
SimpleEventDao() {
HibernateFactory.buildIfNeeded();
//
初始化SessionFactory。
}
public
void
create(Event event)
throws
DataAccessLayerException {
try
{
startOperation();
//
打开Session和开始transaction
session.save(event);
//
保存Event。
tx.commit();
}
catch
(HibernateException e) {
handleException(e);Rolls back和抛出异常。
}
finally
{
HibernateFactory.close(session);
}
}
public
void
delete(Event event)
throws
DataAccessLayerException {
try
{
startOperation();
session.delete(event);
tx.commit();
}
catch
(HibernateException e) {
handleException(e);
}
finally
{
HibernateFactory.close(session);
}
}
public
Event find(Long id)
throws
DataAccessLayerException{
Event event
=
null
;
try
{
startOperation();
event
=
(Event) session.load(Event.
class
, id);
tx.commit();
}
catch
(HibernateException e) {
handleException(e);
}
finally
{
HibernateFactory.close(session);
}
return
event;
}
public
void
update(Event event)
throws
DataAccessLayerException {
try
{
startOperation();
session.update(event);
tx.commit();
}
catch
(HibernateException e) {
handleException(e);
}
finally
{
HibernateFactory.close(session);
}
}
private
void
handleException(HibernateException e)
throws
DataAccessLayerException {
HibernateFactory.rollback(tx);
throw
new
DataAccessLayerException(e);
//
二选一,你能够从新抛出,就像…
//
throw e;
}
private
void
startOperation()
throws
HibernateException {
session
=
HibernateFactory.openSession();
tx
=
session.beginTransaction();
}
}
package
com.manning.hq.ch07;
public
class
DataAccessLayerException
extends
RuntimeException {
//
其他的构造函数省略。
public
DataAccessLayerException(Throwable cause) {
super
(cause);
}
}
---------------------------------------------------------------------------------------------
SimpleEventDao
是个极其简单的
DAO
,它有添,删,改,查四种方法。每种方法在一个
transaction
(事务),打开和关闭一个
session
(会话)范围内处理业务。它是明确的,也就是说每个
Event
都有独有的业务处理,因此客户端类不需要再处理这些了。当这个实现是简单的(以后可能随着扩展会变大),客户端代码在运行
event
时代码就会变得很短。(
While this implementation is simple (perhaps overly so, as we will explore
here later), it greatly shortens the client code that works with events.
)因此创建和查询一个event就象下面的一样简单:---------------------------------------------------------------------------------------------
Event event
=
new
Event();
event.setName(
"
A new Event
"
);
EventDao eventDao
=
new
EventDao();
eventDao.create(event);
Event foundEvent
=
eventDao.find(event.getId());
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
正如你看到的,不需要处理凌乱的异常,这里也不需要处理
resource cleanup
(资源清除)。现在让我们讨论更多一些这个实现出现的问题,并且我们该怎样提高它。
7.2
分析
DAO
我们在前面章节练习的简单的DAO实现出现了一些问题,有些你或许已经遇到。让我们看一看。
7.2.1
boilerplate
code
(
样板代码)
Listing 7.1
包括许多资源管理和异常处理代码。每个方法都有打开
session
(会话),开始事务,执行自己的商务业务,提交事务,事务回滚,最后关闭
session
(会话)。每个方法基本就像下面那样:
-------------------------------------------------------------------------------
try
{
startOperation();
session.save(event);
tx.commit();
} catch (HibernateException e) {
handleException(e);
} finally {
HibernateFactory.close(session);
}
-------------------------------------------------------------------------------
Session.save(event)
这一行是每个方法中唯一改变的。甚至
refactoring
几个便利的方法(译者注:找了不少词典,都没有找到“
refactor
”),如
startOperation
()
和
handleException
()
,都不能完全摆脱你的样板代码。(
Even
refactoring out a few convenience methods,such as
startOperation()
and
handleException()
, doesn’t completely rid you
of boilerplate code
)一个
potential solution
(潜在的解决方案)就是在7.3节讨论的层的父型模式。
7.2.2
P
otential
duplication
(
潜在的复制)
增加一个新的
DAO
变得很简单,只需要复制和粘贴。如果你需要其他的
DAO
,如
LocationDao
,
我们将复制和粘贴
EventDao
,然后在每个方法中改变关联的少许几行代码。因为我们知道复制是所有程序的恶魔之首,显然需要做点事情。建立层的父型模式是非常有帮助的。
7.2.3
游离对象
因为每个方法都是与单一的
session
(会话)和
transaction
(事务)运作的,与
DAO
运作的所有的
Event
是严格的游离对象。这个行为也许是美好的,但它不能够利用
Hibernate
的自动脏对象检查和
session-level
object cache
(会话级对象缓存)。例如,假如客户端这样写:
---------------------------------------------------------------------------------------------
Event foundEvent
=
eventDao.find(event.getId());
foundEvent.setDuration(
30
);
eventDao.update(foundEvent);
---------------------------------------------------------------------------------------------
Find
运行在一个
session
(会话)中,
update
运行在另一个
session
(会话)中。它一定会运行,但如果
find
和
update
能以某种方法共享一个
session
(会话)那才是真正的好。并且,它能更好的避免在
session
(会话)周围的凌乱的方法署名。当它运作时,它是难看的,因此我们不想看到下面这样:
---------------------------------------------------------------------------------------------
Session session
=
HibernateFactory.openSession();
Event foundEvent
=
eventDao.find(event.getId(), session);
foundEvent.setDuration(
30
);
eventDao.update(foundEvent, session);
---------------------------------------------------------------------------------------------
给方法增加
session
(会话)参数,强制责任,管理和在客户端代码共享
session
(会话)。这样会带来重复,复杂和潜在的错误。
这个问题的一个潜在的解决方案是众所周知的
Thread Local Session
模式。这个模式将在第八章介绍,因此我们不会在这里直接介绍。我们改用另外一种框架练习,
spring
,它使用
Thread
Local Session
模式,在下面会有介绍。
7.3
层的父型模式
常规的
J2EE
智者认为应用程序被划分成不同的层。假设你的应用程序已经有这些层了,当然,依赖于你所读的书。一些普遍的层的选择如下:
n
表示层,这里是所有用户交互和表示的代码。
n
领域层,这里是逻辑的商务代码。
n
持久层,这里是数据存储操作的代码。
不管你的应用程序是否有这些层,在层中的每个对象都有某些共同的代码能够被固定到一个类中,这一点是非常普通的。这个原因出现了层的父型模式,每一层都有“一种类型,这种类型作为所有的层中的类型的父型”2。你能够使用父型模式简化你的
DAO
。
Figure7.2
用一个简单的层次显示了层的父型
AbstractDao
,它提供了
protected
方法,在子类中可以覆盖和改为
public
。
Figure 7.2
层的父型
AbstractDao
的图
下一步就是创建
AbstractDao
,下一节将介绍。
7.3.1
创建
AbstractDao
第一步是创建你的父型,
AbstractDao
,所有的
DAO
最后将扩展。
Listing
7.2
显示了类的内容。
Listing 7.2
层的父型实现,
AbstractDao
,它有所有
DAO
的共同的业务。
-----------------------------------------------------------------------------------------
package com.manning.hq.ch07;
import org.hibernate.HibernateException;
import org.hibernate.Query;import org.hibernate.Session;
import org.hibernate.Transaction;
import java.util.List;
/**
* 层的父型处理所有DAO共同的运作
*/
public abstract class AbstractDao{
private Session session;
private Transaction tx;
public AbstractDao() {
HibernateFactory.buildIfNeeded();
}
protected void saveOrUpdate(Object obj){//运作是普通的,而不是特指域对象
try {
startOperation();
session.saveOrUpdate(obj);
tx.commit();
} catch (HibernateException e) {
handleException(e);
} finally {
HibernateFactory.close(session);
}
}
protected void delete(Object obj) {
try {
startOperation();
session.delete(obj);
tx.commit();
} catch (HibernateException e) {
handleException(e);
} finally {
HibernateFactory.close(session);
}
}
protected Object find(Class clazz, Long id){//查找基于类和id的持久化对象
Object obj = null;
try {
startOperation();
obj = session.load(clazz, id);
tx.commit();
} catch (HibernateException e) {
handleException(e);
} finally {
HibernateFactory.close(session);
}
return obj;
}
protected List findAll(Class clazz) {
List objects = null;
try {
startOperation();
Query query = session.createQuery("from " + clazz.getName());
objects = query.list();
tx.commit();
} catch (HibernateException e) {
handleException(e);
} finally {
HibernateFactory.close(session);
}
return objects;
}
protected void handleException(HibernateException e) throws DataAccessLayerException {
HibernateFactory.rollback(tx);
throw new DataAccessLayerException(e);
}
protected void startOperation() throws HibernateException {
session = HibernateFactory.openSession();
tx = session.beginTransaction();
}
}
--------------------------------------------------------------------------------------------
在这个Listing中,你看到了共同的CRUD方法,包括save, find和delete方法都放入AbstractDao类中。他们是
generic和protected,因此子类能够调用它们。这样你的EventDao将简化。这里是一个简单的简化了的方法:
---------------------------------------------------------------------------------------------
public class ImprovedEventDao extends AbstractDao {
// 其他的方法省略
public void create(Event event) throws DataAccessLayerException {
saveOrUpdate(event);
}
public Event find(Long id) throws DataAccessLayerException {
return (Event) find(Event. class , id);
}
}
--------------------------------------------------------------------------------------------
ImprovedEventDao的仅有的责任就是执行业务和委托调用父类。样板代码和潜在的复制的双重问题得到解决。当我们增加一个新的实体对象时,如:Locations或者Speakers,添加新的DAO--使用AbstractDao作为层的父型,将是非常迅速的。
--------------------------------------------------------------------------------------------
public classImprovedLocationDao extendsAbstractDao {
//其他的方法省略
publicvoid create(Location location) throws DataAccessLayerException {
saveOrUpdate(location);
}
publicLocation find(Long id) throws DataAccessLayerException {
return (Location) find(Location. class , id);
}
}
---------------------------------------------------------------------------------------------
通过层的父型的介绍,剩余的问题得到解决,
DAO
与游离对象运作。我们想在我们的方法中共享会话,甚至通过不同的
DAO
。为了做到这一点,我们将学习一个新的流行框架,
Spring
.
7.4
Spring
框架
我们已经注意到已经定义的
DAO
实现中的一些缺陷。复制资源管理代码和使用业务级的
session
(
session per operation
)使解决方案会比我们需要的更加复杂,而且也没有我们喜欢的灵活。我们一定能过编写更好的更健壮的解决方案。幸运的是,我们不需要担心
---
一个优秀的开源解决方案,
Spring
框架已经提供给我们了。
Spring
解决了比帮助我们解决
Hibernate
更多的问题。这是事实,“一个轻量级的容器,允许开发者连接商务对象,
DAO
和资源就像
JDBC DataSources
和
Hibernate SessionFactories
。”
3
它使用中心
XML
配置文件去管理资源,甚至它自身的
web MVC
框架(
Model-View-Controller
)。
Spring
是普通的框架,就是说它可以使用在许多不同的位置中。如果你不熟悉
Spring
,你也许会想我该怎样使用它,你也许认为他仅仅是个框架,假设用来提供某种编译。因此我们将在这里展示。
Spring
已经被考虑成熟的分割为稳固的集中的若干模块,包括我们先前提到的
MVC
web
框架,
JDBC
支持,
aspect-oriented programming
(
AOP
)(
面向剖面编程
)和
ORM
模块。这样就允许你使用其中你需要的,而不用学习或者考虑其他的。为了我们的目的,我们将仅仅学习怎样简化你的
Hibernate
代码,也就是
ORM
模块。最佳的开始的地方是模板。
首先,你需要获得
Spring
框架的副本,你可以在
www.springframework.org
找到。解压到
applications
目录下的
Hibernate
旁边。
Spring
有许多可选择的包,但为了简单,你只需要考虑一个
JAE
包,它就是
applications\spring-framework-1.2-rc2\dist\spring.jar
文件。将它加入到
build.xml
文件的
classpath
中。
-------------------------------------------------------------------------------------------------------------------------
<
property name
=
"
spring.version
"
value
=
"
1.2-rc2
"
/>
<
property name
=
"
spring.lib.dir
"
value
=
"
${applications.dir}/spring-framework-${spring.version}
"
/>
<
path id
=
"
spring.lib.path
"
>
<
fileset dir
=
"
${spring.lib.dir}\dist
"
>
<
include name
=
"
**/spring.jar
"
/>
</
fileset
>
</
path
>
<
path id
=
"
runtime.classpath
"
>
//
其他的 paths 省略。
<
path refid
=
"
spring.lib.path
"
/>
<
path
>
-------------------------------------------------------------------------------------------------------------------------
这个代码配置了
Spring
使它能过在我们的示例项目中使用。
Hibernate3
最近已经发布(在出版的时候),其他支持项目如
Spring
也跟上提供支持了。这里我们使用最新的版本。另外,因为
Spring
不得不支持
Hibernate 2
和
Hibernate 3,
一个新的包,
org.springframework.orm
。
hibernate3
已经将它加入到
Hibernate3
的项目中了。接下来,让我们看看
Spring
怎样被使用来简化我们的示例项目。
7.4.1
在模板中是什么
?
Spring
给我们提供了
Hibernate
业务的模板。模板有什么,我们为什么需要它?答案就在我们的
SimpleEventDao
中的
create
方法中。
--------------------------------------------------------------------------------------------
protected
void
create(Event event) {
try
{
startOperation();
session.save (event);
tx.commit();
}
catch
(HibernateException e) {
handleException(e);
}
finally
{
HibernateFactory.close(session);
}
}
--------------------------------------------------------------------------------------------
如果你注意了,方法中只调用一个真正的事件是:
save
()。其他的每一行,我们喜欢称为“
excise
(税)”。
excise
(税)是我们为了工作不得不去做的额外的事情,这些事情不是真正的直接重要。它就像当你开车去上班,驾驶的实际行动才是重要的事情;开车库门和倒车都是
excise
(税)任务,它可以看成同等重要,也可以忽略或者自动完成。
在程序中,
excise
(税)是框架或者语言安全需要,你不得不编写的代码。一个典型的
excise
(税)例子就是
Java
除去了内存管理。
Spring
能够除去
Hibernate
和
JDBC
要求之下的资源管理
excise
(税)的一部分。
一般情况下,当你复制代码,你能够
refactor
出
一个方法或类。这里,因为复制代码是商务方法周围的
resource
cleanup
(资源清理),使它更加复杂。现在有模板可用。导入
Spring
提供的类:
org.springframework.orm.hibernate3.HibernateTemplate
。它包含了所有资源处理代码以致于你仅只要写一个重要方法就可以了。我们的
create()
方法能够这样写:
----------------------------------------------------------------------------------------------------------------------------------------------------
import
org.hibernate.Hibernate;
import
org.hibernate.SessionFactory;
import
org.springframework.orm.hibernate3.HibernateTemplate;
protected
void
create(Event event) {
SessionFactory sf
=
HibernateFactory.getSessionFactory();
HibernateTemplate template
=
new
HibernateTemplate(sf);
template.saveOrUpdate(event);
}
-------------------------------------------------------------------------------------------------------------------------
注意我们没有做什么:
n
从
SessionFactory
获得
session
n
开始事物
n
捕获预期异常和将他转变为非预期异常(
3.x
版本不需要,
2.x
则需要)
n
提交事物
n
将改变输入数据库
n
关闭
session
这里有许多事情我们不需要担心,因为
HibernateTemplate
已经照顾到了。你或许注意到
HibernateTemplate
看上去像包裹
Session
的周围。实际上,可以把
HibernateTemplate
理解为一个“聪明”的
Session
,它知道怎样打开,关闭和在运行完清除。在之前默认的情况下,它使用的是方法级事务(
transaction per method
)模式。这是非常简单的,但你将在后面看到你也能够改事务的范围。这里有两个基本的方式和
Hibernatetemplate
互动:通过
convenience
mehod
(便利的方法)和重要的
callback
(回调)。
Convenience methods
(便利的方法)
简单的事情将变得容易。在许多案例中,你想用
Session
(会话)做什么是非常重要的:执行保存,更新有力对象,或者运行
HQL
查询。没有一个需要礼仪来获得完成。
HibernateTemplate
类提供了基本的方法,因此一行代码就可以简单的调用业务。这里有一些方法的简单样例:
---------------------------------------------------------------------------------------------
import
com.manning.hq.ch07.Event;
import
com.manning.hq.ch07.HibernateFactory;
import
org.springframework.orm.hibernate3.HibernateTemplate;
import
java.util.List;
SessionFactory sessionFactory
=
HibernateFactory.getSessionFactory();
//
创建连接 SessionFactory 的模板
HibernateTemplate template
=
new
HibernateTemplate(sessionFactory);
Event event1
=
new
Event();
event1.setName(
"
Event 1
"
);
Event event2
=
new
Event();
event2.setName(
"
Event 2
"
);
try
{
template.save (event1);
//
在一个事务中保存 event
template.save (event2);
//
加载一个 event
Event obj
=
(Event) template.load(Event.
class
, event1.getId());
System.out.println(
"
Loaded the event
"
+
obj.getName());
//
找到所有的 event
List events
=
(List) template.find(
"
from Event
"
);
System.out.println(
"
# of Events
"
+
events.size());
}
finally
{
template.delete(event1);
//
删除一个 event
template.delete(event2);
}
---------------------------------------------------------------------------------------------
convenience
methods
(
便利的方法)是有代表性的正确的命名,就像
Session
(会话)中的方法一样。他们能被
session
(会话)作为
one-for-one
(一对一)复位直接调用,没有任何杂乱的
resource cleanup
(资源清除)代码障碍。
Callback
(回调)
复杂的事情可能有好处。不是所有的在单个
transaction
(
事务)中的单个
query
(查询)能够轻松的减少业务。在这些业务中,
spring
提供了
Callback
接口。它允许你在将要执行的模板中编写
callback
方法。例如,如果你想构建一个复杂的
query
(查询),更新一些数据,然后保存,所有的都在一个业务中:
-------------------------------------------------------------------------------------------
import
org.hibernate.HibernateException;
import
org.springframework.orm.hibernate3.HibernateCallback;
import
java.sql.SQLException;
import
org.hibernate.Query;
import
java.util.List;
import
java.util.Iterator;
import
com.manning.hq.ch07.Event;
template.execute(
new
HibernateCallback() {
public
Object doInHibernate(Session session)
throws
HibernateException, SQLException {
Query query
=
session.createQuery(
"
from Event
"
);
query.setMaxResults(
2
);
List events
=
query.list();
for
(Iterator it
=
events.iterator(); it.hasNext();) {
Event event
=
(Event) it.next();
event.setDuration(
60
);
}
return
null
;
}
}) ;
---------------------------------------------------------------------------------------------
这里,
Callback
接口使用了匿名内部类,
HibernateCallback
,定义了一个单一的方法,
doInHibernate()
。你可以编写方法主体,然后提交
HibernateCallback
对象给模板,然后执行。模板处理资源管理代码,让你只需要编写任务的
query
(查询)逻辑。
7.4.2
Beans
和 它们的
factories
(工厂)
你已经看到能够程序化使用
Spring
去减少
resource
cleanup
(资源清除)代码。另外i,它能过更好的组织项目。
Spring
的惯例的说法是“轻量级”容器。它胜任执行和配置简单的
JavaBeans
。它基本扮演构建和配置你应用程序中的
beans
的工厂角色。意思是它能够被使用去配置大多数现存的
architectures
(框架)和库,包括
Hibernate
。
中心的配置文件
在这一点上,你将通过组合使用
hibernate.cfg.xml
文件(
declaratively
(声明性的
))和使用灵活的编程方式,如HibernateFactory来配置Hibernate。Spring提供了另外一个方法整体声明配置Hibernate。使用Spring的最大好处就是你能够减少编程配置需要的元素。
Spring
能够读用一般配置格式编写的
XML
文件。
XML
指定了怎样连接不同对象,包括
DataSource
(数据源),
SessionFactory
和所有的
DAOs
。一旦你配置了文件,你就能够将它作为查找
DAOs
的中心“票据交换所”使用。例如,在
classpath
的
root
(根部)创建一个叫
applicationContext.xml
文件。就像
listing7.3
所显示那样。
listing7.3 ApplicationContext.xml
,定义了
DataSource
(数据源),
SessionFactory
和
DAO
-------------------------------------------------------------------------------
<?
xml version
=
"
1.0
"
encoding
=
"
UTF-8
"
?>
<!
DOCTYPE beans PUBLIC
"
-//SPRING//DTD BEAN//EN
"
"
http://www.springframework.org/dtd/spring-beans.dtd
"
>
<
beans
>
<
bean id
=
"
dataSource
"
class
=
"
org.apache.commons.dbcp.BasicDataSource
"
destroy
-
method
=
"
close
"
>
①
<
property name
=
"
driverClassName
"
>
<
value
>
com.mysql.jdbc.Driver
</
value
>
</
property
>
<
property name
=
"
url
"
>
<
value
>
jdbc:mysql:
//
localhost/events_calendar</value>
</
property
>
<
property name
=
"
username
"
>
<
value
>
root
</
value
>
</
property
>
<
property name
=
"
password
"
>
<
value
></
value
>
</
property
>
</
bean
>
<
bean id
=
"
factory
"
class
=
"
org.springframework.orm.hibernate3.LocalSessionFactoryBean
"
>
②
<
property name
=
"
mappingResources
"
>
<
list
>
<
value
>
com
/
manning
/
hq
/
ch07
/
Event.hbm.xml
</
value
>
<
value
>
com
/
manning
/
hq
/
ch07
/
Location.hbm.xml
</
value
>
</
list
>
</
property
>
<
property name
=
"
hibernateProperties
"
>
<
props
>
<
prop key
=
"
hibernate.dialect
"
>
org.hibernate.dialect.MySQLDialect
</
prop
>
<
prop key
=
"
hibernate.show_sql
"
>
false
</
prop
>
</
props
>
</
property
>
<
property name
=
"
dataSource
"
>
③
<
ref bean
=
"
dataSource
"
/>
</
property
>
</
bean
>
<
bean id
=
"
eventDao
"
class
=
"
com.manning.hq.ch07.EventSpringDao
"
>
④
<
property name
=
"
sessionFactory
"
>
⑤
<
ref bean
=
"
factory
"
/>
</
property
>
</
bean
>
</
beans
>
-------------------------------------------------------------------------------
listing7.3
解释
①
配置一个基本的数据源,它使用由
Hibernate
发布的
Apache Commons database connection
pool (DBCP)
。
②
配置一个
SessionFactory
,构建在
Spring
SessionFactory
wrapper, LocalSessionFactoryBean
。当
Spring
读取这个文件时,它就构建一个
SessionFactory
。
SessionFactory
存储在
Key factory
。
③
连接
SessionFactory
和数据源。
④
配置你的
EventSpringDao
和
eventDao
。
⑤
连接
DAO
和
session factory
。他允许
DAO
打开
session
和发布
queries
。
-------------------------------------------------------------------------------------------------------------------------
Listing 7.3
中
XML
配置文件列举了所有能够经常改变详细内容。它完成了许多与
hibernate.cfg.xml
所作的相同的事情,也为我们构建了
SessionFactory
,这一点将在下一节看到。
构建
AppicationContext
你刚刚创建的
applicationContext.xml
详细的描述了怎样构建
session
工厂。它基本上可以一对一的替换你所看到的
HibernateFactory
使用的
hibernate.cfg.xml
。它定义了通常在
hibernate.cfg.xml
中的属性和映射文件。在我们先前的示例代码中,你需要构建
SessionFactory
,或者通过你的
EventDao
对象连接
SessionFactory
。
Spring
颠覆了这个概念。
Spring
为你构建了
EventDao
,你需要询问
EventDao
的首选项,就像:
---------------------------------------------------------------------------------------------
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import
com.manning.hq.ch07.Event;
ClassPathXmlApplicationContext ctx
=
new
ClassPathXmlApplicationContext(
"
applicationContext.xml
"
);
EventSpringDao eventDao
=
(EventSpringDao) ctx.getBean(
"
eventDao
"
, EventSpringDao.
class
);
Event event
=
new
Event();
eventDao.saveOrUpdate(event);
--------------------------------------------------------------------------------------------
ClasspathXmlApplicationContext
看上去
在
classpath
中,因为配置文件的名字在
instruction
中已提供(
The
ClasspathXmlApplicationContext
looks in the classpath for the name of the
configuration file provided in the instructions.
)在这个案例中,
applicationContext.xml
在
classpath
的
root
(根)。你能够从
application context
通过名字请求
bean
。
getBean
()方法有两个参数:
bean
的名字(
eventDao
),你期望的类的类型(
EventSpringDao
)。
在此之下,
Spring
构建了
SessionFactory
并将所有的
Bean
连接在一起。我们更早的认识到
Spring
与
Javabean
一起工作。所有的在
applicationContext.xml
文件中的
<bean>
元素都需要
JavaBean
。这包括了象下面的
EventSpringDao
。
-------------------------------------------------------------------------------------------------------------------------
public
class
EventSpringDao
extends
AbstractSpringDao{
public
EventSpringDao(){}
public
Event find(Long id){
return
(Event)
super
.find(Event.
class
, id);
}
//
Other methods excluded
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
另外更早的认识到的好处是,
Spring
提供了
org.springframework.orm.hibernate3.support.HibernateDaoSupport
,
应用
Dao
层的父型。她管理了
SessionFactory
和一些有用的方法去处理
Session
,
Logging
和
HibernateTemplate
。这有这些方法的示例:
-------------------------------------------------------------------------------
public
abstract
class
HibernateDaoSupport
implements
InitializingBean {
protected
final
Log logger;
private
HibernateTemplate hibernateTemplate;
public
final
void
setSessionFactory(SessionFactory sessionFactory);
public
final
SessionFactory getSessionFactory();
public
final
void
setHibernateTemplate(HibernateTemplate hibernateTemplate);
public
final
HibernateTemplate getHibernateTemplate();
protected
final
Session getSession()
throws DataAccessResourceFailureException, IllegalStateException;
protected final void closeSessionIfNecessary(Session session);
}
-----------------------------------------------------------------------------------
他提供了一些基本的方法,但我们选择重写
HibernateDaoSupport
对象,为了
提供更多的便利方法。
Listing7.4
显示了改变的类。
Listing
7.4
你的应用的层的父型
DAOs
--------------------------------------------------------------------------------------------
package
com.manning.hq.ch07;
import
java.util.List;
import
org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public
abstract
class
AbstractSpringDao
extends
HibernateDaoSupport{
public
AbstractSpringDao() { }
protected
void
saveOrUpdate(Object obj) {
getHibernateTemplate().saveOrUpdate(obj);
}
protected
void
delete(Object obj) {
getHibernateTemplate().delete(obj);
}
protected
Object find(Class clazz, Long id) {
return
getHibernateTemplate().load(clazz, id);
}
protected
List findAll(Class clazz) {
return
getHibernateTemplate().find(
"
from
"
+
clazz.getName());
}
}
------------------------------------------------------------------
重点注意的是
AbstractSpringDao
使用它的父类
sessionFactory
成员。
HibernateDaoSupport
提供了
getter
和
setter
方法,
Spring
使用
setter
方法去连接
SessionFactory
。从
applicationContext
.xml
文件中调用这些行:
-------------------------------------------------------------------------------
<
bean id
=
"
eventDao
"
class
=
"
com.manning.hq.ch07.EventSpringDao>
<
property name
=
"
sessionFactory
"
>
<
ref bean
=
"
factory
"
/>
</
property
>
</
bean
>
------------------------------------------------------------------
这是调用
setSessionFactory
()
的片断,通过我们配置的
SessionFactory
,我们叫它为工厂。你所看到的
AbstractSpringDao
是由
AbstractDao
进化而来,你可以从
find()
方法中完整的除去大多数资源管理代码。
HibernateTemplate
和
HibernateDaoSupport
替代了被控制的一切。
创建注册(
Creating a registry
)
我们最终是为了将所有集中到一起并创建中心的注册,开发者能够使用它直接获得参数给
DAO
和
SessionFactory
。通过单一的类,
CalendarRegistry
,你能确保将来开发有一个显示的,单一的,强壮类型的类来使用,不需要知道类的详细内部机制。
Figure 7.3
显示了怎样将所有集中到一起。
使用
Spring
,你获得了配置的好处,允许你轻松的交换数据资源,数据库和添加新的对象成员。
Listing7.5
就是
CalendarRegistry
。
Figure
7.3 CalendarRegistry
图表,获得参数到
EventDao
Listing
7.5
CalendarRegistry
,
组织
Dao
的中心的类
-------------------------------------------------------------------------------------------------------------------------
package
com.manning.hq.ch07;
import
org.springframework.context.ApplicationContext;
import
org.springframework.context.support.ClassPathXmlApplicationContext;
import
org.hibernate.SessionFactory;
public
class
CalendarRegistry {
private
static
ApplicationContext ctx;
static
{
ctx
=
new
ClassPathXmlApplicationContext(
"
applicationContext.xml
"
);
}
private
CalendarRegistry() {}
public
static
SessionFactory getSessionFactory() {
return
(SessionFactory) ctx.getBean(
"
factory
"
, SessionFactory.
class
);
}
public
static
EventSpringDao getEventDao() {
return
(EventSpringDao)ctx.getBean(
"
eventDao
"
, EventSpringDao.
class
);
}
}
-------------------------------------------------------------------------------------------------------------------------
正如你所见,
CalendarRegistry
是单例模式,但是因为它背后是
Spring
,你能够轻松的实现底层的交换。它从
classpath
中装载单一的静态的
ApplicationContext
,然后使用它获取参数。现在客户端对象能够在项目中任何地方获得参数给
EventDao
,不需要知道关于
Spring
做了什么。
-------------------------------------------------------------------------------------------------------------------------
EventSpringDao eventDao
=
CalendarRegistry.getEventDao();
eventDao.saveOrUpdate(event);
--------------------------------------------------------------------------------------------
更多的
Spring
工具
就像你所见,使用
Spring
能够最大限度的简化
Hibernate
的资源管理代码。我们显示了两个你能够使用的包含的级别。
HibernateTemplate
能够被嵌入你的
DAO
中,或者使用
Spring
的轻量级容器去管理
DAO
。
Spring is a fairly straightforward framework, but we haven’t scratched the
surface of what it can do here.
(
Spring
还算一个直接的框架,但我们没有就它能在这做什么过多的纠缠)
Spring
也支持事务
API
管理框架,
AOP
框架,一个
RuntimeException
框架,能够拦截数据库发出的大多数迟钝的
SQLException
,将它转变成更显而易见和包罗万象的
exception
。更多的信息,请查看实在又全面的
Spring
文档。
7.5
总结
这一章的焦点在于提高组织你的应用程序代码。我们集中了
HQL
,使用了
DAO
模式。我们通过加入其它的模式更提高了初始实现,层的父型,它允许增加更多的DAO,不需要复制过多的代码到项目中新添加的对象成员。
这一章也探索了怎样使用
Spring
,另一个流行的开源项目,管理
Hibernate
需要的
boilerplate
(样板)资源管理代码。
Spring
提供了标题选项,
HibernateTemplate
,
pluggable
,
ApplicationContext
。增加一个
CalendarRegistry
,
它提供了方法使用
Spring
获得项目中的参数,不再需要去包含一个“单块集成电路”了。
1
Hibernate
如此优秀,为什么还要用别的?在现实中,转变
ORM
实现并不是微不足道的,
DAO
是容易产生漏洞的,提取那样做的话就会有问题,将不能从应用程序中完全隐藏
Hibernate
。(
DAOs are leaky enough
abstractions that doing so probably won’t completely hide Hibernate from the
application.
)。
因此不需要放太多的精力去密封
DAO
层,在以后为了可能的某种目的要转变
ORM
实现。
2
选自
《
Patterns
of Enterprise Application Architecture
》
,
作者:
Martin Fowler(Addision-Wesley
专家,
2003
)
3
选自在线文章“
Data
Access with Spring Framework
”,作者:
Juergen Hoeller
,
2003
年
7
月;
http://hibernate.bluemars.net/110.html
。
posted on 2005-12-21 22:21
千山鸟飞绝 阅读(10510)
评论(6) 编辑 收藏 所属分类:
Hibernate