休眠:hbm2ddl.auto=生产中的更新?

Posted

技术标签:

【中文标题】休眠:hbm2ddl.auto=生产中的更新?【英文标题】:Hibernate: hbm2ddl.auto=update in production? 【发布时间】:2010-09-18 07:04:01 【问题描述】:

是否可以运行配置了hbm2ddl.auto=update 的 Hibernate 应用程序来更新生产环境中的数据库架构?

【问题讨论】:

我们做到了。从来没有任何问题。 非常好的问题。我现在面对它。所以现在是 2018 年 - 10 年后你的看法是什么?在具有复杂模式的重要客户生产数据库上使用 Hibernate 的更新是否安全? 【参考方案1】:

不,这不安全。

尽管 Hibernate 团队尽了最大的努力,但您根本无法依赖生产环境中的自动更新。编写您自己的补丁,与 DBA 一起审查,测试它们,然后手动应用它们。

理论上,如果 hbm2ddl update 在开发中工作,它也应该在生产中工作。但实际上,情况并非总是如此。

即使它运行良好,也可能不是最理想的。 DBA 的薪水这么高是有原因的。

【讨论】:

这是不安全的,因为应用的补丁可能会产生 hbm2ddl 难以预测的副作用(例如禁用为修改表而安装的触发器)。对于复杂的模式,最安全的方法是手动。回归后测试的自动排在第二位。所有恕我直言。 没什么。只是不要使用这个选项。 更新数据库模式也应由专业人员 (dbas) 处理。从坏的数据库更改中恢复充其量是困难的。 Vova 没有提到它——但是如果 hibernate 的更新决定删除一列并重新添加它,因为类型或大小发生了变化,会发生什么。假设该列是您所有用户的电子邮件地址? :-) 再见,再见公司.....您希望自动生成 DDL 更改 - 但您绝对希望人工检查更改。 升级前不备份数据库吗? Fwiw,目前 Hibernate 的架构更新不会删除表或列。【参考方案2】:

我不会冒险,因为您最终可能会丢失本应保留的数据。 hbm2ddl.auto=update 纯粹是一种让您的开发数据库保持最新的简单方法。

【讨论】:

升级前不备份数据库吗? 是的,我当然愿意,但是从备份中恢复需要做很多工作。当您还可以有条不紊地更新数据库时,恢复备份的麻烦是不值得的。【参考方案3】:

我们在生产环境中执行此操作,尽管使用的应用程序不是关键任务,并且员工中没有高薪 DBA。这只是少了一个受人为错误影响的手动过程——应用程序可以检测到差异并做正确的事情,而且您可能已经在各种开发和测试环境中对其进行了测试。

一个警告 - 在集群环境中,您可能希望避免它,因为多个应用程序可能同时出现并尝试修改可能不好的架构。或者加入某种机制,只允许一个实例更新架构。

【讨论】:

我们也在生产中使用它,类似的用例。不是关键任务的分析平台。我们已经在 4 个环境(4 年)中部署了 16000 次,而没有出现过多的休眠问题。我们是一个小团队,大部分都是 SQL RDBS 初学者,并且比我们自己更相信 Hibernate 处理模式。我想知道让 DBA 负责管理迁移和模式更改的员工的错误率是多少?在 ~16K 部署中它是否优于 0? 你对 pat 的这条评论有什么看法? ***.com/questions/221379/…【参考方案4】:

我同意弗拉基米尔的观点。如果我提出这样的课程,我公司的管理员肯定不会感激的。

此外,创建一个 SQL 脚本而不是盲目信任 Hibernate 使您有机会删除不再使用的字段。 Hibernate 不这样做。

而且我发现将生产模式与新模式进行比较可以让您更好地了解您在数据模型中所做的更改。你知道,当然,因为你做到了,但现在你一口气看到了所有的变化。甚至那些让你觉得“这到底是怎么回事?!”。

有一些工具可以为您制作架构增量,所以这甚至不是什么辛苦的工作。然后你就知道会发生什么了。

【讨论】:

“有一些工具可以为你创建一个模式增量”:你能指出一些这样的工具吗? 我认为apexsql.com/sql_tools_diff.asp 会这样做,可能还有更多应用程序。我通常通过转储模式和差异(使用差异)手动完成。【参考方案5】:

我会投反对票。 Hibernate 似乎不理解列的数据类型何时发生变化。示例(使用 mysql):

String with @Column(length=50)  ==> varchar(50)
changed to
String with @Column(length=100) ==> still varchar(50), not changed to varchar(100)

