Hibernate 和 JPA 出了什么问题 已翻译 100%

GuoMengyue 投递于 2014/08/22 09:50 (共 15 段, 翻译完成于 08-27)
阅读 10554
收藏 46
3
加载中

对于关系型数据的持久化,Hibernate当然是市场上最流行的解决方案。它已有数年的历史了,而且有数以千计的项目用到了它。最新版本的Hibernate甚至遵循了Sun的Java持久化API(JPA)规范。所以,既然Hibernate可以完成所有事,为什么还要寻找其他方案呢?

我们认为Hibernate与JPA根本没有它们看起来得那么完美。我们将列举几个对Hibernate误解的原因来解释它为什么没有想像中的那么好。

pseudo
翻译于 2014/08/22 22:15
3

数据模型定义:元数据的问题

所有关系型数据持久化解决方案都需要理解底层数据模型才能工作。Hibernate提供了两种定义数据模型的方式:XML映射文件和注解。注解是最近引入的以用于简化对象关系映射,而且注解比XML映射有很多优点。因此我们将不再考虑XML映射转而关注于注解。但是本文中与注解相关的所有内容都可以用相似的方式通过xml映射实现。

pseudo
翻译于 2014/08/22 22:29
2

提供给Hibernate的元数据用于告知其保存对象到数据库中的位置和方式。这些信息可能不只用于Hibernate也要用于应用程序逻辑,所以你可能希望从代码中访问它而不是再次提供冗余的信息。一个适当的例子是某一文本域的长度,或者某个域是否是强制性的。例如在一个用户接口上,你可能需要这个信息用于展示一个表单输入控件或用于验证。

pseudo
翻译于 2014/08/22 22:50
2

通过注解,你可以从代码中访问这个信息,但一个特别注解的属性不能像普通java对象的属性那样被直接引用。以下是一个示例:

这是使用注解访问元数据的方式:

Method field = Employee.class.getDeclaredMethod( "getFirstname", new Class[0] );
javax.persistence.Column col = field.getAnnotation( javax.persistence.Column.class );
int length = col.length();

这段代码与使用Empire-db的对象模型架构定义效果相同:

int length = mydb.EMPLOYEES.FIRSTNAME.getSize();

(注意EMPLOYEES和FIRSTNAME都是public final修饰的大写的成员变量,但也可以通过getter进行访问——具体访问方式取决于你的代码和决定)。

pseudo
翻译于 2014/08/22 23:04
3

注解也有问题的原因不仅在于编译时安全的缺失与用户端代码的复杂性,它也有很多其他问题。持久化注解所提供的元数据通常并不充足。因此你需要像 Hibernate Validator 提供的附加的注解,或你可能想自定义注解,而这些会使你的映射与访问代码更加难以阅读和管理。更不用说在运行时改变你的数据模型是不可能的了。为元数据使用java对象模型要比以上这些方式都简单易行。既然不用注解可以做得更好那为什么还非要使用它呢?

pseudo
翻译于 2014/08/24 12:18
2

我们认为注解不应该为应用逻辑提供可能用到的信息。注解应该只用于为代码提供编译器优化或代码文档这类具体信息。像@Deprecated 和@SuppressWarnings这类注解是可接受的。尽管注解较XML映射文件能更好地与java代码相结合,但它们的灵活性比普通的interface和类要差得很远。注解现在是很新酷的,但用得越广泛你的代码将会被污染得越严重。不要让该死的注解重蹈该死的XML配置的覆辙。

pseudo
翻译于 2014/08/24 12:35
1

数据对象定义:泛滥的getter setter

