Hibernate: hbm2ddl。自动=更新生产?

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

188944 次浏览

不,不安全。

尽管Hibernate团队尽了最大的努力,但你不能依赖自动更新在生产中。编写您自己的补丁,与DBA一起检查它们,测试它们,然后手动应用它们。

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

即使它运行正常,也可能不是最优的。dba的薪水这么高是有原因的。

我不会冒这个险,因为你可能会丢失本该保存的数据。hbm2ddl。Auto =update纯粹是一种保持开发数据库最新的简单方法。

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

一个警告——在集群环境中,你可能想要避免它,因为多个应用程序可能会同时出现,并试图修改模式,这可能是不好的。或者放入某种只允许一个实例更新模式的机制。

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

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

我发现,比较生产模式和新模式可以让您更好地了解数据模型中所更改的内容。当然,你知道,因为你成功了,但现在你看到所有的变化一气呵成。即使是那些让你想“搞什么鬼?!”

有一些工具可以为您创建模式增量,因此这甚至不是一项艰巨的工作。然后你就知道会发生什么了。

我会投反对票。当列的数据类型发生变化时,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以上,然后看到它转换为文本、mediumtext等等。

当然,我不认为真的有一种方法可以在不创建新列、复制数据和删除旧列的情况下“转换数据类型”。但是一旦你的数据库中有不能反映当前Hibernate映射的列,你就非常危险了……

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

http://flywaydb.org

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

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

应用程序的模式可能会随着时间而变化;如果您有多个安装,可能是不同的版本,您应该有某种方法来确保您的应用程序、某种工具或脚本能够逐步将模式和数据从一个版本迁移到下一个版本。

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

您应该考虑模式演化有几个方面需要考虑:

  1. 数据库模式的演变 增加更多的列和表

  2. 删除旧列,表和 李关系< / p > < / >

  3. 用默认值填充新列

Hibernate工具非常重要,特别是当你在许多不同类型的数据库上有相同应用程序的不同版本时(就像我的经验一样)。

第3点在使用Hibernate时非常敏感,例如在引入一个新的布尔值属性或数值属性时,如果Hibernate在这些列中发现任何空值,则会引发异常。

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

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

这是假设更新时的应用程序能够更新其模式(这是可以做到的),这也意味着它必须拥有对模式进行更新的用户权限。如果客户的策略阻止了这一点(可能是Lizard Brain的情况),您将不得不提供特定于数据库的脚本。

Hibernate创建者在他们的书Java持久化与Hibernate中不鼓励在生产环境中这样做:

警告:我们已经看到Hibernate用户试图使用SchemaUpdate自动更新生产数据库的模式。这很快就会导致灾难,而且DBA不允许这样做。

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

我们在一个已经在生产环境中运行了几个月的项目中这样做,到目前为止从来没有遇到过问题。记住这道菜需要的两种原料:

  1. 使用向后兼容的方法设计对象模型,即deprecate对象和属性,而不是删除/更改它们。这意味着,如果您需要更改对象或属性的名称,请保持旧的名称不变,添加新的名称并编写某种迁移脚本。如果您需要更改对象之间的关联,如果您已经处于生产环境中,这意味着您的设计从一开始就是错误的,因此请尝试考虑一种新的方式来表达新的关系,而不影响旧数据。

  2. 始终备份部署之前的数据库。

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

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

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

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

基本上,您保留一个关于更改集的XML文件,随着应用程序的发展,您可以继续向其中添加更改集。该文件与项目的其余部分一起保存在git(或任何您正在使用的文件)中。当你的应用被部署时,Liquibase会检查你连接到的DB中的更改日志表,这样它就会知道已经应用了什么,然后它会智能地应用文件中还没有应用的任何更改集。它在实践中工作得非常好,如果您将它用于所有模式更改,那么您可以100%确信您签出和部署的代码始终能够连接到完全兼容的数据库模式。

最棒的是,我可以在我的笔记本电脑上使用一个完全空白的mysql数据库,启动应用程序,模式就马上为我设置好了。通过首先将模式更改应用到本地开发或登台db,还可以很容易地测试模式更改。

开始使用它的最简单方法可能是使用现有的DB,然后使用Liquibase生成一个初始的baseline.xml文件。然后在将来,您可以添加到它,并让liquibase接管管理模式更改。

http://www.liquibase.org/

Hibernate必须在prod中声明不使用自动更新,以在不应该使用prod的情况下,当不知道自己在做什么的人使用它时,Hibernate可以保护自己。

当然,不应该使用的情况远远超过可以使用的情况。

我已经在许多不同的项目中使用它很多年了,从来没有出现过一个问题。这不是一个蹩脚的答案,也不是牛仔编码。这是历史事实。

一个人说“永远不要在生产中这样做”,他想到的是一组特定的生产部署,即他所熟悉的(他的公司,他的行业等)。

“生产部署”的范围是巨大而多样的。

有经验的Hibernate开发人员确切地知道给定映射配置将产生什么DDL。只要您测试并验证您期望的内容最终出现在DDL中(在开发、qa、登台等),就没问题。

在添加大量特性时,自动模式更新可以真正节省时间。

自动更新不能处理的事情是无穷无尽的,但一些例子是数据迁移,添加非空列,列名更改,等等。

此外,您还需要注意集群环境。

但话说回来,如果你知道这些,你就不会问这个问题了。嗯……好的,如果您正在问这个问题,那么在考虑在prod中使用它之前,您应该等到对Hibernate和自动模式更新有了丰富的经验。

  • 在我的例子中(Hibernate 3.5.2, Postgresql, Ubuntu),设置hibernate.hbm2ddl.auto=update只创建了新的表,并在已经存在的表中创建了新的列。

  • 它既不删除表,也不删除列,也不更改列。它可以被称为一个安全的选项,但像hibernate.hbm2ddl.auto=create_tables add_columns这样的选项会更清楚。

这不安全,也不推荐,但这是可能的。

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

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

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

因此,我不建议在生产环境中使用自动更新。

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

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

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

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

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

  • 脚本将与你的代码库一起驻留在VCS中。签出分支时,从头重新创建整个模式。
  • 增量脚本在应用到生产环境之前可以在QA服务器上进行测试
  • 不需要手动干预,因为脚本可以由再经运行,因此它减少了与手动运行脚本相关的人为错误的可能性。

甚至Hibernate用户指南也建议你避免在生产环境中使用hbm2ddl工具。

Hibernate ORM用户指南说它是最好的