【IT168 评论】关系型数据库提供了丰富的特性来保证数据的完整性。然而,除了常用的设计规范,很少有企业应用利用这些功能特性,相反,他们倾向于在应用层里实现对数据完整性的检查。如果sql数据约束有价值的话,为什么那么多应用不使用它呢?
关于数据管理,有一个很重要的趋势,特别是在Web应用上,人们乐于使用弱SQL模式 — 或者是完全避免忽略关系数据的一些基本原则。在Artima最近一个讨论是关于“NoSQL”运动的,跟以前一样 — 通常的讨论结果基本认为数据库关系约束很有用处,开发者需要自己去承担不使用约束带来的后果。
这显而易见,规范的关系数据模型存在之初就是为了保证数据的完整性的。用户通常会认为数据库是一个有保障的数据来源:它为我们常用的数据查询提供了一个基本的概念,就像SQL查询,它返回的数据就是一种“正确无误”的数据。但如果你把关系约束给禁掉了,把大部分的数据完整性维护放在应用层的代码里,那对这种“正确无误”的保证是很困难的。
很多的关于NoSQL的讨论都是围绕着反数据库标准的问题 — 弱化SQL规范会导致脏数据的产生 — 现代数据管理制度,比如这个SQL标准,提供了很多额外的机制去确保数据的完整性。在细读了最新版的 Joe Celko's SQL for Smarties — 被高度推荐的一本书 — 它让我明白应该用最少的SQL功能来保证数据的完整性。例如,我建表时从来不用SQL检查机制,我也不使用其它的标准SQL工具,为的就是让数据库更加干净。
尽管我很相信尽量使用数据管理系统的诸多好处,我还是倾向于在domain和controller层的代码里实现数据完整性相关的操作。当然,所有的代码都需要认真的测试。最近我统计了为数不少的我用 ScalaTest 写成的测试套件,大概有上百个测试用例,我发现有不低于30%的测试用例会要求在应用层强制检查数据完整约束。举个例子,测试用例会检查 API-level 的前提条件 — 比如,检查null值是否被正确的处理。如果一个复杂的对象被当作一个方法的参数,我会写代码测试这个方法能否在这个复杂对象参数状态异常时正常处理。
我一直这样做,即使是在实体层使用了 Hibernate Validator 、认真的声明了 entity classes 里需要校验的约束条件。最后,我们好要认真的在表现层 — 对于我,这里是Flex — 写大量的数据完整性检查代码去确保用户提交有效的数据到server端API里。表现层的代码也有测试用例。总之,由于此,我们在实体层、表现层、数据层编码来确保数据完整性,同时在测试用例里相当的一部分都用在覆盖对控制层、表现层的完整性检查。
这样会有大量的代码直接违反DRY原则。我所知道的对非冗余数据完整性校验最有好感的程序是在 ActiveRecord 里 , Rails' O/R mapping and persistence layer. ActiveRecord 在实体层类里声明完整性约束。 Rail 的控制层和表现层使用这些数据资料可以在数据有问题时向用户显示友好的错误信息。然而,跟 Hibernate Validator 相比, ActiveRecord 很少使用数据库丰富的底层提供的功能:这两个框架生成的约束性的sql语句几乎对外键和 non-null 域的声明有所保留,而其他的约束检查是在某种校验层实现的(部分约束在实体类里)。
我想,在一个较为理想的方案里,数据库应该对数据有保护作用,包括强制约束。这种效果我们也可以通过写大量的SQL来实现,但这不会是个很合适的方法,因为大多数的SQL数据库都依赖于SQL错误码来指示约束异常。例如,设置一个约束,salsry 字段不能接受小于5000或者大于100000的数据,当用户试图插入一个100的薪水值时数据库会返回一个错误码 — 但这个错误码很难被转换成应用中其它层中可以使用的信息。我们不去这样做是因为这样做的价值很小,特别是这种转换在不同的数据库之间还不能兼容。
你认为什么地方是最合适的定义数据约束的地方呢?你是否同意SQL应该提供更多的标准错误码来提示数据违法约束条件呢?