@Temporal(TemporalType.TIMESTAMP,TIME,DATE) will not update the DB columns if changed

可能还有其他示例,例如将 String 列的长度推高超过 255 并看到它转换为文本、中等文本等。

当然,我不认为真的有办法在不创建新列、复制数据和清除旧列的情况下“转换数据类型”。但是,当您的数据库中的列不能反映当前的 Hibernate 映射时,您的生活就非常危险......

Flyway 是解决这个问题的好选择:

http://flywaydb.org

【讨论】:

我刚刚尝试了您示例的第一部分 - 在我的情况下将 @Column(length = 45) 更改为 @Column(length = 255)。可以验证 Hibernate 4.3.6.Final 使用hbm2ddl.auto=update 正确更新了数据库架构。 (值得一提的是,数据库中目前没有任何数据 - 只有结构。) 很可能他们在过去大约 6 年左右的时间里修复了这个错误。但是,如果您确实在架构中有数据并进行了导致列宽减小的更改,您将遇到错误或非托管数据截断。 flywaydb需要手动编写的SQL脚本,应该比自动程序能做的更好,但谁会写一个大脚本,那就是问题了。【参考方案6】:

通常大型组织中的企业应用程序以较低的权限运行。

数据库用户名可能没有DDL 权限来添加hbm2ddl.auto=update 需要的列。

【讨论】:

这是我经常遇到的问题。我们尝试使用 hibernate 来创建初始数据库,但通常不可能这样做。 这不是“问题”,这是正确的事情。【参考方案7】:

应用程序的架构可能会随着时间而演变;如果您有多个安装,可能处于不同的版本,您应该有一些方法来确保您的应用程序、某种工具或脚本能够将架构和数据从一个版本逐步迁移到任何后续版本。

将所有持久性保留在 Hibernate 映射(或注释)中是控制模式演变的好方法。

您应该考虑架构演变有几个方面需要考虑:

    数据库模式的演变 添加更多列和表格

    删除旧的列、表和 关系

    用默认值填充新列

Hibernate 工具非常重要,尤其是在(根据我的经验)您在许多不同类型的数据库上拥有同一应用程序的不同版本的情况下。

第 3 点在您使用 Hibernate 时非常敏感,如果您引入新的布尔值属性或数字属性,如果 Hibernate 会在此类列中找到任何空值,则会引发异常。

所以我要做的是:确实使用 Hibernate 工具的模式更新功能,但您必须在其旁边添加一些数据和模式维护回调,例如填充默认值、删除不再使用的列等。通过这种方式,您可以获得优势(数据库独立的模式更新脚本和避免重复编码更新、持久性和脚本),但您还涵盖了操作的所有方面。

因此,例如,如果版本更新只是添加一个 varchar 值属性(因此是列),它可能默认为 null,使用自动更新就可以完成。在需要更复杂的地方,就需要更多的工作。

这是假设更新后的应用程序能够更新其架构(可以这样做),这也意味着它必须具有在架构上执行此操作的用户权限。如果客户的政策阻止了这种情况(可能是蜥蜴脑案例),您将必须提供数据库特定的脚本。

【讨论】:

【参考方案8】:

Hibernate 创建者在他们的书 "Java Persistence with Hibernate" 中不鼓励在生产环境中这样做:

警告:我们已经看到 Hibernate 用户尝试使用 SchemaUpdate 自动更新生产数据库的架构。这可能很快以灾难告终,并且您的 DBA 不允许这样做。

【讨论】:

那是 2006 年写的吗?【参考方案9】:

不,永远不要这样做。 Hibernate 不处理数据迁移。是的,它会使您的架构看起来正确,但不能确保有价值的生产数据不会在此过程中丢失。

【讨论】:

【参考方案10】:

我们在一个生产项目中运行了几个月,到目前为止从未出现过问题。请记住此食谱所需的 2 种成分:

    使用向后兼容的方法设计您的对象模型,即弃用对象和属性,而不是删除/更改它们。这意味着如果您需要更改对象或属性的名称,请保留旧名称,添加新名称并编写某种迁移脚本。如果您需要更改对象之间的关联,如果您已经在生产中,这意味着您的设计首先是错误的,因此请尝试想一种新的方式来表达新的关系,而不影响旧数据。

    在部署之前始终备份数据库。

我的感觉是 - 在阅读了这篇文章之后 - 参与讨论的 90% 的人一想到在生产环境中使用这样的自动化就会感到震惊。有些人向 DBA 扔球。请花点时间考虑一下,并非所有生产环境都会提供 DBA,而且没有多少开发团队能够负担得起(至少对于中型项目而言)。所以,如果我们谈论的是每个人都必须做所有事情的球队,那么球就在他们身上。

