翻译:吴嘉俊 ,叩丁狼高级讲师。
[译者注:这篇文章是开源项目CUBA Platform的作者,在这篇文章中,作者阐述了CUBA平台中关于数据校验的设计思想和使用方式,可以作为大家在设计数据校验方面一个比较好的参考。]
我接触到的很多项目中,对数据校验这方面内容都没有一个很明确的策略。这些团队常常面对即将临近的交付期压力,不明确的项目续期,所以根本没有太多时间来规划和实现项目中的校验策略。所以,你可以看到,数据校验的代码零散的分布在整个应用中:Javascript中有,Java控制器中有,业务逻辑代码中有,实体模型中有,数据库中还有约束和触发器。用于数据校验的代码,充斥着各种if..else..,在不同位置抛出完全混乱的异常,甚至想找到一个数据究竟在哪里验证的,心里面都想骂一句FUCK。长此以往,当项目越来越复杂,验证会越来越难控制,陷入极为难堪的维护境地。
那么,是否有一种优雅的,标准的,简单的方法来处理应用中的数据校验呢?这个方法既让我们的代码可读性较高,又能将大部分的数据校验代码集中管理,还能很好的集成进入目前主流的Java框架呢?
是的,有这种方法。
我们开发了CUBA Platform(https://www.cuba-platform.com/),能让我们按照最佳实践的方式来完成。我们归纳除了关于校验代码的一些要求:
- 能重复使用,遵循DRY原则;
- 能自然清晰的表达验证规则;
- 放在程序员愿意放置的位置;
- 能够支持从不同的数据源中获取数据,比如用户输入,SOAP或者REST请求等;
- 支持并发处理;
- 应用隐式的去调用,而不需要处处都通过手动调用;
- 展示清晰,本地化的提示信息供开发者使用;
- 遵循业界已有标准;
在这篇文章中,我们会使用一个基于CUBA平台的应用来作为示例。CUBA是基于Spring和EclipseLink平台的,所以,文中大部分的例子也能在其他支持JPA和Java bean校验的标准平台上面执行。
数据库约束校验
最普遍,最直接的数据校验可能就是使用数据库级别的约束,比如Require(或者NOT NULL),字符串长度,唯一索引等等,特别在企业应用中,大部分以数据库为中心,这是非常常见的方法。但是,因为开发人员分工职责不同,常常在不同的应用层中,重复定义数据约束,这就非常容易出错。
我们来举一个例子,我们大多数开发都见过或者参与过。如果在需求中提出,护照这个字段需要10位数字,那么,最可能的情况回事这样:DB工程师,会在DDL中限制,后台开发会在实体和REST服务中检查,最后,UI层开发会在前端(客户端)限制。好了,过了一段时间,需求修改了,护照字段改成15位了,技术修改了数据库中的定义,但客户端仍然只能输入10位数据。
所有人都知道如何避免出现这个问题,那就是数据校验必须集中!在CUBA平台中,这个集中点就是在实体类中使用JPA注解。基于这些元数据信息,CUBA平台能够生成正确的DDL,并且在客户端生成正确的校验器,如下图所示。
如果JPA注解发生了变化,CUBA会及时生成修改补丁脚本,下一次再部署应用的时候,基于新的JPA限制会在DB和UI端更新。
为了隔离数据库的复杂性,让生成的DDL脚本保持标准,避免引入数据库相关的独特的触发器或者存储过程,JPA注解仅仅只能做一些最基本的校验,比如,保证实体字段的唯一性,必要性,或者定义字符串长度,此外,还可以使用@UniqueConstraint注解来完成复合唯一约束等,但这些仍远远不够。
在需要更加复杂的验证逻辑,比如