Hibernate: hbm2ddl.auto=update in production?
是否可以运行使用
不,这不安全。
尽管Hibernate团队做出了最大努力,但您根本无法依靠生产中的自动更新。编写自己的补丁,使用DBA进行检查,测试它们,然后手动应用它们。
从理论上讲,如果hbm2ddl update在开发中有效,它也应该在生产中工作。但实际上,并非总是如此。
即使它工作正常,也可能是次优的。 DBA因为某种原因得到了很多报酬。
我们在生产中实现这一点,尽管应用程序不是关键任务,并且员工没有高薪DBA。它只是一个人为错误的手动过程 - 应用程序可以检测到差异并做正确的事情,而且您可能已经在各种开发和测试环境中对其进行了测试。
一个警告 - 在集群环境中,您可能希望避免它,因为多个应用程序可以同时出现并尝试修改可能不好的架构。或者放入一些只允许一个实例更新架构的机制。
Hibernate创建者不鼓励在他们的书"Java Persistence with Hibernate"中的生产环境中这样做:
WARNING: We've seen Hibernate users trying to use SchemaUpdate to update the schema of a production database automatically. This can quickly end in disaster and won't be allowed by your DBA.
查看LiquiBase XML以保留更新的更新日志。我从来没有使用它,直到今年,但我发现它很容易学习,并使数据库修订控制/迁移/变更管理非常万无一失。我在Groovy / Grails项目上工作,Grails在其下面使用Hibernate来创建所有ORM(称为"GORM")。我们使用Liquibase来管理所有SQL架构更改,当我们的应用程序随新功能发展时,我们会经常这样做。
基本上,您保留一个变更集的XML文件,您可以随着应用程序的发展继续添加这些变量集。这个文件与你的项目的其余部分一起保存在git(或者你正在使用的任何东西)中。部署您的应用程序时,Liquibase会检查您要连接的数据库中的更改日志表,以便知道已应用的内容,然后智能地应用文件中尚未应用的任何更改集。它在实践中非常有用,如果您将它用于所有架构更改,那么您可以100%确信您签出和部署的代码始终能够连接到完全兼容的数据库架构。
令人敬畏的是,我可以在笔记本电脑上使用一个完全空白的slate mysql数据库,启动应用程序,然后立即为我设置架构。它还可以通过首先将这些更改应用于local-dev或staging db来轻松测试模式更改。
开始使用它的最简单方法可能是获取现有数据库,然后使用Liquibase生成初始baseline.xml文件。然后在将来,您可以添加到它并让liquibase接管管理架构更改。
http://www.liquibase.org/
Hibernate必须放弃关于不使用自动更新的免责声明,以便当不知道他们正在做什么的人在不应该使用它的情况下使用它时自己覆盖。
在不应该使用的情况下,可以大大超过那些可以使用的情况。
我已经在很多不同的项目上使用它多年,从来没有遇到过一个问题。这不是一个蹩脚的答案,而不是牛仔编码。这是一个历史事实。
一个说"从不在生产中做"的人正在考虑一组特定的生产部署,即他熟悉的(他的公司,他的行业等)。
"生产部署"的范围广泛而多样。
经验丰富的Hibernate开发人员确切知道DDL将从给定的映射配置中产生什么。只要你测试并验证你期望在DDL中的内容(在dev,qa,staging等中),你就可以了。
当您添加许多功能时,自动架构更新可以节省大量时间。
自动更新无法处理的内容列表是无穷无尽的,但一些示例是数据迁移,添加不可为空的列,列名称更改等。
您还需要在群集环境中小心。
但话说回来,如果你知道所有这些东西,你就不会问这个问题了。嗯。 。 。好吧,如果你问这个问题,你应该等到你有很多Hibernate和自动架构更新的经验,然后才考虑在prod中使用它。
我会投票否决。当列的数据类型发生更改时,Hibernate似乎不理解。示例(使用MySQL):
1 2 3 4 5 |
可能还有其他示例,例如将String列的长度超过255并将其转换为text,mediumtext等。
当然,我认为没有真正的方法可以在不创建新列的情况下"转换数据类型",复制数据并吹掉旧列。但是,当您的数据库具有不反映当前Hibernate映射的列时,您生活得非常危险......
Flyway是处理这个问题的好方法:
http://flywaydb.org
正如我在本文中解释的那样,在生产中使用
管理数据库模式的唯一方法是使用增量迁移脚本,因为:
- 脚本将沿着代码库驻留在VCS中。签出分支时,从头开始重新创建整个架构。
- 增量脚本可以在生产中应用之前在QA服务器上进行测试
- 因为脚本可以由Flyway运行,所以不需要手动干预,因此它减少了与手动运行脚本相关的人为错误的可能性。
即使是Hibernate用户指南也建议您避免在生产环境中使用
我们在一个项目中运行了几个月,到目前为止从未出现过问题。请记住此配方所需的2种成分:
使用向后兼容性方法设计对象模型,即弃用对象和属性,而不是删除/更改它们。这意味着如果您需要更改对象或属性的名称,请保留原有的名称,添加新的名称并编写某种迁移脚本。如果您需要更改对象之间的关联,如果您已经在生产中,这意味着您的设计首先是错误的,因此请尝试考虑一种表达新关系的新方式,而不会影响旧数据。
始终在部署之前备份数据库。
我的感觉是 - 在阅读这篇文章后 - 参与讨论的人中有90%只是想到在生产环境中使用这样的自动化。有些人把球扔到了DBA。花一点时间考虑并非所有生产环境都会提供DBA,并且没有多少开发团队能够负担得起(至少对于中等规模的项目)。所以,如果我们谈论的是每个人都必须做所有事情的球队,球就在他们身上。
在这种情况下,为什么不尝试两全其美?像这样的工具可以提供帮助,通过精心的设计和计划,可以在许多情况下提供帮助。相信我,管理员最初可能很难说服,但如果他们知道球不在他们手上,他们就会喜欢它。
就个人而言,我永远不会再手工编写脚本来扩展任何类型的架构,但这只是我的意见。在最近开始采用NoSQL无架构数据库之后,我可以看到,所有这些基于模式的操作都将属于过去,因此您最好开始改变您的观点并展望未来。
我不会冒险,因为你最终可能会丢失应该保留的数据。 hbm2ddl.auto = update纯粹是一种让您的dev数据库保持最新的简单方法。
-
在我的例子中(Hibernate 3.5.2,Postgresql,Ubuntu),设置
hibernate.hbm2ddl.auto=update 只创建了新表并在现有表中创建了新列。 -
它既没有删除表,也没有删除列,也没有改变列。它可以被称为安全选项,但像
hibernate.hbm2ddl.auto=create_tables add_columns 这样的东西会更清楚。
它不安全,不推荐,但它是可能的。
我在使用生产中的自动更新选项的应用程序方面有经验。
那么,这个解决方案中发现的主要问题和风险是:
- 部署在错误的数据库中。如果您错误地在错误的数据库中运行具有旧版本应用程序(EAR / WAR / etc)的应用程序服务器...您将拥有许多新列,表,外键和错误。数据源文件中的一个简单错误(复制/粘贴文件并忘记更改数据库)也会出现同样的问题。在简历中,情况可能是数据库中的灾难。
- 应用程序服务器启动时间过长。这是因为Hibernate每次启动应用程序时都会尝试查找所有创建的表/列/等。他需要知道需要创建什么(表,列等)。随着数据库表的增长,这个问题只会越来越严重。
- 数据库工具几乎不可能使用。要创建使用新版本运行的数据库脚本,您需要考虑启动应用程序服务器后自动更新将创建的内容。例如,如果需要使用某些数据填充新列,则需要启动应用程序服务器,等待Hibernate克隆新列,然后再运行SQL脚本。如您所见,数据库迁移工具(如Flyway,Liquibase等)几乎不可能在启用自动更新时使用。
- 数据库更改不是集中的。由于Hibernate可能会创建表和其他所有内容,因此很难在每个版本的应用程序中查看数据库的更改,因为大多数都是自动生成的。
- 鼓励数据库上的垃圾。由于易于自动更新,您的团队可能会忽略丢弃旧列和旧表。
- 迫在眉睫的灾难生产中发生某种灾难的风险很大(就像其他答案中提到的一些人)。即使应用程序运行多年也可以更新,我认为它不安全。这个选项让我感觉不安全。
因此,我不建议在生产中使用自动更新。
如果您真的想在生产中使用自动更新,我建议:
- 分离的网络。您的测试环境无法访问同系环境。这有助于防止应该在测试环境中的部署更改Homologation数据库。
- 管理脚本顺序。您需要组织脚本以在部署之前运行(结构表更改,删除表/列)和部署之后的脚本(填充新列/表的信息)。
而且,不同的其他帖子,我不认为自动更新使其与"收入很高"的DBA(如其他帖子中提到的)相关... ... DBA比写SQL语句创建更重要的事情要做/更改/删除表和列。这些简单的日常任务可以由开发人员完成并自动完成,并且只能通过DBA团队进行审核,而不需要Hibernate和DBA"非常有偿"来编写它们。
不,不要这样做。 Hibernate不处理数据迁移。是的,它会使您的架构看起来正确,但它不能确保在此过程中不会丢失有价值的生产数据。
-
通常,大型组织中的企业应用程序以较低权限运行
-
数据库用户名可能没有
DDL 权限来添加hbm2ddl.auto=update 所需的列。
应用程序的架构可能会及时发展;如果您有多个安装(可能是不同版本),您应该有一些方法来确保您的应用程序,某种工??具或脚本能够将模式和数据从一个版本逐步迁移到任何一个版本。
拥有Hibernate映射(或注释)中的所有持久性是保持模式进化受控的一种非常好的方法。
您应该考虑模式演变有几个方面需要考虑:
数据库模式的演变
添加更多列和表
丢弃旧的列,表和
关系
使用默认值填充新列
Hibernate工具非常重要(特别是在我的经验中)你在许多不同类型的数据库上拥有相同应用程序的不同版本。
如果您使用Hibernate,则Point 3非常敏感,因为如果您引入新的布尔值属性或数字属性,如果Hibernate将在此类列中找到任何空值,如果将引发异常。
所以我要做的是:确实使用Hibernate工具的架构更新能力,但你必须添加一些数据和架构维护回调,比如填充默认值,丢弃不再使用的列等等。通过这种方式,您可以获得优势(与数据库无关的模式更新脚本,并避免在更新,执行和脚本中重复编码),但您还可以涵盖操作的所有方面。
因此,例如,如果版本更新仅包含添加varchar值属性(因此列)(可能默认为null),则自动更新将完成。如果需要更多的复杂性,则需要做更多的工作。
这假设更新时的应用程序能够更新其模式(可以完成),这也意味着它必须具有在模式上执行此操作的用户权限。如果客户的策略阻止了这种情况(可能是Lizard Brain案例),则必须提供特定于数据库的脚本。
我同意弗拉基米尔的观点。如果我甚至建议这样的课程,我公司的管理员肯定不会欣赏它。
此外,创建一个SQL脚本而不是盲目地信任Hibernate使您有机会删除不再使用的字段。 Hibernate不这样做。
我发现将生产模式与新模式进行比较可以让您更好地了解您在数据模型中发生的变化。当然,你知道,因为你做到了,但现在你可以一次看到所有的变化。甚至那些让你变得像"哎呀?!"的那些。
有些工具可以为您制作架构增量,因此它甚至都不是很难。然后你就知道到底会发生什么。