在这种情况下,为什么不尝试两全其美呢?像这样的工具可以提供帮助,经过精心设计和计划,可以在许多情况下提供帮助。相信我,管理员最初可能很难说服,但如果他们知道球不在他们手中,他们会喜欢的。

就我个人而言,我永远不会回去手动编写脚本来扩展任何类型的架构,但这只是我的看法。而在最近开始采用 NoSQL 无模式数据库之后,我可以看到,所有这些基于模式的操作都将属于过去,所以你最好开始改变你的观点,向前看。

【讨论】:

我不同意 NoSQL 评论。它肯定正在兴起并占有一席之地,但有许多应用程序绝对依赖 ACID 合规性来实现 NoSQL 根本无法提供的并发和事务的数据完整性。【参考方案11】:

查看 LiquiBase XML 以保存更新的变更日志。直到今年我才使用它,但我发现它非常容易学习,并且使数据库修订控制/迁移/变更管理非常简单。我从事一个 Groovy/Grails 项目,Grails 在其所有 ORM(称为“GORM”)下使用 Hibernate。我们使用 Liquibase 来管理所有 SQL 架构更改,随着我们的应用程序随着新功能的发展,我们经常这样做。

基本上,您会保留一个包含变更集的 XML 文件,随着应用程序的发展,您会继续添加这些变更集。该文件与项目的其余部分一起保存在 git(或您正在使用的任何东西)中。部署您的应用程序后,Liquibase 会检查您要连接的数据库中的变更日志表,以便知道已应用的内容,然后智能地应用文件中尚未应用的任何变更集。它在实践中工作得非常好,如果您将它用于所有架构更改,那么您可以 100% 确信您签出和部署的代码将始终能够连接到完全兼容的数据库架构。

很棒的是,我可以在笔记本电脑上使用一个完全空白的 mysql 数据库,启动应用程序,然后立即为我设置架构。它还可以通过首先将这些更改应用到本地开发或暂存数据库来轻松测试架构更改。

开始使用它的最简单方法可能是获取您现有的数据库,然后使用 Liquibase 生成初始的 baseline.xml 文件。然后在未来你可以附加到它并让 liquibase 接管管理架构更改。

http://www.liquibase.org/

【讨论】:

完美,正是我即将转向的。我觉得最好的事情是,向前迈出一步是添加hbm2ddl.auto=update,以便您的类/数据库映射得到验证,并且您可以通过 liquibase 完全控制数据库的创建。你怎么看? 哎呀,我的意思是validate liquibase 更擅长使用“include-import”(如支持和版本控制支持)和“类型”属性来管理脚本,这可以帮助您为具有父子关系的不同环境拥有不同的 SQL 文件。简而言之,Go 传统的 SQL Mgmt。在生产中。对于开发,我们需要速度对于生产,我们需要保证、稳定性和备份。【参考方案12】:

当不知道自己在做什么的人在不应该使用它的情况下使用它时,Hibernate 必须在 prod 中添加关于不使用自动更新的免责声明。

认为不应该使用的情况远远多于可以使用的情况。

多年来,我在许多不同的项目中使用它,从未遇到过一个问题。这不是一个蹩脚的答案,也不是牛仔编码。这是历史事实。

说“永远不要在生产中这样做”的人正在考虑一组特定的生产部署,即他熟悉的那些(他的公司、他的行业等)。

“生产部署”的范围广阔而多样。

经验丰富的 Hibernate 开发人员确切地知道给定映射配置会产生什么 DDL。只要您测试并验证您期望的内容最终会出现在 DDL 中(在 dev、qa、staging 等中),就可以了。

当您添加大量功能时,自动架构更新可以节省大量时间。

自动更新无法处理的东西是无穷无尽的,但一些例子是数据迁移、添加不可为空的列、列名更改等。

您还需要注意集群环境。

但话又说回来,如果你知道所有这些东西,你就不会问这个问题了。唔 。 . .好的,如果你问这个问题,你应该等到你对 Hibernate 和自动模式更新有很多经验之后再考虑在 prod 中使用它。

【讨论】:

【参考方案13】:

在我的情况下(Hibernate 3.5.2、Postgresql、Ubuntu),设置hibernate.hbm2ddl.auto=update 只会创建新表并在现有表中创建新列。

它既没有删除表,也没有删除列,也没有更改列。它可以称为安全选项,但像hibernate.hbm2ddl.auto=create_tables add_columns 这样的东西会更清楚。

