经过一年多的活动,Hibernate2分支的开发终于告一段落;Hibernate 2.1.3将是最后的几个版本之一,它代表了具有所有典型Java应用程序所需功能的基本稳定的POJO持久化解决方案。Hibernate 2.1的任何未来版本都只包含错误修复。我们一直称之为2.2的分支实际上将作为版本3发布。
Hibernate项目之前的目标相当有限 - 我们限制了自己只考虑下一个版本,以及我们的用户现在正在要求什么。这种方法最终走到了尽头。Hibernate3怀有傲慢的雄心,目标是在我们的用户要求或甚至想到的范围内进行创新。我们当然会添加一些功能,这些功能远远超出了最佳商业ORM解决方案(如TopLink)的功能。(其中一些功能对于处理某些特定类型问题的用户来说将非常有意思。)
因此,在这种傲慢的精神下,我将放弃通常不吹嘘我们尚未实现的事情的政策,并简要介绍一些计划用于Hibernate3 alpha的事情(就像往常一样,我们对未来版本的发布日期不作出承诺)。
虚拟化
通过向用户呈现一定范围的过滤数据,可能会解决许多重要且有趣的问题。例如,用户可能希望查看特定时间点的数据,或者可能只有权查看特定区域的数据。Hibernate 3将允许(参数化的)过滤器在会话级别应用,而不是强迫应用程序代码在繁琐的查询条件中指定此过滤标准。
(此功能尚未实现,尽管一些必要的重构目前正放在我的笔记本电脑上。)
更多映射灵活性
对于绝大多数情况,Hibernate2提供了您所需的全部O/R映射选项。然而,现在有一些新功能非常强大,如果使用得当。
- 使用<join>
- table-per-concrete-class-mappings 使用<union-subclass>
- 使用SQL公式映射的灵活判别器
我们长期以来一直主张极其细粒度的类别,并将多表映射视为一个巨大的缺陷(除了非常重要的特殊案例:按子类继承映射的每个表)。不幸的是,一些商业供应商坚持试图将这个缺陷吹捧为其实际的竞争优势,这让我们感到非常烦恼。因此,我们提供了<join>映射主要是为了让他们闭嘴。
好吧,我承认确实有一个非常棒的用例<join>。现在,我们可以在同一继承层次结构中混合使用按层次结构和按子类映射。例如
<class name="Document" table="DOCUMENTS"> <id name="id">...</id> <discriminator column="TYPE" type="string"/> ... <subclass name="Book" discriminator-value="BOOK"/> <subclass name="Newspaper" discriminator-value="NEWSPAPER"/> <subclass name="XML" discriminator-value="XML"> <join table="XML_DOCUMENTS"> .... </join> </subclass> </class>
Hibernate的隐式多态是一种很好的方式,可以在不强制不同表的列类型不自然限制的情况下实现表按具体类的大部分功能。它还很好地允许将表按具体类与其他继承映射策略在同一继承层次结构中混合。不幸的是,它有两个限制
- 我们不能有超类类型的多态集合
- 针对超类的查询解析为多个SQL查询,这可能比使用SQL UNION慢。
新的<union-subclass>结构提供了一个将类映射到表按具体类模型的方法,并使用SQL UNION实现。
如果你的表按类层次结构映射没有具有简单判别列的很好特性,其中值一对一映射到不同的类,那么公式判别器就是为你准备的。现在,你可以使用多个列、同一列的多个值、任意的SQL表达式或函数、甚至子查询来区分子类!以下是一个示例
<class name="Document" table="DOCUMENTS"> <id name="id">...</id> <discriminator type="string" formula="case when TYPE='MONOGRAPH' then 'BOOK' when type='TABLOID'\ or type='BROADSHEET' then 'NEWSPAPER' else TYPE end"/> <property name="type" column="TYPE"/> .... <subclass name="Book" discriminator-value="BOOK"/> <subclass name="Newspaper" discriminator-value="NEWSPAPER"/> </class>
所有这些功能都已实现于Hibernate3分支。
表示独立性
Hibernate原本是为了POJO域模型提供持久化解决方案,这一点仍然是重点。偶尔,我们会遇到一些人希望以更动态的方式表示他们的持久化实体,本质上是一个映射。我们以前会向他们推荐OFBiz Entity Engine。然而,Hibernate3允许你将域模型表示为HashMap的树,或者,通过一点用户编写的代码,几乎可以表示任何东西。以下是这个新功能的三种直接应用
- 在Hibernate之上重新实现JBoss CMP 2.1引擎
- 使用Hibernate重新实现OFBiz EntityEngine
- 本地持久化SDOs
实际上,最终目标是允许应用程序在执行任务时切换到适当的表示;相同的实体可能表示为类型安全的POJO、Map或SDO,所有这些只需要一个Hibernate映射。
这个功能已在Hibernate3分支中实现。
JDK 1.5支持
JSR 175注解非常适合Hibernate元数据,我们将积极采用它们。Emmanuel Bernard正在从事这项工作。
我们还需要支持Java泛型,这基本上归结为允许类型安全的集合(这非常简单)。
存储过程支持
我们并不是非常喜欢在CRUD操作中使用存储过程(当然,还有其他一些用例中SP非常合适)但是与遗留数据库打交道的人经常需要Hibernate调用一个SP而不是生成它自己的SQL语句。在Hibernate 2.1中,可以通过自定义持久化器来实现这一点。Hibernate3将允许在映射文档中指定任意的SQL语句以用于创建、更新和删除。Max Andersen目前正在实现这个功能。
完全事件驱动设计
在项目初期,我积极应用YAGNI原则。Hibernate偶尔因缺乏明确的前期设计而被批评存在所谓的架构
缺陷。(在我看来,这种缺乏前期设计实际上并不是一个架构问题——至少按照我对“架构”这个词的理解——但这场辩论留待他日再谈……)无论如何,我坚信优雅且灵活的设计最终会自然发展,而且我们有限的注意力和开发资源最好用于解决真正的用户可见问题。现在,我对这个决定感到非常满意;Hibernate已经繁荣发展,出现的设计既强大又相当优雅。YAGNI的怀疑者们,接受挑战吧!
这个过程最后一步现在落在Steve Ebersole手中(并且已经部分实现)。Hibernate3将采用事件驱动的架构,事件对象代表发生的任何有趣的事
,而监听器类则实现标准的Hibernate行为,或者自定义的用户定义行为。这种用户扩展功能在审计到实现奇特
级联语义方面都有应用。(我们之前尝试解决这些问题的方法——拦截器接口——证明是不够的。)重要的是,重新设计简化了当前的单一SessionImpl类。
新的AST驱动的查询解析器
实际上,还有两个最后步骤。(没有人预料到西班牙宗教审判所,对吧?)
当我编写HQL解析器时,我对解析器一无所知。Hibernate最终使用了一个奇特的纯滞后解析器,令我惊讶的是,它实际上为我们服务得很好,并且很少出现错误。(YAGNI,又一次。)但现在,我们确实需要有一个基于ANTLR语法的AST的“正规”语法,Joshua Davis目前正在编写一个。这可能在用户可见的方面不会有太大变化,但它将使我们更容易支持其他查询语言,如EJBQL。
声明式会话管理
目前这是一个针对JBoss的特定功能。新用户常常发现会话管理一开始很棘手。理解Hibernate会话扮演的双重角色(一方面是缓存
,另一方面是连接
)需要一些思考。JDBC连接是无状态的;而Hibernate会话则是一个有状态的连接!我们通过告诉人们,实际上会话代表的是一种事务
,进一步混淆了这个问题。
与其强迫人们实现自己的会话处理,与threadlocal和异常处理打交道,不如有一种方式可以声明性地指定会话模型(会话与数据库事务或应用程序事务一一对应),在会话bean的元数据中。Spring框架已经为此类方法提供了一些支持。Michael Gloegl正在使用JBoss拦截器实现这一功能。如果这个功能不仅限于JBoss的应用服务器,那将是件好事。J2EE需要为EJB提供可移植的拦截器(它们已经为servlets提供了)。
我的清单上还有一些其他事情,但暂时就到这里吧!