Validation 我应该保护我的数据库不受无效数据的影响吗?

Validation 我应该保护我的数据库不受无效数据的影响吗?,validation,database-design,Validation,Database Design,我总是倾向于通过服务层来保护我的持久性层不受侵犯。然而,我开始怀疑这是否真的有必要。当我的数据库从未真正发挥作用时,花时间让它变得健壮、建立关系和数据完整性有什么意义呢 例如,考虑电子邮件字段上具有唯一约束的用户表。我自然希望在我的服务层中编写阻止程序代码,以确保添加的电子邮件在尝试添加任何内容之前不在数据库中。在过去,我从来没有真正看到过它有什么问题,但是,由于我接触了更多的最佳实践/设计原则,我觉得这种方法不是很好 因此,始终确保进入持久层的数据确实有效是正确的,还是让无效数据进入数据库并处

我总是倾向于通过服务层来保护我的持久性层不受侵犯。然而,我开始怀疑这是否真的有必要。当我的数据库从未真正发挥作用时,花时间让它变得健壮、建立关系和数据完整性有什么意义呢

例如,考虑电子邮件字段上具有唯一约束的用户表。我自然希望在我的服务层中编写阻止程序代码,以确保添加的电子邮件在尝试添加任何内容之前不在数据库中。在过去,我从来没有真正看到过它有什么问题,但是,由于我接触了更多的最佳实践/设计原则,我觉得这种方法不是很好


因此,始终确保进入持久层的数据确实有效是正确的,还是让无效数据进入数据库并处理错误更自然?

这样做的两个原因。可以从其他应用程序访问数据库

您可能会在代码中出现一个小错误,并将数据放入数据库中,这是因为您的服务层在假设这种情况永远不会发生的情况下运行,如果您幸运的话,它会崩溃,无声数据损坏是最坏的情况

当我在代码中犯错误时,我总是将DB中的规则视为极少数情况下的后盾:

需要记住的是,如果您需要,您可以随时放松约束,在用户花费大量精力输入数据后收紧约束,问题会大得多


千万要当心那个词,在它里面,它的意思是比你希望的要快得多。

两个理由。可以从其他应用程序访问数据库

您可能会在代码中出现一个小错误,并将数据放入数据库中,这是因为您的服务层在假设这种情况永远不会发生的情况下运行,如果您幸运的话,它会崩溃,无声数据损坏是最坏的情况

当我在代码中犯错误时,我总是将DB中的规则视为极少数情况下的后盾:

需要记住的是,如果您需要,您可以随时放松约束,在用户花费大量精力输入数据后收紧约束,问题会大得多

千万要当心那个词,在它里面,它的意思是比你希望的要快得多。

请不要这样做

在并发环境中,实现即使是简单的约束(如键)也绝对不是件容易的事。例如,仅在第一步返回空结果的情况下,在一个步骤中查询数据库并允许在另一个步骤中插入是不够的——如果一个并发事务在第一步和第二步之间插入了与您尝试插入和提交的值相同的值,该怎么办?您有可能导致重复数据的竞争条件。可能最简单的解决方案是使用全局锁来序列化事务,但随后可伸缩性就消失了

对于键的插入/更新/删除操作的其他组合,以及其他类型的约束(例如外键),甚至在某些情况下检查,也存在类似的考虑

DBMS在过去几十年中已经设计出了非常聪明的方法,可以在这样的情况下保持正确和性能,同时允许您以声明的方式轻松定义约束,最大限度地减少出错的机会。所有访问同一数据库的应用程序都将自动受益于这些集中的约束

如果您必须选择哪一层代码不应该验证数据,那么数据库应该是您的最后选择

那么,始终确保进入持久层的数据确实是有效的服务层是正确的,还是让无效数据进入数据库并处理错误更自然

永远不要假设正确的数据,始终尽可能多地在数据库级别进行验证

是否在上层代码中也进行验证取决于具体情况,但在关键冲突的情况下,我会让数据库承担重任。

请不要这样做

在并发环境中,实现即使是简单的约束(如键)也绝对不是件容易的事。例如,仅在第一步返回空结果的情况下,在一个步骤中查询数据库并允许在另一个步骤中插入是不够的——如果一个并发事务在第一步和第二步之间插入了与您尝试插入和提交的值相同的值,该怎么办?您有可能导致重复数据的竞争条件。可能最简单的解决方案是使用全局锁来序列化事务,但随后可伸缩性就消失了