除了元数据,我们也需要在某地存取我们自己的数据。对于Hibernate和JPA这个地方就是(装备了与相应表各字段对应的成员变量及它的getter setter方法的)JavaBean或POJO。对于大型数据模型这也就意味着很多行的代码。Hibernate工具可以通过逆向工程自动生成这些代码。但对于大型成熟的工程你可能会遇到这样的问题:一旦你手动更改了bean或映射代码——并且你希望保存这些改动——自动化工具就出问题了。所以通常这些代码(包括元数据)是通过手工维护的。更糟糕的是,因为这些对象通常用作填充业务对象的DTO(数据传送对象),你可能会看到用于Java对象间复制属性值的无数行代码。所以最好要把这些getter和setter放到哪里呢?

pseudo
翻译于 2014/08/24 12:59
1

Empire-db的动态bean对于每个实现的实体都只有一个通用的getter和setter方法。我们仍推荐为每个数据库实体创建一个单独的数据对象类,这样类的总数没有变——虽然当使用一个通用DBRecord对象时这是没必要的, 但我们推荐这样做有两个原因:首先是为了类型安全,因为你希望你的代码依赖于特定的实体。其次,因为随着项目的增长,你很可能需要重写已经存在的方法并实现的新方法。尽管是这样,但因为少了这些成员变量和它们对应的getter setter方法,你会有相当少的代码需要维护。另外,如果有必要或为了简便,你可能要为某字段添加特殊的getter setter。

pseudo
翻译于 2014/08/24 15:24
1

动态查询: 查询的困境

对于一个关系型数据库,我们希望它可以友好地支持动态查询,接下来我们看下Hibernate是如何处理动态查询的。 Hibernate提供两种方法: Hibernate查询语言(HQL)和标准的API。 HQL是Hibernate自己的语言,你必须先学习如何使用它。 它可以视为一个支持java编码映射的SQL方言。 当你尝试编译一个复杂的、带有约束和连接的、有条件查询语句时,就会发生问题,因为HQL是由不安全的字符串常量拼接成的。 我们认为在一些复杂度比较高的场景,使用HQL编写的代码会变得难以维护。 此时,标准的API是更好的选择,但是这种方法有缺点:灵活性比较低。

daxiang
翻译于 2014/08/26 08:44
1

但这又出现了另一个问题:通常的编程任务是需要从一个或多个表中选出几个字段的集合或计算结果。查询结果可能用于展示给某一用户,或用于做其他处理。

对此Hibernate的HQL提供了可定义的select子句,原理是提供了一个特殊的结果bean,它持有你所需要的数据,甚至通过string拼接和数值运算这类SQL函数对其进行转换。但奇怪的这个功能在项目中几乎没有被用到过,人们转而使用全实体bean(full entity bean),这也就意味着从数据库中加载了很多没用的属性。对于实体间关系的解析,Hibernate既可以使用join(饿汉式)加载所有引用到的实体,也可以启用懒汉模式——查询每个引用到的对象——有时甚至只为了一个简单的属性。所以假设对于有5个属性的结果bean,实际上该对象不是持有5个属性,而是5个对象共加载了超过50个属性。很明显这不是你所期望的完美方案。

pseudo
翻译于 2014/08/26 11:55
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(38)

骑着毛驴遛狗
骑着毛驴遛狗
ORM 的世界,mybatis结合auto generate code 非常完美
水牛叔叔
水牛叔叔

引用来自“wangle85”的评论

Hibernate框架本身的复杂性有很多时候超过了业务本身
无能的悲伤
无能的悲伤
我还是那句话,数据非对象
零下三度
零下三度
hibernate是什么?没用过,只会jdbc。
蛋看江湖
蛋看江湖
建议抛弃hibernate 的多表关联能力,只做单表映射,复杂sql 自己拼接吧 ,当然你也可以换成mybatis sql 与编程分离的方式优雅简洁,借助自动生成dao层的插件 开发效率毫不低下
snail_pang
snail_pang
用mybatis的飘过。身边有不少人用spring data jpa,确实很好用。
sfmjp
sfmjp

引用来自“糖咖啡”的评论

