Hibernate 差点毁了我的职业生涯
已发表: 2022-03-11想象一下,您是一名 Java 开发人员,并且您即将开始您的下一个大项目。 您需要做出在项目的其余部分中将与您保持一致的基本决策。 您想为灵活的数据模型选择最佳的面向对象抽象,因为您不想处理简单的 SQL。 您希望支持各种数据,理想情况下,支持各种数据库。
显而易见的答案是只使用Hibernate ,对吗? 90% 的 Java 开发人员会同意你的观点,但这是否是正确的决定?
让我们看看如果仅仅因为它是公认的标准而盲目使用Hibernate会出现什么问题。
考虑一下 Java 开发人员 Monica。 Monica 最近被提升为架构师,现在负责为她公司的新产品布置技术堆栈。 她知道在 Java 世界中只有一种处理数据库通信的好工具: Hibernate 。 Hibernate是一个众所周知且受支持的 JPA 标准。 但是,在开始项目之前检查一些事情总是一个好主意。 幸运的是,她的同事 Ben 认识合适的人。
休眠听起来像一颗银弹
本- 你好莫妮卡,我想介绍约翰。 他是Hibernate专家,他会帮助你。
莫妮卡- 嘿约翰,很高兴你有时间陪我。 所以,我们正在构建我们的下一件大事,你知道的。 我们正计划成为下一个 Facebook 或 Google。 忙碌的日子。 这将是巨大的。 绝对精彩! 每个人都很兴奋! 我已经被提升为架构师的角色,所以现在我必须选择我们将使用的堆栈。 唯一缺少的部分是坚持……
约翰-冬眠!
莫妮卡——是的! 确切地! 正是我的想法! 对我们来说,这似乎是一个完美的匹配和真正的交易。 针对真正企业问题的真正企业解决方案,经市场证明且历史悠久。 我听到了很多关于它的积极经验。 但是,我对我们的一位队友有意见; 他完全反对。 他对数据库非常了解,他害怕在我们的应用程序和数据库之间添加另一层。 他非常聪明,我需要一些非常好的论据来说服他这是一个很好的决定。 你能帮我解决这个问题吗?
约翰——当然! 我会很高兴的。 Hibernate确实是一个出色的工具。 它广泛用于大型、真正的企业解决方案,如银行。 你不会错的。 考虑持久性:选择Hibernate 。 如果您使用 Java 编写,这绝对是正确的选择,而且您还有其他语言的端口。 看看有多少职位描述需要它!
莫妮卡——我完全同意! 我对此也有同样的感受。 在之前的项目中,我们主要通过普通的旧 JDBC 使用 SQL。 荒谬的! 我知道! 但是,事情是这样的:我们团队中有非常聪明的 SQL 人员,当他们看到Hibernate生成的 SQL 时,他们很紧张。 它看起来丑陋且难以阅读。 这会是未来的问题吗?
约翰- 看。 DBA 人有不同的看法。 他们害怕Hibernate ,因为它似乎取代了他们在项目中的角色。 此外,数据库具有内置的查询优化器,因此您无需担心这些查询的实际外观。 数据库将为您优化它。 这都是关于快速开发的,这是 SQL 做不到的。
莫妮卡——真的吗?! 不再处理 SQL? 惊人的! 上次 DBA 花了数周时间尝试优化某些查询。 周! 哦,告诉你这个我很尴尬,但你知道我们使用的是……存储过程(笑)。 哦,真是一团糟。 你能相信这个项目还在使用它吗? 我为外面的人感到非常抱歉。 他们仍然必须一遍又一遍地编写这个乏味的代码。 我想知道它是否仍然是 Java 或 SQL 项目?
John - 这正是面向对象方法和关系方法之间的区别。 这就是所谓的面向对象的阻抗失配。 Hibernate可以弥补这一差距。 开发人员可以专注于构建业务逻辑。 推送功能使利益相关者和整个管理层感到高兴。 做最重要的事情:做生意! 许多样板代码将消失,您将在逻辑和数据之间建立一种神奇的、看不见的但可靠的连接。
莫妮卡——相互合作。 充分协同。 就像数据库从一开始就是语言的一部分一样。 我很高兴能成为这种信仰技术飞跃的领导者。 这就像软件跋涉中的翘曲速度。
约翰- 是的! 你明白了!
莫妮卡- 哦,天哪,我太兴奋了! 谢谢你,约翰! 我准备好了!
不灵活的解决方案带来的痛苦
Monica - Hey John,还记得我们去年谈到的那个项目吗?
约翰——当然。 怎么样了?
莫妮卡——我们很快就要生产了。 一切都很好,但出现了一些问题。
约翰- 当然,打我。
Monica - 好吧,我们不能再从头开始生成我们的数据库模式。 在不丢失数据的情况下支持架构更改的最佳方式是什么?
John - 首先, Hibernate不打算用作生产迁移工具。 使用 FlywayDB 或 Liquibase 之类的东西。 这很简单。 您编写迁移脚本,然后更新实体模型以及Hibernate映射,使其与实际数据库结构保持同步。
莫妮卡——嗯,我明白了。 我们在之前的项目中只使用了普通的 SQL 迁移。
约翰- 那也很好。 只要您保持实体模型和模式同步,就可以按照您的喜好进行操作。
莫妮卡——我明白了。 还有一件事。 我们一直在为懒惰/急切的获取问题而苦苦挣扎。 有一次,我们决定急切地做所有事情,但这似乎不是最理想的,此外,有时由于没有会话或类似的原因,无法访问某些字段。 这正常吗?
John - 您需要了解更多有关Hibernate的信息。 从数据库映射并不简单。 基本上,有多种方法可以做到这一点。 您只需要选择一种适合您的方式。 延迟获取使您能够按需加载这些对象,但您需要在活动会话中进行操作。
Monica - 我们仍在为最终部署使用哪个数据库引擎而苦苦挣扎。 我认为Hibernate是可移植的,但我们有一些使用 MS SQL 魔法的本机查询,我们实际上希望在生产中使用 MySQL。
John - 只要您使用分离的标准或 HQL, Hibernate就会为您提供灵活性; 任何本机查询只会将您的解决方案绑定到数据库。
Monica - 看来我们必须坚持使用 MS SQL。 最后一个问题:我的队友说HQL中没有“limit”关键字。 我以为他在开玩笑,但我也找不到。 对不起这个愚蠢的问题......
John - 事实上,HQL 中没有“限制”关键字。 您可以通过查询对象来控制它,因为它是特定于数据库供应商的。
Monica - 所有其他元素都在 HQL 中,这似乎很奇怪。 没关系。 谢谢你的时间!
我们现在再次在 SQL 中一起破解解决方案
Monica - John,一开始我们不打算处理 SQL,但现在看来我们必须处理。 我们的需求在增长,似乎没有办法绕过它。 感觉不对,但我们已经开始每天再次使用 SQL。