对于键的插入/更新/删除操作的其他组合,以及其他类型的约束(例如外键),甚至在某些情况下检查,也存在类似的考虑

DBMS已经设计出了非常聪明的方法 ecades在这种情况下既正确又有性能,但允许您以声明的方式轻松定义约束,从而最大限度地减少出错的机会。所有访问同一数据库的应用程序都将自动受益于这些集中的约束

如果您必须选择哪一层代码不应该验证数据,那么数据库应该是您的最后选择

那么,始终确保进入持久层的数据确实是有效的服务层是正确的,还是让无效数据进入数据库并处理错误更自然

永远不要假设正确的数据,始终尽可能多地在数据库级别进行验证


是否在上层代码中也进行验证取决于具体情况,但在关键违规的情况下,我会让数据库承担重任。

即使没有结论性的答案,我认为这是一个很好的问题

首先,我大力支持在数据库中至少包含基本验证,并让数据库做它擅长的事情。至少,这意味着外键(在适当情况下不为NULL)、强类型字段(在可能的情况下),例如,不要将文本字段放在整数所属的位置、唯一约束等。正如@Branko Dimitrijevic指出的,让数据库处理并发性也是至关重要的,事务原子性应由数据库拥有

如果这是适度冗余,那么就这样吧。验证太多总比验证太少好

然而,我认为业务层应该知道它正在执行的验证,即使逻辑存在于数据库中

区分异常和验证错误可能更容易。在大多数语言中,失败的数据操作可能会表现为某种异常。包括我在内的大多数人都认为在常规程序流中使用异常是不好的,我认为电子邮件验证失败并不是例外情况

更荒谬的是,想象一下,点击数据库只是为了确定用户是否填写了表单上的所有必填字段

换句话说,我宁愿调用一个方法IsEmailValid并接收一个布尔值,而不是试图确定抛出的数据库错误是否意味着该电子邮件已被其他人使用

这种方法也可以执行得更好,并避免像跳过ID这样的麻烦,因为从SQL Server的角度来看,插入操作失败

验证电子邮件的逻辑很可能存在于可重用的存储过程中,如果它比唯一约束更复杂的话

最后,这个简单的唯一约束在业务层出错时提供了最终保护

有些验证根本不需要进行数据库调用就可以成功,即使数据库可以轻松地处理它

有些验证比单独使用数据库结构/函数表示更复杂

应用程序之间的业务规则可能会有所不同,即使是针对相同的完全有效的数据

某些验证非常关键或昂贵,应该在数据访问之前进行

一些简单的约束(如字段类型/长度)可以自动执行通过ORM运行的任何操作都可能具有某种程度的自动化


虽然没有一个结论性的答案,但我认为这是一个很好的问题

首先,我大力支持在数据库中至少包含基本验证,并让数据库做它擅长的事情。至少,这意味着外键(在适当情况下不为NULL)、强类型字段(在可能的情况下),例如,不要将文本字段放在整数所属的位置、唯一约束等。正如@Branko Dimitrijevic指出的,让数据库处理并发性也是至关重要的,事务原子性应由数据库拥有

如果这是适度冗余,那么就这样吧。验证太多总比验证太少好

然而,我认为业务层应该知道它正在执行的验证,即使逻辑存在于数据库中

区分异常和验证错误可能更容易。在大多数语言中,失败的数据操作可能会表现为某种异常。包括我在内的大多数人都认为在常规程序流中使用异常是不好的,我认为电子邮件验证失败并不是例外情况

更荒谬的是,想象一下,点击数据库只是为了确定用户是否填写了表单上的所有必填字段

换句话说,我宁愿调用一个方法IsEmailValid并接收一个布尔值,而不是试图确定抛出的数据库错误是否意味着该电子邮件已被其他人使用

这种方法也可能表现得更好,避免像skippe这样的麻烦 d ID,因为从SQL Server角度讲插入失败

验证电子邮件的逻辑很可能存在于可重用的存储过程中,如果它比唯一约束更复杂的话

最后,这个简单的唯一约束在业务层出错时提供了最终保护

有些验证根本不需要进行数据库调用就可以成功,即使数据库可以轻松地处理它

有些验证比单独使用数据库结构/函数表示更复杂

应用程序之间的业务规则可能会有所不同,即使是针对相同的完全有效的数据

某些验证非常关键或昂贵,应该在数据访问之前进行

一些简单的约束(如字段类型/长度)可以自动执行通过ORM运行的任何操作都可能具有某种程度的自动化



