是否应该在数据库级别和应用程序级别强制执行约束?

我一直在阅读Dan Chak的“Enterprise Rails”一书,这让我想:你觉得在数据库级别和应用程序级别都应该有数据限制吗? 或者你觉得像Ruby on Rails这样的自以为是的框架 – 数据库只是一个“愚蠢的数据库”,所有的检查都应该在你的应用程序中完成(我不想在这里单独列出RoR – 我是我自己的Rails的狂热粉丝,但我不同意它的数据库的方法)?

就我个人而言,我觉得你应该有他们两个,以确保你的数据库和应用程序安全。 我的意思是你应该使用非空约束,给你的字段一个长度,如果知道(而不是把它们全部留在nvarchar(255) ),在你的数据库上有诸如外键检查约束触发器 ,然后通过应用程序中的业务逻辑规则来执行此操作。 海事组织这使得你的应用程序通过其用户界面健壮,并且还可以防止有人可能直接访问数据库。

我经常看到的反驳论点是它需要多less重复的逻辑; 一次在数据库级别,一次在应用程序级别 – 假设您有一个检查约束来validation产品的SKU是否进入(即它的长度大于零)。

现在,您还需要在业务逻辑中包含validation方法,以确保用户input的值长度大于零,并且还可能需要某些客户端JavaScript来在用户input数据时捕获错误。

我不认为这是一件坏事 – 是的,你有一些重复的逻辑,但最终的结果是“数据库为堡垒”的思维方式,因为如果你考虑一下,你的数据是你的应用程序中最重要的部分; 毕竟,如果数据很容易被损坏和危害,那么你的shiny的新的Web 2.0应用程序有什么好处呢?

你对此有什么想法? 数据库应该是像诺克斯堡这样一个不可逾越的堡垒,还是一个由激光防护的开放保险箱? 换句话说,你应该牺牲一些重复的逻辑来确保一个安全的数据模型,或者把所有的东西都留给你的应用程序,并简单地使用数据库来存储数据?

简而言之: 数据库应该强制约束。

为什么:

  1. 更轻松。 例如。 要在特定数据列上设置约束,只有一个地方可以设置它:列本身。 数据可能来自各种来源,但检查放在数据最终放置在哪里。
  2. 诚信。 数据库应该负责它所托pipe的数据。 不一致的数据库和没有数据库一样好。
  3. 灵活性。 新的UI开发环境经常出现。 如果数据库放手说它会照顾到这个限制,那么前端开发和functiontesting就更容易了。

是的,如果你想限制数据库中的内容。 这些图层应该尽可能彼此不同,并且您的数据库不应该依赖另一个图层来确保它遵循规则。

不能保证有问题(或恶意)的“业务逻辑”层不会将有毒数据插入到表中。 当然,如果你可以信任其他层,你可能不需要它。 但是我在一家大型机商店里工作,DBA总是不得不解决年轻的Java whippersnapper在没有足够的(任何?)testing的情况下将错误代码推出到生产环境中所造成的问题:-)。

不同开发领域之间共享的数据库表(这些都是我们的)应该始终保护自己免受错误的数据。 当应用程序A将不友好的数据放入应用程序B所使用的表中时,应用程序A的开发人员并不热衷,而是数据库pipe理员。

如果您按照数据库的Jeff Atwood学校只是一个愚蠢的数据存储和检索系统,那么你会把所有的validation在应用程序层。

不过,我发现应用程序就像小孩子一样。 没有检查他们会扔在房间周围的一切。 清理混乱将由父母决定。 在这种情况下,这将是数据库pipe理员进行清理。

但是,我认为您需要谨慎使用每个数据库数据完整性function,只是因为它在那里。 用外键约束和触发器重载数据库可能会产生比您想象的更多的问题。 我倾向于只在与表格非常接近的表格上使用外键,例如表头/明细表对。 如果你开始在任何地方添加外键,那么你最终会得到一个不可用的数据库。

我很less使用触发器。 我认为他们使数据库非常不透明。 你发出一个简单的更新/插入/删除命令,可能会发生奇怪的事情。 我想有两个地方触发器是不可避免的:

  1. 如果没有源代码写入数据库的应用程序,则需要修改该行为。 触发器是你唯一的select。

  2. 如果您正在视图上执行CRUD操作。 触发器对于插入/更新/删除操作是必需的。

我倾向于在应用程序中执行基本validation。 通过这种方式,用户可以立即得到反馈,说明有什么不对。 需要查找相关表的复杂validation最好在数据库中完成(以及应用程序的简单validation)。 我认为,某些forms的validation几乎不可能在应用程序级别保证,而不使用复杂的locking策略。

如果你有多个应用程序,可能在不同的平台上用不同的语言编写,那么把更多的validation放入数据库层的情况就会加强。 由不同程序员编写的两个或更多应用程序的可能性相当远, 最好在一个地方做。

这个世界的杰夫·阿特伍德(Jeff Atwoods)会build议你编写一个所有应用程序用于通信的Web服务。 Web服务执行数据validation。 这样做可以使数据库保持一个愚蠢的存储容器,从而使您能够切换数据库引擎。 实际上,你很less改变数据库引擎(除非你开始使用Microsoft Access!)。 如果你正在编写Web服务来集中你的数据validation,那么我认为你会过度。

