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


338

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


6
我们做。从来没有任何问题。
cretzel

4
很好的问题。我现在面对它。所以现在是2018年-十年后您如何看待?在具有复杂模式的重要客户的生产数据库上使用Hibernate更新是否安全?
Kirill Ch '18

Answers:


388

不,这是不安全的。

尽管Hibernate团队做出了最大的努力,但您根本不能依靠生产中的自动更新。编写自己的补丁程序,使用DBA对其进行审核,对其进行测试,然后手动进行应用。

从理论上讲,如果hbm2ddl update在开发中起作用,那么它也应在生产中起作用。但实际上,并非总是如此。

即使工作正常,也可能不是最佳选择。DBA之所以得到这么多报酬是有原因的。


33
为什么不安全?
cretzel

74
这是不安全的,因为所应用的修补程序可能具有hbm2ddl几乎无法预测的副作用(例如,禁用已安装的触发器以用于修改表)。对于复杂的模式,最安全的方法是手动。带有回归分析测试的自动测试距离第二远。所有恕我直言。
弗拉基米尔·朱热夫(Fladimir Dyuzhev)

18
另外,更新数据库模式也应由专业人员(dbas)处理。从错误的数据库更改中恢复是很难的。Vova没有提到它-但是如果由于类型或大小更改而休眠的更新决定删除一列并重新添加它,会发生什么情况。并可以说该列是您所有用户的电子邮件地址吗?:-)再见,再见公司.....您希望自动生成DDL更改-但您绝对希望人工检查更改。
帕特

9
升级之前不备份数据库吗?
雅各布

21
首先,目前Hibernate的架构更新不会删除表或列。
布赖恩·德特林

70

我们在生产中做到这一点,尽管它的应用程序不是任务关键型的,并且没有高薪的DBA。这只是一个较少的人为错误的手动过程-应用程序可以检测出差异并做正确的事情,此外,您大概已经在各种开发和测试环境中对其进行了测试。

一个警告-在集群环境中,您可能要避免它,因为可能同时出现多个应用程序并尝试修改架构,这可能是很糟糕的。或者采用某种仅允许一个实例更新架构的机制。


2
我们也在类似的用例中在生产中使用它。不是关键任务的分析平台。我们已经在4个环境(4年)中部署了16,000次,而休眠却没有太多麻烦。我们是一个小团队,都是SQL RDBS的初学者,与我们自己相比,他们对Hibernate处理架构有更好的信念。我想知道让DBA负责人员管理迁移和架构更改的错误率是多少?在〜16K部署中,它是否比0好?
user3263752 '19

pat对此评论会怎么说?stackoverflow.com/questions/221379/...
湿婆库马尔

52

Hibernate的创建者在他们的《 Java Persistence with Hibernate》一书中不建议在生产环境中这样做:

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


1
那是2006年写的吗?
user3263752

28

签出LiquiBase XML以保留更新的变更日志。我直到今年才使用过它,但是我发现它非常容易学习,并使数据库版本控制/迁移/变更管理变得万无一失。我从事一个Groovy / Grails项目,Grails在其所有ORM(称为“ GORM”)下面都使用了Hibernate。我们使用Liquibase来管理所有SQL模式更改,随着应用程序具有新功能的发展,我们经常会这样做。

基本上,您将保留一个变更集的XML文件,您将在应用程序发展时继续添加这些变更集。此文件与项目的其余部分一起保存在git(或您使用的任何文件)中。部署您的应用程序后,Liquibase会在您要连接的数据库中检查它的changelog表,以便知道已经应用了什么,然后智能地应用文件中尚未应用的所有变更集。它在实践中绝对有效,如果您将其用于所有架构更改,那么您可以100%确信签出和部署的代码将始终能够连接到完全兼容的数据库架构。

令人敬畏的是,我可以在笔记本电脑上获取一个完全空白的slate mysql数据库,启动应用程序,然后立即为我设置了架构。通过将模式更改首先应用于本地开发或暂存数据库,还可以轻松测试模式更改。

最简单的开始方法可能是获取现有的数据库,然后使用Liquibase生成初始的baseline.xml文件。然后,将来您可以追加它,让liquibase接管管理模式更改。

http://www.liquibase.org/


完美,正好是我要转向的东西。我觉得最好的是,向前迈出了一步,那就是添加它,hbm2ddl.auto=update以便使您的Class / DB映射得到验证,并且您可以通过liquibase完全控制数据库的创建。你怎么看?
bholagabbar

糟糕,我的意思是validate
bholagabbar

liquibase更擅长使用“包括导入”之类的支持和版本控制支持以及“文件”的“类型”属性来管理脚本,这有助于您针对具有父子关系的不同环境使用不同的SQL文件。简而言之,转到传统的SQL Mgmt。在生产中。为了发展,我们需要生产速度,我们需要保证,稳定性和备份。
卡兰·考 Karan Kaw)

26

我不会投票。当列的数据类型已更改时,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以上,并查看将其转换为text,mediumtext等。

当然,我不认为没有一种方法可以在不创建新列,复制数据并删除旧列的情况下进行“转换数据类型”。但是当数据库中的列一刻不反映当前的Hibernate映射时,您生活的非常危险……

Flyway是解决此问题的好选择:

http://flywaydb.org


1
我只是尝试了您的示例的第一部分-在我的情况下更改@Column(length = 45)@Column(length = 255)。可以使用验证Hibernate 4.3.6.Final是否正确更新了数据库架构hbm2ddl.auto=update。(要提到的一件事是,数据库中目前没有任何数据,只有结构。)
Steve Chambers