垃圾输入,垃圾输出。@njk但这是我的观点,数据永远不会被添加到数据库中。问题是在不同的层之间复制业务规则是否正确。拥有数据完整性有性能优势。如果将规则放入数据库,然后为应用程序服务器(可能还有客户端层)自动生成规则,则不会重复自己。或者,如果您将规则结果中继到应用程序服务器或客户端垃圾输入,垃圾输出。@njk但这是我的观点,数据将永远不会添加到数据库中。问题是在不同的层之间复制业务规则是否正确。拥有数据完整性有性能优势。如果将规则放入数据库,然后为应用程序服务器(可能还有客户端层)自动生成规则,则不会重复自己。或者,如果您将规则结果转发给应用程序服务器或客户端,我不是在问您是否应该验证您的数据库-这是必须的,我是在问您是否应该在服务层保护它不受已知违规行为的影响,而不是让数据库处理错误并将其发回。@James。抱歉,我把你的问题理解为删除数据库中的规则,而不是服务层。如果DB规则存在,我不会调用服务层验证保护。我通常从方便的角度看待这个问题。要么我已经确定我有一个客户多用户可能会单步执行并更改,要么我检查空值,因为这比捕获外键冲突更快更整洁。我不是问你是否应该验证你的数据库-这是必须的,我想问的是,您是否应该在服务层保护它不受已知违规行为的影响,而不是让数据库处理错误并将其发回。@James。抱歉,我把你的问题理解为删除数据库中的规则,而不是服务层。如果DB规则存在,我不会调用服务层验证保护。我通常从方便的角度看待这个问题。要么我已经确定我有一个客户多用户可能会单步执行并更改它,要么我检查空值,因为它比捕获外键违规更快更整洁。例如+1这更符合我所追求的响应类型。我同意在服务层进行基本验证,即检查必填字段等。我关心的是更多的验证需要DB交互,比如重复检查。总之,您是否认为在服务/DB层之间设置此保护层实际上是正常的?以用户名/电子邮件的重复检查为例,我将在数据库中使用唯一约束作为故障保护来强制执行这一点,但我将使用业务层使用SELECT而不是INSERT调用数据库来执行检查。这避免了抛出异常,并允许您对返回值进行更多操作,例如建议其他用户名。总的来说,我认为坏的/错误的数据应该尽快停止。它进入系统的深度越深,风险就越大。一个应该在应用程序和db中检查的约束示例:数据在不允许的地方保留NULL。每个地方的开销都很小。通过在应用程序中禁用NULL up,如果用户在表单中遗漏了某些内容,应用程序可以返回给用户,而无需往返到db服务器。通过在DB中不允许NULL,DB可以保护自己免受恶意应用程序的攻击,甚至可以保护自己免受使用交互式SQL手动编辑数据库的DBA的攻击。仅应在DB中检查的内容示例:引用完整性。@James。我认为我们在保护的概念上有困难。如果规则在db中,那么从保护的角度来看,服务层中的规则加倍并不一定是坏事。如果他们没有,那么就没有加倍。也许一个促使你提出这个问题的例子将是一个有用的背景。我认为答案是肯定的

