目录
- 1.1 前端代码规范
- 1.1.1 代码风格
- 1.1.2 语言特性
- 1.1.3 浏览器环境
- 1.2 后端代码规范
- 1.2.1 编程规约
- 1.2.1.1 命名风格
- 1.2.1.2 常量定义
- 1.2.1.3 代码格式
- 1.2.1.4 OOP规约
- 1.2.1.5 控制语句
- 1.2.1.6 注释规约
- 1.2.1.7 其它
- 1.2.2 异常处理
- 1.2.3 单元测试
- 1.2.4 MySQL数据库
- 1.2.4.1 建表规约
- 1.2.4.2 索引规约
- 1.2.4.3 SQL语句
- 1.2.4.4 ORM映射
1.1 前端代码规范
1.1.1 代码风格
文件
- JavaScript 文件使用无
BOM
的 UTF-8
编码。 - 在文件结尾处,保留一个空行。
结构
- 缩进
- 使用
4
个空格做为一个缩进层级,不允许使用 2
个空格 或 tab
字符。 switch
下的 case
和 default
必须增加一个缩进层级。
- 空格
- 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。
- 用作代码块起始的左花括号
{
前必须有一个空格。 if / else / for / while / function / switch / do / try / catch / finally
关键字后,必须有一个空格。- 在对象创建时,属性中的
:
之后必须有空格,:
之前不允许有空格。 - 函数声明、具名函数表达式、函数调用中,函数名和
(
之间不允许有空格。 ,
和 ;
前不允许有空格。如果不位于行尾,,
和 ;
后必须跟一个空格。- 在函数调用、函数声明、括号表达式、属性访问、
if / for / while / switch / catch
等语句中,()
和 []
内紧贴括号部分不允许有空格。 - 单行声明的数组与对象,如果包含元素,
{}
和 []
内紧贴括号部分不允许包含空格. - 行尾不得有多余的空格。
- 换行
- 每个独立语句结束后必须换行。
- 每行不得超过
120
个字符。 - 运算符处换行时,运算符必须在新行的行首。
- 在函数声明、函数表达式、函数调用、对象创建、数组创建、
for
语句等场景中,不允许在 ,
或 ;
前换行。 - 不同行为或逻辑的语句集,使用空行隔开,更易阅读。
- 在语句的行长度超过
120
时,根据逻辑条件合理缩进。 - 对于
if...else...
、try...catch...finally
等语句,推荐使用在 }
号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。
- 语句
- 不得省略语句结束的分号。
- 在
if / else / for / do / while
语句中,即使只有一行,也不得省略块 {...}
。 - 函数定义结束不允许添加分号。
IIFE
必须在函数表达式外添加 (
,非 IIFE
不得在函数表达式外添加 (
。
命名
变量
使用 Camel命名法
。常量
使用 全部字母大写,单词间下划线分隔
的命名方式。函数
使用 Camel命名法
。- 函数的
参数
使用 Camel命名法
。 类
使用 Pascal命名法
。- 类的
方法
/ 属性
使用 Camel命名法
。 枚举变量
使用 Pascal命名法
,枚举的属性
使用 全部字母大写,单词间下划线分隔
的命名方式。命名空间
使用 Camel命名法
。- 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。
类名
使用 名词
。函数名
使用 动宾短语
。boolean
类型的变量使用 is
或 has
开头。Promise对象
用 动宾短语的进行时
表达。
注释
单行注释
必须独占一行。//
后跟一个空格,缩进与下一行被注释说明的代码一致。
多行注释
避免使用 /*...*/
这样的多行注释。有多行注释内容时,使用多个单行注释。
文档化注释
为了便于代码阅读和自文档化,以下内容必须包含以 /**...*/
形式的块注释中。
文档注释前必须空一行。
自文档化的文档说明 what,而不是 how。
类型定义
- 类型定义都是以
{
开始, 以 }
结束。 - 对于基本类型 {string}, {number}, {boolean},首字母必须小写。
文件注释
- 文件顶部必须包含文件注释,用
@file
标识文件说明。 - 文件注释中可以用
@author
标识开发者信息。
命名空间注释
命名空间使用 @namespace
标识。
1.1.2 语言特性
变量
- 变量、函数在使用前必须先定义。
- 每个
var
只能声明一个变量。 - 变量必须
即用即声明
,不得在函数或其它形式的代码块起始位置统一声明所有变量。
条件
- 在 Equality Expression 中使用类型严格的
===
。仅当判断 null
或 undefined
时,允许使用 == null
。 - 尽可能使用简洁的表达式。
- 按执行频率排列分支的顺序。
- 如果函数或全局中的
else
块后没有任何语句,可以删除 else
。
循环
- 不要在循环体中包含函数表达式,事先将函数提取到循环体外。
- 对循环内多次使用的不变值,在循环外用变量缓存。
类型
字符串
- 字符串开头和结束使用单引号
'
。 - 使用
数组
或 +
拼接字符串。 - 使用字符串拼接的方式生成HTML,需要根据语境进行合理的转义。
- 复杂的数据到视图字符串的转换过程,选用一种模板引擎。
对象
- 使用对象字面量
{}
创建新 Object
。 - 对象创建时,如果一个对象的所有
属性
均可以不添加引号,建议所有 属性
不添加引号。 - 对象创建时,如果任何一个
属性
需要添加引号,则所有 属性
建议添加 '
。 - 不允许修改和扩展任何原生对象和宿主对象的原型。
- 属性访问时,尽量使用
.
。 for in
遍历对象时, 使用 hasOwnProperty
过滤掉原型中的属性。
数组
- 使用数组字面量
[]
创建新数组,除非想要创建的是指定长度的数组。 - 不因为性能的原因自己实现数组排序功能,尽量使用数组的
sort
方法。 - 清空数组使用
.length = 0
。
函数
函数长度
一个函数的长度控制在 50
行以内。
参数设计
- 一个函数的参数控制在
6
个以内。 - 通过
options
参数传递非数据输入型参数。
闭包
- 在适当的时候将闭包内大对象置为
null
。 - 使用
IIFE
避免 Lift 效应
。
空函数
- 空函数不使用
new Function()
的形式。 - 对于性能有高要求的场合,建议存在一个空函数的常量,供多处使用共享。
9.面向对象
- 类的继承方案,实现时需要修正
constructor
。 - 属性在构造函数中声明,方法在原型中声明。
- 自定义事件的
事件名
必须全小写。 - 自定义事件只能有一个
event
参数。如果事件需要传递较多信息,应仔细设计事件对象。 - 设计自定义事件时,应考虑禁止默认行为。
动态特性
eval
- 避免使用直接
eval
函数。 - 尽量避免使用
eval
函数。
动态执行代码。
使用 new Function
执行动态代码。
with
尽量不要使用 with
。
对象属性
避免修改外部传入的对象。
1.1.3 浏览器环境
模块化
DOM
1.2 后端代码规范
1.2.1 编程规约
1.2.1.1 命名风格
- 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
- 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
- 类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:DO / BO / DTO / VO / AO
- 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式。
例:localValue / getHttpMessage() / inputUserId
- 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
- 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾。
- 中括号是数组类型的一部分,数组定义如下:String[] args;
- POJO 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。
- 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
- 杜绝完全不规范的缩写,避免望文不知义。
- 为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词组合来表达其意。
- 如果模块、接口、类、方法使用了设计模式,在命名时体现出具体模式。
- 接口类中的方法和属性不要加任何修饰符号(public 也不要加),保持代码的简洁性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是 与接口方法相关,并且是整个应用的基础常量。
- 接口和实现类的命名
- 枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。
- 各层命名规约:
- Service/DAO 层方法命名规约
- 获取单个对象的方法用 get 做前缀。
- 获取多个对象的方法用 list 做前缀。
- 获取统计值的方法用 count 做前缀。
- 插入的方法用 save/insert 做前缀。
- 删除的方法用 remove/delete 做前缀。
- 修改的方法用 update 做前缀。
- 领域模型命名规约
- 数据对象:xxxDO,xxx 即为数据表名。
- 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
- 展示对象:xxxVO,xxx 一般为网页名称。
- POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
1.2.1.2 常量定义
- 不允许任何魔法值(即未经定义的常量)直接出现在代码中。
- long或者Long初始赋值时,使用大写的L,不能是小写的l,小写容易跟数字1混 淆,造成误解。
- 不要使用一个常量类维护所有常量,按常量功能进行归类,分开维护。
- 常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包 内共享常量、类内共享常量。
- 跨应用共享常量:放置在二方库中,通常是client.jar中的constant目录下。
- 应用内共享常量:放置在一方库中,通常是modules中的constant目录下。
- 子工程内部共享常量:即在当前子工程的constant目录下。
- 包内共享常量:即在当前包下单独的constant目录下。
- 类内共享常量:直接在类内部private static final定义。
- 如果变量值仅在一个范围内变化,且带有名称之外的延伸属性,定义为枚举类。下面 正例中的数字就是延伸信息,表示星期几。
例:public Enum { M0NDAY(1), TUESDAY(2), WEDNESDAY©), THURSDAY(4), FRIDAY(5), SATURDAY(6), SUNDAY(7);}
1.2.1.3 代码格式
- 大括号的使用约定。如果是大括号内为空,则简洁地写成{}即可,不需要换行;如果 是非空代码块则:
- 左大括号前不换行。
- 左大括号后换行。
- 右大括号前换行。
- 右大括号后还有else等代码则不换行;表示终止的右大括号后必须换行。
- 左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格。
- if/for/while/switch/do等保留字与括号之间都必须加空格。
- 任何二目、三目运算符的左右两边都需要加一个空格。 说明:运算符包括赋值运算符=、逻辑运算符&&、加减乘除符号等。
- 采用4个空格缩进,禁止使用tab字符。
说明:如果使用tab缩进,必须设置1个tab为4个空格。IDEA设置tab为4个空格时, 请勿勾选 Use tab character;而在 eclipse 中,必须勾选 insert spaces for tabs
例:(涉及1-5点)
public static void main(String[] args) {
//缩进4个空格
String say = "hello";
//运算符的左右必须有一个空格
int flag = 0;
//关键词if与括号之间必须有一个空格,括号内的f与左括号,0与右括号不需要空格
if (flag == 0) (
System.out.println(say);
)
//左大括号前加空格且不换行;左大括号后换行
if (flag == 1) (
System.out.println("world");
//右大括号前换行,右大括号后有else,不用换行
) else ( System.out.println("ok");
//在右大括号后直接结束,则必须换行
)
)
- 单行字符数限制不超过120个,超出需要换行
- 方法参数在定义和传入时,多个参数逗号后边必须加空格。
- IDE的text file encoding设置为UTF-8
- 方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行。相同业务逻辑和语义之间不需要插入空行。
1.2.1.4 OOP规约
- 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
- 外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。
- 当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起, 便于阅读。
- 类内方法定义顺序依次是:公有方法或保护方法 > 私有方法> getter/setter方法。
- final可以声明类、成员变量、方法、以及本地变量,下列情况使用final关键字:
- 不允许被继承的类,如:String类。
- 不允许修改引用的域对象,如:POJO类的域变量。
- 不允许被重写的方法,如:POJO类的setter方法。
- 不允许运行过程中重新赋值的局部变量。
- 避免上下文重复使用一个变量,使用final描述可以强制重新定义一个变量,方便更好 地进行重构。
- 慎用Object的clone方法来拷贝对象。
- 类成员与方法访问控制从严:
- 如果不允许外部直接通过new来创建对象,那么构造方法必须是private。
- 工具类不允许有public或default构造方法。
- 类非static成员变量并且与子类共享,必须是protected。
- 类非static成员变量并且仅在本类使用,必须是private。
- 类static成员变量如果仅在本类使用,必须是private。
- 若是static成员变量,必须考虑是否为final。
- 类成员方法只供类内部调用,必须是private。
- 类成员方法只对继承类公开,那么限制为protectedo
1.2.1.5 控制语句
- 在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程 序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且 放在最后,即使它什么代码也没有。
- 在if/else/for/while/do语句中必须使用大括号。即使只有一行代码,避免采用单行的编码方式:if (condition) statements;
- 表达异常的分支时,少用if-else方式
- 除常用方法(如getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复 杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
- 循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、 获取数据库连接,进行不必要的try-catch操作(这个try-catch是否可以移至循环体外)。
- 接口入参保护,这种场景常见的是用于做批量操作的接口。
- 下列情形,需要进行参数校验:
- 调用频次低的方法。
- 执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参 数错误导致中间执行回退,或者错误,那得不偿失。
- 需要极高稳定性和可用性的方法。
- 对外提供的开放接口,不管是RPC/API/HTTP接口。
- 敏感权限入口。
- 下列情形,不需要进行参数校验:
- 极有可能被循环调用的方法。但在方法说明里必须注明外部参数检查要求。
- 底层调用频度比较高的方法。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底 层才会暴露问题。一般DAO层与Service层都在同一个应用中,部署在同一台服务器中,所 以DAO的参数校验,可以省略。
- 被声明成private只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参 数已经做过检查或者肯定不会有问题,此时可以不校验参数。
1.2.1.6 注释规约
- 类、类属性、类方法的注释必须使用Javadoc规范,不得使用 // xxx方式。
- 所有的抽象方法(包括接口中的方法)必须要用Javadoc注释、除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能。
- 所有的类都必须添加创建者和创建日期。
- 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
- 所有的枚举类型字段必须要有注释,说明每个数据项的用途。
- 与其“半吊子”英文来注释,不如用中文注释把问题说清楚。专有名词与关键字保持 英文原文即可。
- 代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑 等的修改。
- 谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。 说明:代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。前者如果没 有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。
- 对于注释的要求:
- 能够准确反应设计思想和代码逻辑;
- 能够描述业务含 义,使别的程序员能够迅速了解到代码背后的信息。
- 好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的 一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。
- 特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描, 经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
待办事宜(TODO):(标记人,标记时间,[预计处理时间])
表示需要实现,但目前还未实现的功能。这实际上是一个Javadoc的标签,目前的Javadoc 还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个Javadoc标签)。
错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])
1.2.1.7 其它
- 在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。
- velocity调用POJO类的属性时,建议直接使用属性名取值即可,模板引擎会自动按 规范调用POJO的getXxx(),如果是boolean基本数据类型变量(boolean命名不需要加is 前缀),会自动调用isXxx()方法。
- 后台输送给页面的变量必须加$!{var}—中间的感叹号。 说明:如果var = null或者不存在,那么${var}会直接显示在页面上。
- 注意Math . random()这个方法返回是double类型,注意取值的范围0<x<1 (能够 取到零值,注意除零异常),如果想获取整数类型的随机数,不要将x放大10的若干倍然后 取整,直接使用Random对象的nextInt或者nextLong方法。
- 获取当前毫秒数 System.currentTimeMillis();而不是 new Date() .getTime(); 说明:如果想获取更加精确的纳秒级时间值,使用System.nanoTime()的方式。在JDK8中, 针对统计时间等场景,推荐使用Instant类。
- 不要在视图模板中加入任何复杂的逻辑。
- 任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。
- 及时清理不再使用的代码段或配置信息。
1.2.2 异常处理
- Java类库中定义的一类RuntimeException可以通过预先检查进行规避,而不应该 通过 catch 来处理。
- 对大段代码进行try-catch,这是不负责任的表现。catch时请分清稳定代码和非稳 定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的catch尽可能进行区分 异常类型,再做对应的异常处理。
- 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请 将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的 内容。
- 有try块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意手动回 滚事务。
- finally块必须对资源对象、流对象进行关闭,有异常也要做try-catch。
- 不能在finally块中使用return,finally块中的return返回后方法结束执行,不 会再执行try块中的return语句。
- 捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。 说明:如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。
- 方法的返回值可以为null,不强制返回空集合,或者空对象等,必须添加注释充分 说明什么情况下会返回null值。调用方需要进行null判断防止NPE问题。
- 防止NPE,是程序员的基本修养,注意NPE产生的场景:
- 返回类型为基本数据类型,return包装数据类型的对象时,自动拆箱有可能产生NPE。
- 数据库的查询结果可能为null。
- 集合里的元素即使isNotEmpty,取出的数据元素也可能为null。
- 远程调用返回对象时,一律要求进行空指针判断,防止NPE。
- 对于Session中获取的数据,建议NPE检查,避免空指针。
- 级联调用obj.getA().getB().getC(); 一连串调用,易产生NPE。
- 定义时区分unchecked / checked异常,避免直接抛出new RuntimeException(), 更不允许抛出Exception或者Throwable,应使用有业务含义的自定义异常。推荐业界已定义 过的自定义异常,如:DAOException / ServiceException 等。
- 避免出现重复的代码。
1.2.3 单元测试
- 好的单元测试必须遵守AIR原则。
- A: Automatic (自动化)
- I: Independent (独立性)
- R: Repeatable (可重复)
- 单元测试应该是全自动执行的,并且非交互式的。测试框架通常是定期执行的,执行 过程必须完全自动化才有意义。输出结果需要人工检查的测试不是一个好的单元测试。单元测 试中不准使用System.out来进行人肉验证,必须使用assert来验证。
- 保持单元测试的独立性。为了保证单元测试稳定可靠且便于维护,单元测试用例之间 决不能互相调用,也不能依赖执行的先后次序。
- 单元测试是可以重复执行的,不能受到外界环境的影响。
- 对于单元测试,要保证测试粒度足够小,有助于精确定位问题。单测粒度至多是类级 另U, 一般是方法级别。
- 核心业务、核心应用、核心模块的增量代码确保单元测试通过。
- 单元测试代码必须写在如下工程目录:src/test/java,不允许写在业务代码目录下。 说明:源码构建时会跳过此目录,而单元测试框架默认是扫描此目录。
- 单元测试的基本目标:语句覆盖率达到70%;核心模块的语句覆盖率和分支覆盖率都 要达到100%
- 编写单元测试代码遵守BCDE原则,以保证被测试模块的交付质量。
- B: Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
- C: Correct,正确的输入,并得到预期的结果。
- D: Design,与设计文档相结合,来编写单元测试。
- E: Error,强制错误信息输入(如:非法数据、异常流程、非业务允许输入等),并得 到预期的结果。
- 对于数据库相关的查询,更新,删除等操作,不能假设数据库里的数据是存在的, 或者直接操作数据库把数据插入进去,请使用程序插入或者导入数据的方式来准备数据。
- 和数据库相关的单元测试,可以设定自动回滚机制,不给数据库造成脏数据。或者 对单元测试产生的数据有明确的前后缀标识。
- 对于不可测的代码建议做必要的重构,使代码变得可测,避免为了达到测试要求而 书写不规范测试代码。
- 在设计评审阶段,开发人员需要和测试人员一起确定单元测试范围,单元测试最好 覆盖所有测试用例(UC)。
- 单元测试作为一种质量保障手段,不建议项目发布后补充单元测试用例,建议在项 目提测前完成单元测试。
- 为了更方便地进行单元测试,业务代码应避免以下情况:
- 构造方法中做的事情过多。
- 存在过多的全局变量和静态方法。
- 存在过多的外部依赖。
- 存在过多的条件语句。
1.2.4 MySQL数据库
1.2.4.1 建表规约
- 表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint (1表示是,0表示否)。
- 表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只 出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
- 表名不使用复数名词。
- 禁用保留字,如desc、range、match、delayed等,请参考MySQL官方保留字。
- 主键索引名为pk_字段名;唯一索引名为uk_字段名;普通索引名则为idx_字段名。 说明:pk_ 即 primary key; uk_ 即 unique key; idx_ 即 index 的简称。
- 小数类型为decimal,禁止使用float和double。
- 如果存储的字符串长度几乎相等,使用char定长字符串类型。
- varchar是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长 度大于此值,定义字段类型为text,独立出来一张表,用主键来对应,避免影响其它字段索 引效率。
- 表必备三字段:id, gmt_create, gmt_modified。
- 表的命名最好是加上“业务名称_表的作用”。
- 库名与应用名称尽量一致。
- 如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。
- 字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循:
- 不是频繁修改的字段。
- 不是varchar超长字段,更不能是text字段。
- 单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。
- 合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检 索速度。
1.2.4.2 索引规约
- 业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。
- 超过三个表禁止join。需要join的字段,数据类型必须绝对一致;多表关联查询时, 保证被关联的字段需要有索引。
- 在varchar字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据 实际文本区分度决定索引长度即可。
- 页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。
- 如果有order by的场景,请注意利用索引的有序性。order by最后的字段是组合 索引的一部分,并且放在索引组合顺序的最后,避免出现file_sort的情况,影响查询性能。
- 利用覆盖索引来进行查询操作,避免回表。
- 利用延迟关联或者子查询优化超多分页场景。
- SQL性能优化的目标:至少要达到range级别,要求是ref级别,如果可以是consts 最好。
- 建组合索引的时候,区分度最高的在最左边。
- 防止因字段类型不同造成的隐式转换,导致索引失效。
- 创建索引时避免有如下极端误解:
- 宁滥勿缺。认为一个查询就需要建一个索引。
- 宁缺勿滥。认为索引会消耗空间、严重拖慢更新和新增速度。
- 抵制惟一索引。认为业务的惟一性一律需要在应用层通过“先查后插”方式解决。
1.2.4.3 SQL语句
不要使用count(列名)或count(常量)来替代count(),count()是SQL92定义的 标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关。
count(distinct col)计算该列除NULL之外的不重复行数,注意count(distinct col1, col2)如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0。
当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为 NULL,因此使用sum()时需注意NPE问题。
使用ISNULL()来判断是否为NULL值。
在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的分页语句。
不得使用外键与级联,一切外键概念必须在应用层解决。
禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
数据订正时,删除和修改记录时,要先select,避免出现误删除,确认无误才能执 行更新语句。
in操作能避免则避免,若实在避免不了,需要仔细评估in后边的集合元素数量,控 制在1。。。个之内。
如果有全球化需要,所有的字符存储与表示,均以utf-8编码,注意字符统计函数 的区别。
如果需要存储表情,那么选择utfmb4来进行存储,注意它与utf-8编码的区别。
TRUNCATE TABLE比DELETE速度快,且使用的系统和事务日志资源少,但TRUNCATE 无事务且不触发trigger,有可能造成事故,故不建议在开发代码中使用此语句。
1.2.4.4 ORM映射
在表查询中,一律不要使用星号作为查询的字段列表,需要哪些字段必须明确写明。
POJO类的布尔属性不能加is,而数据库字段必须加is_,要求在resultMap中进行 字段与属性之间的映射。
不要用resultclass当返回参数,即使所有类属性名与数据库字段一一对应,也需 要定义;反过来,每一个表也必然有一个与之对应。
sql.xml配置参数使用:#{},#param#不要使用${}此种方式容易出现SQL注入。
iBATIS 自带的 queryForList(String statementName, int start, int size)不推 荐使用。
不允许直接拿HashMap与Hashtable作为查询结果集的输出。
更新数据表记录时,必须同时更新记录对应的gmt_modified字段值为当前时间。
不要写一个大而全的数据更新接口。传入为POJO类,不管是不是自己的目标更新字 段,都进行叩date table set c1=value1,c2=value2, c3=value3;这是不对的。执行 SQL 时,不要更新无改动的字段,一是易出错;二是效率低;三是增加binlog存储。
@Transactional事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需 要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
<isEqual>中的compareValue是与属性值对比的常量,一般是数字,表示相等时带 上此条件;<isNotEmpty>表示不为空且不为null时执行;<isNotNull>表示不为null值时 执行。