背景
O/R
映射技术的出场顺序:
1997
年
-1998
年:
TopLink
,
CocoBase
,
ODMG
1999
年
-2001
年:
Entity Bean
,
JDO
2002
年
-2003
年:
TopLink
,
Hibernate
,
iBatis
数据库层
2004
年:
JDO
稳步发展;
Hibernate
飞黄腾达
功能
以下出自Rod Johnson《J2EE设计开发编程指南》
使用EJB2.0
实体进行O/R
建模的限制:
尽管引进了各种重要增强,但根据制定,
CMP
实体组件依然是
O/R
映射的一个基本形式。
EJB
规范忽略了
O/R
映射方面最难办的部分问题,并且使利用关系数据库的部分能力变得没有可能。例如:
1.
不支持开放式加锁
2.
对批量更新的支持极差(
EJB2.0
主方法至少使它们有可能,但容器——和
EJB QL
——在实现它们方面没有提供任何帮助)
3.
从一个对象到单个表的映射概念是有限的,而且
EJB2.0
规范没有建议
EJB
容器应该如何解决这个问题。
4.
不支持被映射对象中的继承性。像
WebSphere
那样的有些
EJB
容器把这实现为一个专有的扩展。
在现实经验方面,实体组件远远落后于那些最好的
O/R
映射框架。不过,
O/R
映射框架一直是很昂贵的产品(尽管这种状况在
2002
年似乎正在发生改变。)据笔者所知,目前还没有任何开源产品提供一个企业强度的
O/R
映射解决方案(注:不过现在有
Hibernate
了)。
以下出自Rod Johnson《J2EE without EJB》
如果出现下列征兆,就可以考虑使用
O/R
映射:
1.
针对领域对象的“加载
/
编辑
/
存储”流程,例如先加载一条产品记录,对其进行修改。
2.
对象以批量查询的方式取出,但更新和删除则是单独进行的
3.
大量对象需要积极的缓存(通常出现在“读操作远多于写操作”的情况下,如
Web
应用)
4.
在领域对象与数据库表
/
字段之间有一个相当自然的对应关系。
5.
不需要对
SQL
进行特别的优化。在大多数时候,好的
O/R
映射解决方案可以把生成的
SQL
优化得相当好,例如
Hibernate
的“方言”(
dialect
)支持;但一些特殊的
SQL
优化只有在完全关系型的方式下才可能进行。
O/R
映射两大好处:
1.
可以不必编写重复的
JDBC
代码处理领域对象的实例(意味着编写的代码少,缩短开发周期;进一步意味着维护的代码少,意味着容易维护)
2.
透明持久化:完善的
O/R
映射工具可以在事务提交时自动将修改后的数据持久化到数据库。正如前面所说,只有真正被修改的数据才会被提交,应用代码无需进行任何脏数据检查。
Entity Bean
的败笔(指的是
EJB2.0
)
1.
EJB QL
(
CMP
查询语言——注:与之对比的是
Hibernate
的
HQL
)的能力非常有限,迫使开发者不得不编写
SQL
语句或者依赖于应用服务器厂商专有的扩展功能。
2.
Entity Bean
的性能经常很糟糕,因为组件管理和方法拦截造成了巨大的开销,尤其是在处理大结果集时更加明显。只有对于大量缓存的对象,它的性能还算差强人意。
EJB2.1
中的
Entity Bean
终于开始走向一个可用的
O/R
映射解决方案了,但也仅仅是“很多应用可以用它来实现”而已,还根本无法与那些更易用、更强大的持久化技术相匹配。由于
Entity Bean
并不是用于持久化细颗粒度领域对象的合适方案。
以下出自《Pro Hibernate》
为什么不用
EJB
来存储,显示,查询数据库中的数据呢?严格的说,
EJB
服务器支持两种类型的持久化,就是
BEAN
管理的持久化(
BMP
)和容器管理的持久化(
CMP
)。在
BMP
中,
Bean
自己负责执行所有的
SQL
语句来完成存储和查询数据。换句话说,我们自己要去编写
JDBC
逻辑代码。另一方面,
CMP
是由容器来执行存储和检索
bean
数据的工作。
我们这里不选择
EJB
的原因如下:
1 CMP
实体
bean
需要和数据表一对一的映射
2
它们很慢
3
有时候要人工参与的去决定哪一个
bean
字段对应表的哪一列
4
它们对方法命名有要求
5 EJB
的容器是重量级的
6
它们和容器依赖强,不容易移植
下面看看
Hibernate
的特点:
1
不需要强制映射一个
POJO
到一个表,不强制一对一的关系
2
尽快启动并加载它的配置文件时会对性能有些负载,但总的来说,它是很快的工具
3
和容器没有强依赖,很方便的移植
4
可以很轻松的处理
serializable POJOs
以下出自网上评论
entity bean
是我见过的
O/R mapping
中最差的一个。它对实体的描述能力太弱(实际上不支持继承),对实体对象的限制太多(要求继承接口),查询能力太弱(不支持动态查询,更不支持非对象查询)。现在就连
Sun
的人都已经不推荐用
entity bean
了,他们用
JDO
。在
O/R mapping
这里,
Hibernate
和
Castor
都比
entity bean
要好。
性能
以下出自Rod Johnson《J2EE设计开发编程指南》
实体组件性能主要取决于
EJB
容器的实体组件高速缓存策略,而高速缓存又取决于该容器所运用的加锁策略。
使用实体组件可能会产生比主流的持久性替代方法更糟糕的性能,除非你的应用服务器有一个有效的分布式和事务性实体组件高速缓存器,否则开发人员需要把大量的精力投入到多部署上。在后一种情况中,性能将由应用的性质来决定:①对数据主要进行只读访问的应用将会运转得很好,而
②
对数据主要进行写访问的应用从高速缓存中将得不到什么好处。
这为什么重要呢?因为没有高效的高速缓存,实体组件性能可能会非常差。
来自实体组件模型的高效性能取决于下列条件:
1.
数据在被访问时可能被修改。除了只读实体的专有支持外,实体组件采用一个读—修改—写模型。
2.
修改发生在个别的被映射对象级别上,而不是作为一个集合操作(也就是说,更新可以利用
Java
中的个别对象来有效地完成,而不是对一个
RDBMS
中的多个元组进行更新)
实体组件在许多情况下为什么有性能问题呢?
1.
实体组件使用一种“以一概全”的方法。正如我们使用
RDBMS
时所见过的,实体组件抽象可能会使有效地访问持久性数据变得不可能。
2.
实体组件约定是严格的,进而使编写有效的
BMP
代码变得不可能。
3.
调整实体组件数据存取是很困难的,无论我们使用
BMP
还是
CMP
。
4.
实体组件具有相当大的运行时开销,即便使用本地而不是远程接口。(一个实体组件将始终比普通
Java
类有一个大得多的开销)
5.
在实践中,实体组件性能常常会下降到
O/R
映射性能,而且不保证一个
J2EE
应用服务器供应商在这个领域内拥有很强的专门技能。
实体组件运行效率特别差,而且由于大型结果集的缘故而耗用过多的资源,尤其在结果集(比如搜索结果)没有被用户修改的时候。实体组件对总是在个别记录级别上被修改的数据有最佳的运行效率。
BMP
中蕴含的性能问题:
n+1
查询发现者问题
问题本质:
BMP
实体的约定要求开发人员实现发现者来返回实体组件的主键,而不是返回实体。
例子:使用
SQL
(这就是所谓的
n+1
查询发现者问题中那个
1
)查询
5000
个
User
,返回后,
EJB
容器会创建或重用
5000
个
User
实体,然后根据每个主键用来自
5000
个独立查询(当然查询是使用
select
语句了,这就是所谓的
n+1
查询发现者问题中那个
n
)的数据填充这些
User
实体。这就意味着总共有
n+1
条
select
语句了。
复杂性
EJB2.0需要3个Java文件两个描述文件才能搞定,而Hibernate则只需要1个Java文件和一个描述文件,如果数据库中有1000张表要映射成EJB实体和Hibernate的POJO,那么就是5000个文件和2000个文件的差别,那如果需要1万个EJB实体,就意味着是5万和2万的差别了。
结论
总而言之,
EJB
实体
bean
(就是
J2EE
的
O/R Mapping
方案)实现的功能只是
Hibernate
实现功能的一个子集,而且采用了笨拙和低效的方法实现的,而且复杂性远高于
Hibernate
(开发,部署,维护),此外其可测试性方面和
Hibernate
也没法相比。
参考资料
2. Rod Johnson的两部巨著《J2EE设计开发编程指南》和《J2EE without EJB》