【讨论】:

【参考方案14】:

不安全,不推荐,但有可能。

我有在生产环境中使用自动更新选项的应用程序经验。

嗯,这个解决方案中发现的主要问题和风险是:

部署在错误的数据库中。如果您错误地在错误的数据库中使用旧版本的应用程序(EAR/WAR/etc)运行应用程序服务器......您将有很多新的列、表、外键和错误。数据源文件中的一个简单错误(复制/粘贴文件并忘记更改数据库)也会出现同样的问题。在简历中,这种情况可能会给您的数据库带来灾难。 应用服务器启动时间过长。发生这种情况是因为 Hibernate 每次启动应用程序时都会尝试查找所有创建的表/列/等。他需要知道需要创建什么(表、列等)。随着数据库表的增长,这个问题只会变得更糟。 几乎不可能使用的数据库工具。要创建与新版本一起运行的数据库 DDL 或 DML 脚本,您需要考虑启动应用程序服务器后自动更新将创建什么。例如,如果您需要用一些数据填充新列,则需要启动应用程序服务器,等待 Hibernate 创建新列,然后才运行 SQL 脚本。如您所见,数据库迁移工具(如 Flyway、Liquibase 等)几乎不可能在启用自动更新的情况下使用。 数据库更改不是集中的。由于 Hibernate 创建表和其他一切的可能性,很难在应用程序的每个版本中观察数据库的变化,因为它们大部分是自动进行的。 鼓励数据库上的垃圾。由于自动更新的“简单”使用,您的团队可能会忽略删除旧列和旧表,因为休眠自动更新无法做到这一点。 灾难迫在眉睫。生产中发生某些灾难的迫在眉睫的风险(就像其他答案中提到的一些人一样)。即使应用程序运行和更新多年,我认为这不是一个安全的选择。使用此选项时我从未感到安全。

所以,我不建议在生产中使用自动更新。

如果你真的想在生产中使用自动更新,我建议:

独立的网络。您的测试环境无法访问同源环境。这有助于防止应该在测试环境中的部署更改认证数据库。 管理脚本顺序。您需要组织脚本以在部署之前运行(结构表更改、删除表/列)和部署之后的脚本(为新列/表填充信息)。

而且,与其他帖子不同的是,我不认为启用自动更新与“高薪”DBA 相关(如其他帖子中所述)。 DBA 有比编写 SQL 语句来创建/更改/删除表和列更重要的事情要做。这些简单的日常任务可以由开发人员完成和自动化,并且只传递给 DBA 团队审查,而不需要 Hibernate 和 DBA “非常高薪”来编写它们。

【讨论】:

【参考方案15】:

在生产环境中使用 hbm2ddl.auto 不是一个好主意。

管理数据库架构的唯一方法是使用增量迁移脚本,因为:

脚本将与您的代码库一起驻留在 VCS 中。当您签出一个分支时,您会从头开始重新创建整个架构。 增量脚本可以在 QA 服务器上进行测试,然后再应用到生产中 不需要人工干预,因为脚本可以由Flyway 运行,因此它减少了与手动运行脚本相关的人为错误的可能性。

即使是Hibernate User Guide 也建议您避免在生产环境中使用hbm2ddl 工具。

【讨论】:

这是一个完美的答案并同意它。但是我真的发现手动创建第一个数据库脚本的想法很麻烦(即 V1_0__initial_script.sql 在链接中的示例的情况下)。有没有一种方法可以从 Hibernate 为我创建的现有开发数据库创建脚本并存储到 V1_0_intial_script.sql 中?? 使用SchemaExport,如test case所示。 谢谢。我遇到了单行转储“mysqldump -u root -p --no-data dbname > schema.sql”。使用由此产生的转储有什么缺点吗? 使用数据库转储没有问题。 Flyway的一部分,你可以使用liquibase,liquibase的好处是可以配置为你生成迁移脚本

以上是关于休眠:hbm2ddl.auto=生产中的更新?的主要内容,如果未能解决你的问题,请参考以下文章

休眠:hbm2ddl.auto=update 和 autoincrement

hibernate.hbm2ddl.auto Hibernate 如何决定何时创建或更新 ddl?

Hibernate hbm2ddl.auto=update 不更新 MySQL 中的列定义

hibernatehibernate.hbm2ddl.auto=update无法更新数据表

hibernatehibernate.hbm2ddl.auto=update无法更新数据表

hibernatehibernate.hbm2ddl.auto=update无法更新数据表