我一直在阅读Dan Chak的“Enterprise Rails”一书,它让我想到:你觉得你应该在数据库级别和应用程序级别都有数据限制吗?或者你觉得类似于Ruby on Rails这样的自以为是的框架 - 数据库只是数据的“哑存储库”,所有检查都应该在你的应用程序中完成(我不是试图在这里挑出RoR - 我是一个Rails自己的忠实粉丝,但我不同意它对数据库的方法)?
就个人而言,我觉得你应该同时拥有它们,以确保你的数据库和应用程序安全。我的意思是你应该使用非空约束,如果已知的话给你的字段一个长度(而不是将它们全部留在 nvarchar(255)),有之类的东西数据库上的外键,检查约束和触发器,然后通过应用程序中的业务逻辑规则强制执行此操作。 IMO通过其用户界面使您的应用程序更加健壮,并且还可以防止可能直接访问数据库的人员。
我经常看到的反驳论点是它需要重复的逻辑;一次在数据库级别,一次在应用程序级别 - 假设您有一个检查约束来验证是否输入了产品的SKU(即它的长度大于零)。
现在,您需要在业务逻辑中包含验证方法,以确保用户输入的值的长度大于零,并且可能还有一些客户端Javascript可以在用户键入数据时捕获错误。 / p>
我认为这不是一件坏事 - 是的,你有一些重复的逻辑,但最终的结果是“数据库作为堡垒”的思维方式,因为如果你考虑它,你的数据是最重要的一部分你的申请;毕竟,如果数据容易被破坏和泄露,那么你的新Web 2.0应用程序有什么用呢?
您对此有何看法?数据库应该是像诺克斯堡这样难以穿透的堡垒,还是一个被激光守护的开放式保险箱?换句话说,您是否应该牺牲一些逻辑重复来确保安全的数据模型,或者将所有内容留给您的应用程序并使用数据库来存储数据?
答案 0 :(得分:33)
简而言之:数据库应该强制执行约束。
为什么:
答案 1 :(得分:21)
是的,如果您想限制数据库中的内容。这些层应尽可能彼此不同,并且您的数据库不应依赖于另一层,以确保它遵循规则。
无法保证错误(或恶意)“业务逻辑”层不会将有毒数据插入到您的表中。当然,如果你可以相信其他层,你可能不需要它。但我在一家大型机商店工作,在那里,DBA总是要解决由年轻的Java whippersnappers在没有足够(任何?)测试的情况下将他们的错误代码推广到生产所引起的问题: - )。
在不同的开发区域之间共享的数据库表(以及它们对我们来说都是共享的)应始终保护自己免受错误数据的侵害。当App A将狡猾的数据放入App B使用的表格中时,不是App A开发人员就会受到热议,而是DBA。
答案 2 :(得分:19)
如果你跟随杰夫阿特伍德学校的数据库只是一个愚蠢的数据存储&检索系统,然后你将所有验证放在应用程序层。
但是,我发现应用程序就像小孩子一样。如果不加以控制,他们会扔掉房间里的一切。父母要清理这些烂摊子。在这种情况下,DBA将进行清理。
但是,我认为您需要小心使用每个数据库数据完整性功能,因为它就在那里。使用外键约束和触发器重载数据库可能会产生比您想象的更多问题。我倾向于只在非常密切相关的表上使用外键,例如标题/详细信息表对。如果您开始在任何地方添加外键,您最终可能会得到一个无法管理的数据库。
我很少使用触发器。我认为他们使数据库非常不透明。您发出一个简单的更新/插入/删除命令,可能会发生奇怪的事情。我猜有两个触发器不可避免的地方:
当您没有源代码写入数据库的应用程序时,您需要修改行为。触发器是您唯一的选择。
如果您正在对视图执行CRUD操作。对于插入/更新/删除操作,触发器是必需的。
我倾向于在应用程序中执行基本验证。通过这种方式,用户可以立即得到错误的反馈。需要查找相关表的复杂验证最好在数据库中完成(以及应用程序执行的简单验证)。我认为,在不使用复杂的锁定策略的情况下,在应用程序级别几乎无法保证某些形式的验证。
如果您有多个应用程序(可能在不同平台上使用不同语言编写),则可以加强将更多验证放入数据库层的情况。由不同程序员编写的两个或多个应用程序执行相同验证的可能性相当低。最好在一个地方做。
这个世界的杰夫阿特伍德建议您编写一个所有应用程序用来与之通信的Web服务。 Web服务执行数据验证。这样做可以使数据库保持一个愚蠢的存储容器,从而使您可以切换数据库引擎。实际上,您很少更改数据库引擎(除非您从Microsoft Access开始!)。如果您正在编写Web服务纯粹是为了集中数据验证,那么我认为您已经过火了。
答案 3 :(得分:16)
如果您确定永远不会有其他客户端应用程序,那么您可能会将数据库视为一个简单的存储空间。但是,如果您将拥有多个客户端应用程序,显然您将不得不在所有客户端应用程序中复制约束,这是一个坏主意。请记住,其他客户包括开发人员工具。
此外,通过将数据库用作“哑存储库”,您很可能最终会得到效率较低的应用程序。数据库可以比应用程序更有效地完成许多工作。为什么不利用这个呢?
答案 4 :(得分:8)
我认为您应该不惜一切代价尝试保护您的数据。没有什么比尝试报告具有错误数据的应用程序更糟糕的了,因为应用程序有错误。现在这是什么意思?
您应该通过FK强制执行您的关系,没有理由不这样做。您应该尝试避免空值,并且仅在空值适当时使用它们。我确实认为有一条细线。
您是否应该解析一个电话号码以确保其格式正确?可能不是,但是你应该再次将电话号码存储在没有格式化问题的模式中。
答案 5 :(得分:8)
通常总会有一些重复,数据库不仅仅是愚蠢的存储库。
数据库确保数据级别的完整性。外键约束,非空约束和大小约束都基本上由数据库涵盖。
你不能在数据库中做所有事情,但你可以做很多事情。保护数据。
提升一个级别,你就拥有了业务逻辑。通常,这是您与其他应用程序(Web服务,您自己的UI等)集成的关键点。这里将业务逻辑编码到应用程序中。如果产品的结束日期为x,那么如果y具有不同的结束日期,那么它也不会出现在y中。
在数据库中描述那种规则很难,所以你不这样做。但是,您的业务逻辑层仍会拦截它知道无效的事物。例如如果不允许描述字段为空,则业务逻辑不应将其发送到数据库。无论如何都会出错,但是你试图在他们被认为不好的时候拦截事物。
在数据库中也很难表达其他“规则”,例如“如果新用户来自Arkensas,则有1年到期日期,否则为2年,除非他们有3个孩子,其中一个被命名为Barry”。我们可以嘲笑这个例子,但经验丰富的程序员会告诉你,业务逻辑是最大的矛盾之一。
向上移动到用户界面,用户界面还会定期对屏幕中的业务逻辑进行编码。表单和其他页面通常会以无效状态存在,并且UI的工作是至少在大多数时间知道规则。希望UI将逻辑推迟到业务层,但业务层不知道字段1是截止日期,字段2是描述。
如果用户已经选择了小部件Y,则UI知道用X搜索产品。 UI知道需要描述,并且项目计数是> 0和< 100(在这些示例中,一个好的UI将依赖于业务层来告诉它,例如,min和max,但UI仍然知道这种关系)
在Web UI中,我们还添加了客户端脚本,它再次复制了服务器代码中的逻辑。我们使用客户端脚本来获得更好的用户体验,但最终不要相信来自客户端的任何内容(可以关闭脚本,手动操作表单字段等)
所以你可以看到逻辑将被重复。你试图尽可能地减少重复,但实际上很少有可能给出一个非常重要的程序。
答案 6 :(得分:3)
如果您在数据库中至少强制执行基本完整性,则某些时候无效数据将会进入。可能来自应用程序。 bug,也许来自某人拉起SQL控制台,无论如何。然后你会发现你的应用程序在不可能的时候(“所有B记录必须有A记录!你的意思是什么不存在?”)会发生有趣的失败模式。
只有当应用程序是唯一可以触及该数据库的应用程序时,才能在应用程序中实施完整性。即便如此,您最好还是要小心应用程序错误,因为应用程序比架构大得多,因此更容易。
答案 7 :(得分:2)
为了提供略微不同的观点,我认为这取决于背景。
首先,多年来我已经从100%确信在DBMS中实现约束,试图完全避免它:我希望我的所有业务逻辑都在同一层。
其次,我正在使用Rails和ActiveRecord进行大量迁移,不允许在字段大小和NULL_ness之外进行大量的数据库驻留约束定义。
如果您正在构建一个新应用程序,其中包含一个完全专用于您的应用程序的新数据库,那么您可以在应用程序代码中强制执行约束,作为业务逻辑的一部分。这就是Rails喜欢工作的方式,它看起来效果很好。
如果您正在针对在db约束中实现业务规则的遗留数据库构建新应用程序,那么我建议继续这样做,并接受业务层中的一些验证规则重复。在尝试将数据应用于数据库之前,最好先检查数据是否有效。
如果您正在构建一个新的应用程序/数据库,期望其他应用程序将访问数据,那么该方法将取决于如何构建其他应用程序。同样,在Rails中,您可能应该考虑共享模型的方法,在这种情况下,该层应该足够了。如果您不能拒绝直接访问数据的其他实现样式,那么您将重新开始复制。我倾向于尝试 - 非常努力 - 拒绝对这些应用程序的直接数据库访问,并努力通过(希望是RESTful)Web服务为它们提供服务,以便您可以在业务逻辑级别管理数据完整性。
如果第三方(内部或其他)应用程序具有对您的架构的DDL访问权限,那么就不要再担心这个问题 - 您已经失去了对数据的控制权而且您已经搞砸了!
答案 8 :(得分:1)
我个人的偏好是在数据库层强制执行基本验证,然后使用内省技术将这些约束冒充到应用程序层作为默认值(约定优于配置)。如果我在表单等中有一些不寻常的用户交互,那么我将覆盖我从数据库中获得的默认值,我需要的任何新行为。这有助于我在数据库层中主要保持最基本的验证,而我在应用层进行更复杂的验证(即电话号码格式)。
我可以检查电话号码等内容的约束,但我发现检查约束是一种难以实施格式的令人沮丧的方式。
如果我有一个需要从第二个源接收输入的应用程序(即它同时具有Web界面和桌面客户端),那么我会尝试让第二个界面完成第一个(通过提交桌面数据)例如,web服务或者在失败的情况下,除了在应用程序层进行验证之外,我还会在数据库中使用检查约束。
最重要的是我希望尽可能地保持数据的完整性,因为糟糕的数据将不可避免地在应用程序中造成破坏 - 然后另外(实际上同样重要)我希望应用程序能够处理验证用户提供的信息。
答案 9 :(得分:1)
从OOP的角度来看,数据库就像在Object / Actor中一样,在较大的系统中,它应该对自己负责。这包括验证输入的必要需求。
答案 10 :(得分:1)
两者都是。我在最后一个地方发现了这个。我们有遗留的delphi系统和sybase数据库。新系统是.NET和Sql server。一个特定的员工全权负责将sybase数据库转换为sql server数据库,以供希望升级到新.NET系统的客户使用。他从未使用.NET应用程序代码,因此从未在应用程序级别看到数据限制。
因此,他不得不依赖传递给他的信息和数据库级别的数据约束。如果数据库级别的约束不正确或缺失,则会导致数据不良并支持来自客户的调用。这种情况比我们想要的次数多,因为数据约束并不总是从应用程序级别复制到数据库级别。
答案 11 :(得分:0)
最好在数据库级别强制执行这些约束,因为它们性能更高(在C中实现)。可以在另一层上复制验证,因为它提供了更加用户友好的错误和验证消息。
这不是XOR主张,最好在安全方面犯错,在数据库上强制执行这些约束会使您的系统更加d。
答案 12 :(得分:0)
取决于。
如果要构建要嵌入到您正在构建的单个应用程序中的数据库,则可以选择将数据验证放在DBMS或应用程序中或两者中。几乎所有由数据库新手经验丰富的程序员构建的数据库都适合这一类。在这种情况下,其他一些回复会回答您的问题。
但是,如果您正在构建一个用于存储从多个应用程序接收的数据的数据库,并且某些应用程序的编程超出了您的控制范围,那么您必须构建DBMS以防御,而不是允许来自破坏的应用程序的错误数据会感染您提供给其他应用程序和交互式用户的数据。你无法捕捉到所有错误,但你可以捕获很多错误。
至少应该设计表格,使其至少有一个可能的主键(可能的主键称为候选键)。您应该从候选键中选择一个主键,并将其声明为主键约束。这可以强制实体完整性。
此外,您应该为每个外键声明一个“引用”约束。这会强制引用完整性。使用合适的DBMS,即使外键是可选的,您也应该能够强制引用完整性,换句话说可能是NULL。 NULL当然不是指任何东西。
将这两种验证移动到应用程序是没有意义的。无论如何,应用程序必须往返数据库以检测违反规则的行为。
在我看来,业务逻辑不应该在数据库中的想法是对数据库的全部误解。同样,如果您的数据库嵌入在一个应用程序中,那么请适合自己。
对于禁止缺失值(NULLS)的验证规则,在应用程序和数据库中实现它们没有任何害处。在许多情况下,这是正确的做法。与范围检查类似。
对于非常大的项目,您需要一个单独的文档来概述数据的所有业务规则。本文档应说明规则的执行位置,数据库,应用程序或两者。
答案 13 :(得分:0)
尽管DBA喜欢确保数据库级别的验证,但这绝对不切实际:您必须验证JavaScript中的数据 ,并且通常在客户端验证数据为Web 2.0 +。
即使您通过创建约束来验证所有内容,您将如何向用户显示验证错误?作为底层数据库引擎的答案?即使支持API答案,最好将这些数据库答案映射到更有意义的API方面。返回“拒绝访问”而不是“未找到记录”。
假设所有3个方面:客户端,API和数据库。实现客户端,因为它对于出色的用户体验和降低服务器负载是必要的。使DB成为第二只是为了保持所有数据的约束。实现API(服务器端)验证,使API用户可以区分所有消息(免责声明:我不是Node社区公关人员,但使用Node.js服务器甚至是个好主意,这样你可以重用你的客户端验证码)。
答案 14 :(得分:-1)
我同意这个问题的答案取决于环境。
在我目前的环境中,我们的应用程序只有两个开发人员和不到一千个用户。如果您可以确保您的编程实践包括在应用程序层中实现业务逻辑的要求,那么您可以成功处理数据库外部的约束。
如果您的应用程序需要扩展良好并且最终将由大量开发人员维护并被无数用户使用,则可能在数据库中实施数据约束可以提高效率并消除潜在的灾难。应用层的变化。