如果您确定您永远不会拥有其他客户端应用程序,那么您可能会将数据库视为简单存储。 但是,如果您将拥有多个客户端应用程序,那么您将不得不在所有客户端应用程序中复制约束,这是一个糟糕的主意。 请记住其他客户端包括开发人员工具

另外,通过将数据库用作“哑库”,您很可能最终得到效率较低的应用程序。 数据库可以比应用程序更有效地完成许多工作。 为什么不利用呢?

我认为你应该不惜一切代价来保护你的数据。 没有什么比这更糟糕的了,因为应用程序有一个bug,所以试图报告数据不正确的应用程序。 现在这是什么意思?

你应该通过FK来强化你的关系,没有理由不这样做。 您应该尝试并避免使用空值,只有在空值适用时才使用它们。 我认为这是一个很好的路线。

你应该parsing一个电话号码,以确保它在正确的格式? 可能不会,但是,您应该再次将电话号码存储在没有格式问题的架构中。

通常总有一些重复,数据库不只是愚蠢的存储库。

D b

数据库确保数据级别的完整性。 数据库从根本上覆盖了外键约束,非空约束和大小约束。

你不能在数据库中做任何事情,但你可以做很多事情。 保护数据。

业务层

提升一个级别,你有业务逻辑。 通常这是您与其他应用程序(Web服务,您自己的UI等)的集成点。 这里将业务逻辑编码到应用程序中。 例如,如果一个产品的结束date是x,那么如果y有不同的结束date,它也不会出现在y中。

在数据库中描述这种规则是很难的,所以你不这样做。 但是你的业务逻辑层仍然拦截它所知道的无效的东西。 例如,如果描述字段不允许为空,则业务逻辑不应将其发送到数据库。 无论如何,它都会出错,但是你试图在已知不好的地方拦截事物。

也很难在数据库中expression其他“规则”,如“新用户如果来自Arkensas,则有2年的有效期,除非有3个孩子,其中一个被命名为Barry”。 我们可以笑这个例子,但一位经验丰富的程序员会告诉你,业务逻辑是最大的矛盾之一。

UI

移动到用户界面,用户界面也定期编码屏幕上的业务逻辑。 表单和其他页面通常会以无效的状态存在,至less在大多数情况下,UI的工作是知道规则的。 希望UI将逻辑推迟到业务层,但业务层不知道字段1是到期日,字段2是描述。

如果用户已经select了小部件Y,UI知道要用X来search产品.UI知道需要描述,并且项目数大于0且小于100(在这些示例中,良好的UI将依赖于业务层告诉它,例如,最小和最大,但用户界面仍然知道的关系)

在networking用户界面中,我们还添加了客户端脚本,这又重复了服务器代码中的逻辑。 我们使用客户端脚本来获得更好的用户体验,但是最终不要相信来自客户端的任何东西(脚本可以closures,手动操作表单域等)


所以你可以看到逻辑将被复​​制。 你尽可能地减less重复,但实际上很less有可能给一个不重要的程序。

如果在数据库中至less没有执行基本的完整性,那么在某些时候无效的数据将会进入。也许从一个应用程序。 错误,也许从某人拉起SQL控制台,无论如何。 然后,当不可能(“所有Blogging必须有Alogging!你的意思是不存在?”)发生时,你会发现你的应用程序具有有趣的失败模式。

强化应用程序的完整性只有在应用程序是唯一一个会触及该数据库的情况下才有效。 即使如此,您最好还是要小心应用程序错误,因为应用程序比架构大得多。

为了提供一个稍微不同的观点,我认为这取决于上下文。

首先,多年来,我从100%的DBMS实施约束转向试图完全避免它:我希望我所有的业务逻辑都在同一层。

其次,我使用Rails进行了很多工作,而且ActiveRecord迁移不允许在字段大小和NULL_ness之外使用大量的db驻留约束。

如果您正在构build一个新的应用程序,其中包含一个完全专用于您的应用程序的新数据库,那么您可以在应用程序代码中强制实施您的约束,作为业务逻辑的一部分。 Rails喜欢这样工作,而且看起来工作得很好。

如果你正在构build一个新的应用程序对遗传数据库,在数据库约束实现业务规则,那么我会build议继续这一点,并接受业务层中的validation规则的一些重复。 在尝试将其应用于数据库之前,最好检查一下数据是否有效。

如果您正在构build一个新的应用程序/数据库,并希望其他应用程序能够访问这些数据,那么这个方法将取决于这些其他应用程序的构build方式。 再一次,在Rails中,你可能应该想办法分享你的模型,在这种情况下,这个层就足够了。 如果你不能否认其他的实现方式直接访问你的数据,那么你就重复了。 我倾向于尝试 – 非常困难 – 拒绝直接访问这些应用程序的数据库,并努力通过(希望为REST的)Web服务来提供服务,以便您可以在业务逻辑级别pipe理数据完整性。

如果第三方(内部或其他)应用程序对您的架构有DDL访问权限,那么请不要担心这个问题 – 您已经失去了对您的数据的控制权,并且被搞砸了!