约翰——嗯,没错。 您不必在一开始就专注于数据库。 但是,随着项目的发展,最好使用 SQL 并进行性能优化。
Monica - 有时我们会花费数天时间寻找错误。 似乎我们必须分析Hibernate生成的 SQL,因为我们不知道为什么它没有按预期工作并且会产生意想不到的结果。 我们遇到了一些在Hibernate错误跟踪器中众所周知的问题。 此外,很难在保持实体模型同步的同时编写正确的迁移。 这很耗时,因为我们需要了解很多有关Hibernate内部结构的知识并预测它的工作原理。
约翰- 总有一条学习曲线。 你不必写太多,但你需要知道它是如何工作的。
Monica - 使用更大的数据集也很烦人。 最近,我们对数据库进行了大规模导入,而且速度非常缓慢。 然后我们发现我们必须清除会话以使其更快。 即便如此,它仍然慢得多,所以我们决定将其重写为普通的 SQL 语句。 有趣的是,编写纯 SQL 实际上是最快的方法,所以我们决定将其作为最后的选择。
John - Import 不是一个面向对象的过程。 Hibernate专注于面向对象的设计。 请记住,您始终可以使用本机查询。
Monica - 你能帮我理解Hibernate缓存是如何工作的吗? 我只是不明白。 有一些一级/二级缓存。 这是怎么回事?
约翰——当然。 它是持久数据的所谓事务级缓存。 可以逐个类和逐个集合地配置集群或 JVM 级缓存。 您甚至可以插入集群缓存。 但请记住,缓存不知道其他应用程序对持久存储所做的任何更改。 但是,它们可以配置为定期删除过期的缓存数据。
莫妮卡- 对不起,我觉得我今天过得很糟糕。 你能再解释一下吗?
约翰——当然。 每当您将对象传递给save
、 update
、 saveOrUpdate
或通过load
、 get
、 list
、 iterate
或scroll
检索时,该对象都会添加到会话的内部缓存中。 您还可以从一级缓存中删除对象及其集合。
莫妮卡——呃……
John - 此外,您可以控制缓存模式。 您可以使用normal
模式读取和写入项目到二级缓存。 使用get
模式从第二级读取,但不能回写。 使用put
,它与get
相同,但您不能从第二级读取。 您还可以使用refresh
模式,该模式将写入二级缓存,但不从二级缓存中读取,并绕过use minimal puts
属性,强制刷新从数据库读取的所有项目的二级缓存。
莫妮卡——我明白了。 好的。 让我想想这个。 哦,时间不早了,我得走了。 谢谢你的时间!
约翰-不客气!
放弃休眠
Monica - John,我以为我们正在进入一个软件开发的新时代。 我以为我们在做光年的跳跃。 但是,四年后,我们似乎仍在处理所有相同的问题,只是从不同的角度。 我必须学习Hibernate架构、配置、日志记录、命名策略、元组器、实体名称解析器、增强的标识符生成器、标识符生成器优化、联合子类、XDoclet 标记、与索引集合的双向关联、三元关联、idbag、将隐式多态性与其他继承映射、在两个不同的数据存储之间复制对象、分离对象和自动版本控制、连接释放模式、无状态会话接口、集合持久性分类、缓存级别、惰性或急切获取等等。 即使我所知道的一切,我们似乎都失败了。 这是一场软件惨败! 最终失败! 灾难! 世界末日!
约翰- 等等! 发生了什么?
莫妮卡——我们走到了死胡同。 我们的应用程序性能慢得离谱! 要获得报告,我们必须等待两天! 两天时间为客户实际生成仪表板。 这意味着我们每天都必须增加我们的计算尾部,而我们的仪表板变得越来越过时。 我们的 DBA 专家已经工作了两个月来优化一些查询,而我们的数据库结构却是一团糟。 有开发人员支持他,但问题是 DBA 正在思考 SQL,开发人员花费数天时间试图将其转换为独立的标准或 HQL 格式。 我们正在尝试尽可能多地使用原生 SQL,因为目前性能至关重要。 无论如何,我们不能做太多,因为数据库模式似乎是错误的。 从面向对象的角度来看,这感觉是对的,但从关系的角度来看,这似乎很荒谬。 我在问自己:这是怎么发生的? 开发人员告诉我们改变实体结构将是一项巨大的工作,所以我们负担不起。 我记得在之前的项目中它是一团糟,但我们从未在如此关键的时刻结束。 我们能够编写一个完全不同的应用程序来处理数据。 现在,修改这些生成的表是有风险的,因为很难确保实体模型始终正常运行。 这甚至还不是最糟糕的部分! 为了提高性能,我们不仅要解决数据库问题,还要解决数据库和应用程序之间的整个层的问题。 这是压倒性的! 我们有这些新人,你知道的,顾问。 他们试图提取数据,将其放入其他存储中,然后从外部执行计算。 这一切都花费了太多时间!
约翰——我不知道该说什么。
莫妮卡——你看约翰; 我不想怪你。 我选择Hibernate来解决所有这些问题,但现在我知道它不是灵丹妙药。 伤害已经造成,而且是不可逆转的。 实际上,我想问你一件事:在我职业生涯的最后四年里,我一直在处理Hibernate的东西。 我现在的公司似乎没有未来。 你能帮助我吗?
那么吸取的教训是什么?
约翰- 嘿,彼得,让我介绍一下莫妮卡。
彼得- 嘿,莫妮卡! 我们正在构建我们新的下一件大事,你知道。 这将是巨大的! 我们想像优步一样! 你知道也许如何坚持...
莫妮卡- 不是休眠!
包起来
Monica 是Hibernate专家。 然而,在这种情况下, Hibernate是一个错误的决定。 当她发现自己的解决方案变成了比原来更大的问题时,这就是对整个项目的最大威胁。
数据是应用程序的中心目的,无论喜欢与否,都会影响整个架构。 正如我们从这个故事中了解到的那样,不要仅仅因为您的 Java 应用程序正在使用数据库或因为社交证明而使用Hibernate 。 选择一个包含灵活性的解决方案。 健壮的 JDBC 包装器有很多选项,例如 JdbcTemplate 或 Fluent JDBC Wrapper。 或者,还有其他强大的解决方案,例如 jOOQ。