用hibernate搞报表 完全崩溃。。。
确实赞成。不过,这种情况还是用sql吧,哈哈
程序员Joe
程序员Joe
作为一个4年+经验的菜比程序员,我觉得hibernate jpa最麻烦的是在做表关联的时候。jpa的查询条件还需要用spec....选择器,简直头大
zfc827
zfc827

引用来自“zfc827”的评论

作者对Hibernate的理解和认识应该是多年前的眼光,Hibernate本身无疑是强大的,几乎没有人否认过,当然也存在着各种的不足。可是Java世界之所以这么健壮(也可以说是“复杂”),就是因为从来不缺乏解决方案。

1、元数据的问题
传统的ORM架构设计,无非就是XML和注解的选择,基本上脱离不了这个,但是约定大于配置的理念出现后,Hibernate也有做了部分的改进,包括支持JPA注解,默认的字段关联,按照约定的方式使得代码中的注解数量大大减少。

2、泛滥的getter setter
这确实是一个Java中让人恶心和诟病的问题,但是硬要扯到Hibernate身上也很牵强,我现在使用Lombok,可以只写属性,所有的getter setter和基本的equals、hash、toString都在编译时动态生成好,源代码会看起来非常简洁。

引用来自“zfc827”的评论

3、查询的困境 其实我现在使用Hibernate仅仅只是作为JPA的实现了,代码中也没有用到Hibernate的HQL以及Criteria API,自从Spring Data JPA项目诞生后,将接口改为JPA的方式就成了顺理成章的事情,这些我想用过Spring Data JPA的同学都有体会。 另外,查询的问题我也赞同文中的观点,使用HQL,JPQL等等查询语句会让代码变的难以维护,更重要的是让代码变得不那么优雅。
在这一点上,JPA的Criteria API对Hibernate进行了优化和设计上的改良,包括文中提到的编译期安全性的问题,JPA使用Metadata替换了Hibernate中字符串化的属性,解决了编译期类型安全等等问题。当然JPA优化过的 Criteria 接口会让适应了 Hibernate 方式的人有些不适应,并且增加了学习成本,所以我基本上是不用的,还是上面那句话Java世界之所以这么健壮(也可以说是“复杂”),就是因为从来不缺乏解决方案。 这个查询API的另一个解决方案就是Querydsl,它是一个针对Java语言的通用的查询API,屏蔽了各个数据源之间API风格的差异,以一种更紧凑,安全的,易于理解的方式来组织查询,更棒的是Spring Data也提供对这个API的支持。 Querydsl目前支持的数据源和衍生技术包括:JPA,JDO,SQL,Mongodb,Lucene,以及Java Collections。
zfc827
zfc827

引用来自“zfc827”的评论

作者对Hibernate的理解和认识应该是多年前的眼光,Hibernate本身无疑是强大的,几乎没有人否认过,当然也存在着各种的不足。可是Java世界之所以这么健壮(也可以说是“复杂”),就是因为从来不缺乏解决方案。

1、元数据的问题
传统的ORM架构设计,无非就是XML和注解的选择,基本上脱离不了这个,但是约定大于配置的理念出现后,Hibernate也有做了部分的改进,包括支持JPA注解,默认的字段关联,按照约定的方式使得代码中的注解数量大大减少。

2、泛滥的getter setter
这确实是一个Java中让人恶心和诟病的问题,但是硬要扯到Hibernate身上也很牵强,我现在使用Lombok,可以只写属性,所有的getter setter和基本的equals、hash、toString都在编译时动态生成好,源代码会看起来非常简洁。
3、查询的困境 其实我现在使用Hibernate仅仅只是作为JPA的实现了,代码中也没有用到Hibernate的HQL以及Criteria API,自从Spring Data JPA项目诞生后,将接口改为JPA的方式就成了顺理成章的事情,这些我想用过Spring Data JPA的同学都有体会。 另外,查询的问题我也赞同文中的观点,使用HQL,JPQL等等查询语句会让代码变的难以维护,更重要的是让代码变得不那么优雅。
返回顶部
顶部