我个人的偏好是在数据库层执行基本validation,然后使用自省技术将这些约束作为默认值(约定优于configuration)提升到应用层。 如果我有一些不寻常的forms或类似的用户交互,那么我将覆盖从数据库中获得的默认值,而不pipe我需要什么新的行为。 这可以帮助我在数据库层面保持大部分基本的validation,而在应用层做更复杂的validation(即电话号码格式)。

我可以检查诸如电话号码之类的东西的限制,但是我发现检查限制是强制执行格式的困难和令人沮丧的方式。

如果我有一个需要从第二个来源接收input的应用程序(即它有一个Web界面和一个桌面客户端),那么我会试着让第二个界面通过第一个工作(通过Web服务提交桌面数据例如)或失败,那么我会回头在数据库中使用检查约束,除了在应用程序层的validation。

首先,我希望尽可能保持数据的完整性,因为不好的数据将不可避免地在应用程序中造成严重破坏 – 然后(而且同样重要),我希望应用程序能够处理用户提供的信息的validation。

从OOP的angular度来看,数据库在对象/angular色中,在更大的系统内,它应该对自己负责。 这包括validationinput的必要需求。

两个都是。 我在最后的地方发现了这一点。 我们有传统的delphi系统与sybase数据库。 新系统是.NET和Sql服务器。 一名特定员工独自负责将sybase数据库转换为SQL Server数据库,以便客户升级到新的.NET系统。 他从来没有使用.NET应用程序代码,因此从未在应用程序级别看到数据限制。

因此,他不得不依靠传递给他的信息和数据库级别的数据限制。 如果数据库级别的约束不正确或缺失,则会导致客户的数据和支持电话不良。 这发生了比我们想要的更多的时间,因为数据约束并不总是从应用程序级别复制到数据库级别。

在数据库级别执行这些约束会更好,因为它们更具性能(用C实现)。 可以在另一个层上复制validation,因为它提供了更多用户友好的错误和validation消息。

这不是异或命题,最好在安全方面犯错,对数据库执行这些限制将会使您的系统更加可靠。

这取决于。

如果您正在构build要embedded到您正在构build的单个应用程序中的数据库,那么可以select将数据validation置于DBMS或应用程序中,或者两者兼而有之。 数据库新手经验丰富的程序员几乎所有的数据库都适合这个类别。 在这种情况下,一些其他答复回答你的问题。

但是,如果您正在构build一个旨在存储从多个应用程序接收到的数据的数据库,而且其中一些应用程序的编程超出了您的控制范围,那么您必须构buildDBMS来防御,并且不允许错误的数据一个破碎的应用程序感染您提供给其他应用程序和交互式用户的数据。 你不能抓住所有的错误,但你可以捕捉到很多错误。

至less,你应该devise你的表,使他们至less有一个可能的主键(可能的主键称为候选键)。 您应该从候选键中select一个主键,并将其声明为主键约束。 这强制实体完整性。

另外,你应该为每个外键声明一个“引用”约束。 这强制参照完整性。 有了一个体面的DBMS,即使外键是可选的,也应该能够强制引用完整性,换句话说可以是NULL。 NULL当然不会引用任何东西。

将这两种validation移动到应用程序是毫无意义的。 应用程序必须往返数据库,以检测违反规则。

在我看来,业务逻辑不应该在数据库中的想法是误解了数据库是关于什么的。 再一次,如果你的数据库被embedded到一个单一的应用程序,那么适合自己。

至于禁用缺失值(NULLS)的validation规则,在应用程序和数据库中实现它们都没有什么坏处。 在许多情况下,这是正确的。 同样的范围检查。

对于非常大的项目,您需要一个单独的文档来概述数据的所有业务规则。 这个文件应该说明规则在哪里执行,在数据库中,在应用程序中或在两者中。

尽pipe数据库pipe理员喜欢确保在数据库级别进行validation,但这绝对是不切实际的:您必须validationJavaScript中的数据并且经常在客户端validation为Web 2.0+。

即使您通过创build约束来validation所有内容,您将如何向用户显示validation错误? 作为底层数据库引擎的答案? 即使为了支持API答案,将这些数据库答案映射到更有意义的API也是一个好主意。 返回“拒绝访问”而不是“未findlogging”。

假设所有三方:客户端,API和数据库。 实施客户端,因为它是伟大的UX和减less服务器上的负载所必需的。 为了保持所有的数据限制,第二个做DB。 实现API(服务器端)validation,使API用户可以区分所有消息(声明:我不是Node社区公关人员,但使用Node.js服务器甚至是个好主意,这样可以重用客户端validation码)。

我同意这个问题的答案取决于环境。

在我目前的环境中,只有两个开发人员,而我们的应用程序只有不到一千个用户。 如果您可以确保您的编程实践包括在应用程序层中实现业务逻辑的要求,那么您可以成功处理数据库之外的约束。

如果您的应用程序需要很好的扩展,并最终由大量开发人员维护,并由数量众多的用户使用,那么在数据库中实施数据约束可能会提高效率,并消除潜在的灾难应用程序层。