他们很可能在过去大约6年的时间内修复了该错误。但是,如果您确实在模式中包含数据并进行了更改,导致列宽减小,那么您将遇到错误或非托管数据截断。
悬崖.meyers 2014年

1
flywaydb需要一个手工制作的SQL脚本,它应该比自动程序可以做的更好,但是谁可以编写一个大脚本,则是有问题的。
砰力丸

25

当不知道自己在做什么的人在不应该使用自动更新的情况下使用它时,Hibernate必须将免责声明置于产品中以掩盖自己。

在不应该使用的情况下,可以使用的情况要多得多。

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

一个说“从不在生产中进行”的人正在思考一组特定的生产部署,即他所熟悉的生产部署(他的公司,他的行业等)。

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

经验丰富的Hibernate开发人员确切地知道给定的映射配置将导致什么DDL。只要您测试并验证所期望的结果最终会出现在DDL中(在开发,质量保证,分段等),就可以了。

当您添加许多功能时,自动模式更新可以节省大量时间。

不会自动处理的东西列表是无止境的,但是一些示例是数据迁移,添加不可为空的列,列名更改等。

另外,您还需要在群集环境中保重。

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


19

正如我在本文中所解释的,hbm2ddl.auto在生产中使用它不是一个好主意。

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

  • 这些脚本将沿着您的代码库驻留在VCS中。签出分支时,将从头开始重新创建整个架构。
  • 在生产中应用增量脚本之前,可以在QA服务器上对其进行测试
  • 由于脚本可以由Flyway运行,因此无需手动干预,因此可以减少与手动运行脚本相关的人为错误的可能性。

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

在此处输入图片说明


这是一个完美的答案,并同意这一点。但是我真的很麻烦地找到了手动创建第一个数据库脚本的想法(例如,链接中的示例为V1_0__initial_script.sql)。有没有一种方法可以从Hibernate为我创建的现有开发数据库中创建脚本并将其存储到V1_0_intial_script.sql中?
HopeKing

1
使用SchemaExport测试用例演示的方法。
Vlad Mihalcea

谢谢。我遇到了单行转储“ mysqldump -u root -p --no-data dbname> schema.sql”。使用由此生成的转储有任何缺点吗?
HopeKing

1
使用数据库转储没有问题。
Vlad Mihalcea

8

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

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

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

我的读后感是,参与讨论的90%的人只是出于在生产环境中使用这种自动化的想法而感到恐惧。有些人 DBA 扔球。请花一点时间考虑一下,并不是所有的生产环境都可以提供DBA,而且没有多少开发团队能够负担得起(至少对于中等规模的项目而言)。因此,如果我们谈论的是每个人都必须做所有事情的团队,那么球就在他们身上。

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

就个人而言,我绝不会再为扩展任何类型的架构而手动编写脚本,但这只是我的观点。而且,在最近开始采用NoSQL的无模式数据库之后,我很快就会看到,所有这些基于模式的操作都属于过去,因此您最好开始改变观点并展望未来。


4
我不同意NoSQL的评论。它的确在上升并占有一席之地,但是有许多应用程序完全依赖ACID来实现NoSQL根本无法提供的并发性和事务处理的数据完整性。
jpswain 2011年

7

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


2
升级之前不备份数据库吗?
雅各布

6
是的,我当然可以,但是从备份中还原需要很多工作。当您还可以有序地更新数据库时,再也不必花费精力来恢复备份了。
Jaap Coomans 2010年

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

  • 它既没有删除表,也没有删除列,也没有更改列。可以将其称为安全选项,但类似方法hibernate.hbm2ddl.auto=create_tables add_columns会更加清晰。


6

这不安全,不建议使用,但是有可能。

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

好了,此解决方案中发现的主要问题和风险是:

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

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

如果您确实要在生产中使用自动更新,我建议:

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

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


4

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


4
  • 通常,大型组织中的企业应用程序以降低的特权运行。

  • 数据库用户名可能没有DDL添加所需列的特权hbm2ddl.auto=update


这是我经常遇到的问题。我们尝试使用休眠模式进行初始数据库创建,但是通常不可能这样做。

5
这不是一个“问题”,这是对的。
Vladimir Dyuzhev 2011年

2

我同意弗拉基米尔。如果我什至建议过这样的课程,我公司的管理员绝对不会喜欢它。

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

而且我发现将生产模式与新模式进行比较可以使您更好地了解数据模型中的更改。您当然知道,因为您做到了,但是现在您可以一次性看到所有更改。甚至那些使您走开的东西都像“该死的什么?!”。

有一些工具可以为您创建架构增量,因此它甚至都不是一件容易的事。然后,您确切地知道会发生什么。


“有些工具可以为您创建架构增量”:您能否指出一些这样的工具?
丹尼尔·卡西迪

我认为apexsql.com/sql_tools_diff.asp可以做到这一点,可能还有更多的应用程序。我通常通过转储模式和差异(使用diff)来手工完成。
Extraneon 2010年

2

应用程序的架构可能会随时间演变;如果您有多个安装(可能具有不同的版本),则应该采用某种方法来确保您的应用程序,某种工​​具或脚本能够将模式和数据从一个版本逐步迁移到以下任何一个版本。

在Hibernate映射(或注释)中保持所有持久性是使模式演变处于受控状态的一种很好的方法。

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

  1. 添加更多列和表时数据库架构的发展

  2. 删除旧列,表和关系

  3. 用默认值填充新列

当您在许多不同种类的数据库上拥有同一应用程序的不同版本时,Hibernate工具尤其重要。

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

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

因此,例如,如果版本更新仅包含添加varchar值属性(因此列),该属性可能默认为null,那么使用自动更新就可以完成。在需要更多复杂性的地方,将需要更多的工作。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.