“那要看情况了。”托尼霍普金森我在问题中给出的例子实际上是一个经典的例子,也是一个真实的例子。我在数据库中有一个电子邮件字段,它有一个唯一的约束,可以阻止添加重复的电子邮件。我还有一个应用程序级别的检查,用于查询数据库中的重复项。我关心的是这件复制品。如果验证比这更高级,例如,数据库验证唯一的电子邮件,但服务验证电子邮件是@domain.com电子邮件,那么我可以理解。+1这更符合我所追求的响应类型。我同意在服务层进行基本验证,即检查必填字段等。我关心的是更多的验证需要DB交互,比如重复检查。总之,您是否认为在服务/DB层之间设置此保护层实际上是正常的?以用户名/电子邮件的重复检查为例,我将在数据库中使用唯一约束作为故障保护来强制执行这一点,但我将使用业务层使用SELECT而不是INSERT调用数据库来执行检查。这避免了抛出异常,并允许您对返回值进行更多操作,例如建议其他用户名。总的来说,我认为坏的/错误的数据应该尽快停止。它进入系统的深度越深,风险就越大。一个应该在应用程序和db中检查的约束示例:数据在不允许的地方保留NULL。每个地方的开销都很小。通过在应用程序中禁用NULL up,如果用户在表单中遗漏了某些内容,应用程序可以返回给用户,而无需往返到db服务器。通过在DB中不允许NULL,DB可以保护自己免受恶意应用程序的攻击,甚至可以保护自己免受使用交互式SQL手动编辑数据库的DBA的攻击。仅应在DB中检查的内容示例:引用完整性。@James。我认为我们在保护的概念上有困难。如果规则在db中,那么从保护的角度来看,服务层中的规则加倍并不一定是坏事。如果他们没有,那么就没有加倍。也许一个促使你提出这个问题的例子将是一个有用的背景。“我想答案是,这要看情况而定。”托尼霍普金森我在问题中给出的例子实际上是一个经典的例子,也是一个真实的例子。我在数据库中有一个电子邮件字段,它有一个唯一的约束,可以阻止添加重复的电子邮件。我还有一个应用程序级别的检查,用于查询数据库中的重复项。我关心的是这件复制品。如果验证比这更高级,例如,数据库验证唯一的电子邮件,但服务验证电子邮件是@domain.com电子邮件,那么我可以理解这一点。关于并发/竞争条件,这可能是人们在该级别忘记的事情。我只是想澄清一下,虽然我不是说不要验证你的数据库-我认为不验证是一种犯罪。我想问的是,您是否应该始终拥有一个验证层,它基本上复制了DB设计用来处理的相同规则。@James取决于具体情况。有时,您不能像使用键那样真正地重新实现数据库约束,所以您应该将其留给数据库,而不要试图重复它。有时,重新实现(例如简单的检查)相当容易,在这种情况下,您可能希望纯粹为了用户体验而这样做。有时,在应用程序级别实现约束要容易得多,例如,在某些情况下,继承、EAV中存在所需属性等。,因此,只有在应用程序级别上才有例外,并执行规则。如果考虑到我在该问题中给出的示例,我想知道,如果数据库ESP的正确大小比数据库中的违规行为要多,那么查询ESP是否更有效。我想它需要进行测试和基准测试。然而,这就引出了一个问题,如果你知道它会失败,那么让它走这么远是不是一个好主意…@James这肯定是更多的工作-你只是在现有的数据库密钥执行处理之上添加了新的查询处理,所以它必须花费更多的时间。如果您需要查询数据库,那么您还不知道它会失败。如果您可以在不查询数据库的情况下以某种方式确定它将失败,例如电子邮件格式不正确,那么您将获得更大的权力-您可以完全跳过一次数据库往返…@James Al总之,我认为键和外键应该留给数据库,上层应该让数据库遇到冲突。简单的检查可能值得在上层重新实现,以避免不必要的数据库往返和用户体验。一些复杂的验证可能只存在于应用程序中
ion级别,特别是当您需要DBMS不可知解决方案时,因此希望避免在触发器和存储过程中放入太多逻辑。+1是关于并发性/竞争条件的,因为在该级别上,人们可能会忘记这一点。我只是想澄清一下,虽然我不是说不要验证你的数据库-我认为不验证是一种犯罪。我想问的是,您是否应该始终拥有一个验证层,它基本上复制了DB设计用来处理的相同规则。@James取决于具体情况。有时,您不能像使用键那样真正地重新实现数据库约束,所以您应该将其留给数据库,而不要试图重复它。有时,重新实现(例如简单的检查)相当容易,在这种情况下,您可能希望纯粹为了用户体验而这样做。有时,在应用程序级别实现约束要容易得多,例如,在某些情况下,继承、EAV中存在所需属性等。,因此,只有在应用程序级别上才有例外,并执行规则。如果考虑到我在该问题中给出的示例,我想知道,如果数据库ESP的正确大小比数据库中的违规行为要多,那么查询ESP是否更有效。我想它需要进行测试和基准测试。然而,这就引出了一个问题,如果你知道它会失败,那么让它走这么远是不是一个好主意…@James这肯定是更多的工作-你只是在现有的数据库密钥执行处理之上添加了新的查询处理,所以它必须花费更多的时间。如果您需要查询数据库,那么您还不知道它会失败。如果您可以在不查询数据库的情况下以某种方式确定它将失败,例如电子邮件格式不正确,那么您将获得更大的权力-您可以完全跳过一次数据库往返…@James Al总之,我认为键和外键应该留给数据库,上层应该让数据库遇到冲突。简单的检查可能值得在上层重新实现,以避免不必要的数据库往返和用户体验。而且一些复杂的验证可能只存在于应用程序级别,特别是当您需要DBMS不可知的解决方案时,因此希望避免在触发器和存储过程中放置太多逻辑。