阿里巴巴java开发规范解读

一. 编程规约

(一)命名风格

1.【强制】 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。

…..反例: name / __name / $name / name / name$ / name__

解读:很多程语言底层使用了这些符号做特殊的标识(fastjson对_和$开头的变量序列化bug)

2.【强制】 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。

…..说明: 正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,纯拼音命名方式更要避免采用。

…..正例: renminbi / alibaba / taobao / youku / hangzhou 等国际通用的名称, 可视同英文。

…..反例: DaZhePromotion [打折] / getPingfenByName() [评分] / int 某变量 = 3

3.【强制】 类名使用 UpperCamelCase 风格,但以下情形例外: DO / BO / DTO / VO / AO
/ PO / UID 等。

…..正例: JavaServerlessPlatform / UserDO / XmlService / TcpUdpDeal / TaPromotion

…..反例: javaserverlessplatform / UserDo / XMLService / TCPUDPDeal / TAPromotion

4.【强制】 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵
从驼峰形式。

…..正例: localValue / getHttpMessage() / inputUserId

5.【强制】 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字
长。

…..正例: MAX_STOCK_COUNT / CACHE_EXPIRED_TIME

…..反例: MAX_COUNT / EXPIRED_TIME

6.【强制】 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾; 测试类
命名以它要测试的类的名称开始,以 Test 结尾。

7.【强制】 类型与中括号紧挨相连来表示数组。

…..正例: 定义整形数组 int[] arrayDemo;

…..反例: 在 main 参数中,使用 String args[]来定义

8.【强制】 POJO 类中布尔类型变量都不要加 is 前缀,否则部分框架解析会引起序列化错误。

…..说明: 在本文 MySQL 规约中的建表约定第一条,表达是与否的值采用 is_xxx 的命名方式,所以,需要在

设置从 is_xxx 到 xxx 的映射关系。

……反例: 定义为基本数据类型 Boolean isDeleted 的属性,它的方法也是 isDeleted(), RPC 框架在反向解
析的时候, “误以为” 对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。

9.【强制】 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使
用单数形式,但是类名如果有复数含义,类名可以使用复数形式。

…..正例: 应用工具类包名为 com.alibaba.ai.util、类名为 MessageUtils(此规则参考 spring 的框架结构)。

10.【强制】 避免在子父类的成员变量之间、或者不同代码块的局部变量之间采用完全相同的命
名,使可读性降低。

…..说明: 子类、父类成员变量名相同,即使是 public 类型的变量也是能够通过编译,而局部变量在同一方法
内的不同代码块中同名也是合法的,但是要避免使用。对于非 setter/getter 的参数名称也要避免与成员
变量名称相同。

…..反例:

public class ConfusingName {
public int age;
// 非 setter/getter 的参数名称,不允许与本类成员变量同名
public void getData(String alibaba) {
if(condition) {
final int money = 531;
// ...
}
for (int i = 0; i < 10; i++) {
// 在同一方法体中,不允许与其它代码块中的 money 命名相同
final int money = 615;
// ...
}
}
}
class Son extends ConfusingName {
// 不允许与父类的成员变量名称相同
public int age;
}

11.【强制】 杜绝完全不规范的缩写, 避免望文不知义。

…..反例: AbstractClass“缩写” 命名成 AbsClass; condition“缩写” 命名成 condi,此类随意缩写严重
降低了代码的可阅读性

12.【推荐】 在常量与变量的命名时,表示类型的名词放在词尾,以提升辨识度。

…..正例: startTime / workQueue / nameList / TERMINATED_THREAD_COUNT

…..反例: startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD

13.【推荐】如果模块、 接口、类、方法使用了设计模式,在命名时需体现出具体模式。

…..说明: 将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。

…..正例: public class OrderFactory;

…..public class LoginProxy;

…..public class ResourceObserver;

14.【参考】 枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。

…..说明: 枚举其实就是特殊的类, 域成员均为常量, 且构造方法被默认强制是私有。

…..正例: 枚举名字为 ProcessStatusEnum 的成员名称: SUCCESS / UNKNOWN_REASON

15.各层命名规约:

..A) Service/DAO 层方法命名规约

…..1) 获取单个对象的方法用 get 做前缀。

……2) 获取多个对象的方法用 list 做前缀,复数形式结尾如: listObjects。

……3) 获取统计值的方法用 count 做前缀。

……4) 插入的方法用 save/insert 做前缀。

……5) 删除的方法用 remove/delete 做前缀。

……6) 修改的方法用 update 做前缀。

..B) 领域模型命名规约

……1) 数据对象: xxxDO, xxx 即为数据表名。

……2) 数据传输对象: xxxDTO, xxx 为业务领域相关的名称。

……3) 展示对象: xxxVO, xxx 一般为网页名称。

…..4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。

(二) 常量定义

16.【强制】 不允许任何魔法值( 即未经预先定义的常量) 直接出现在代码中。

…..反例: String key = “Id#taobao_” + tradeId;

…..cache.put(key, value);

…..// 缓存 get 时,由于在代码复制时,漏掉下划线,导致缓存击穿而出现问题

17.【强制】 在 long 或者 Long 赋值时, 数值后使用大写的 L,不能是小写的 l,小写容易跟数
字 1 混淆,造成误解。

…..说明: Long a = 2l; 写的是数字的 21,还是 Long 型的 2。

18.【推荐】 不要使用一个常量类维护所有常量, 要按常量功能进行归类,分开维护。

…..说明: 大而全的常量类, 杂乱无章, 使用查找功能才能定位到修改的常量,不利于理解和维护。

…..正例: 缓存相关常量放在类 CacheConsts 下;系统配置相关常量放在类 ConfigConsts 下.

(四) OOP 规约

19.【强制】 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析
成本,直接用类名来访问即可。

20.【强制】 所有的覆写方法,必须加@Override 注解。

…..说明: getObject()与 get0bject()的问题。一个是字母的 O,一个是数字的 0,加@Override 可以准确判
断是否覆盖成功。另外,如果在抽象类中对方法签名进行修改,其实现类会马上编译报错。

21.【强制】 外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产
生影响。接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什
么。

22.【强制】 不能使用过时的类或方法

23.【强制】 Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用
equals。

…..正例: “test”.equals(object);

…..反例: object.equals(“test”);

…..说明: 推荐使用 java.util.Objects#equals( JDK7 引入的工具类) 。

24.【强制】 所有整型包装类对象之间值的比较, 全部使用 equals 方法比较。

…..说明: 对于 Integer var = ? 在-128 至 127 范围内的赋值, Integer 对象是在 IntegerCache.cache 产
生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数
据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。

25.【强制】浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用
equals 来判断。

…..说明: 浮点数采用“尾数+阶码” 的编码方式,类似于科学计数法的“有效数字+指数” 的表示方式。二进Java 开发手册
8/44
制无法精确表示大部分的十进制小数,

反例:
    float a = 1.0f - 0.9f;
    float b = 0.9f - 0.8f;
    if (a == b) {
    // 预期进入此代码快,执行其它业务逻辑
    // 但事实上 a==b 的结果为 false
    }
    Float x = Float.valueOf(a);
    Float y = Float.valueOf(b);
    if (x.equals(y)) {
    // 预期进入此代码快,执行其它业务逻辑
    // 但事实上 equals 的结果为 false
    }
正例:
    (1) 使用 BigDecimal 来定义值,再进行浮点数的运算操作。
    BigDecimal a = new BigDecimal("1.0");
    BigDecimal b = new BigDecimal("0.9");
    BigDecimal c = new BigDecimal("0.8");
    BigDecimal x = a.subtract(b);
    BigDecimal y = b.subtract(c);
    if (x.equals(y)) {
    System.out.println("true");
    }

补充:对于bigdecimal的大小比较,用equals方法的话会不仅会比较值的大小,还会比较两个对象的精确度,而compareto方法则不会比较精确度,只比较数值的大小。

26.【强制】 为了防止精度损失, 禁止使用构造方法 BigDecimal(double)的方式把 double 值转
化为 BigDecimal 对象。

…..说明: BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。
如: BigDecimal g = new BigDecimal(0.1f); 实际的存储值为: 0.10000000149

……正例: 优先推荐入参为 String 的构造方法,或使用 BigDecimal 的 valueOf 方法,此方法内部其实执行了
Double 的 toString,而 Double 的 toString 按 double 的实际能表达的精度对尾数进行了截断。
BigDecimal recommend1 = new BigDecimal(“0.1”);
BigDecimal recommend2 = BigDecimal.valueOf(0.1);

27.【强制】 定义数据对象 DO 类时,属性类型要与数据库字段类型相匹配。

…..正例: 数据库字段的 bigint 必须与类属性的 Long 类型相对应。

…..反例: 某个案例的数据库表 id 字段定义类型 bigint unsigned(unsigned 就是将数字类型无符号化, 例如 int 型的范围:-2^31 ~ 2^31 - 1,而unsigned int的范围:0 ~ 2^32。),实际类对象属性为 Integer,随着 id 越来
越大,超过 Integer 的表示范围而溢出成为负数。

28.【强制】关于基本数据类型与包装数据类型的使用标准如下:

…..1) 【强制】 所有的 POJO 类属性必须使用包装数据类型。

…..2) 【强制】 RPC 方法的返回值和参数必须使用包装数据类型。

…..说明:数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。

29.【强制】 POJO 类必须写 toString 方法。

…..说明: 在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。

30.【推荐】 慎用 Object 的 clone 方法来拷贝对象。

…..说明: 对象 clone 方法默认是浅拷贝,若想实现深拷贝需覆写 clone 方法实现域对象的深度遍历式拷贝。

31.【推荐】循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展。 说明:下例中,反编译出的字节码文件显示每次循环都会new出一个StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象,造成内存资源浪费。

反例:

String str = "start";
for (int i = 0; i < 100; i++) {
 str = str + "hello";
}

(五) 集合处理

32.【强制】 关于 hashCode 和 equals 的处理,只要覆写 equals,就必须覆写 hashCode

…..说明:HashCode是编译器为不同对象产生的不同整数,根据equal方法的定义:如果两个对象是相等(equal)的,那么两个对象调用 hashCode必须产生相同的整数结果,即:equal为true,hashCode必须为true,equal为false,hashCode也必须 为false,所以必须重写hashCode来保证与equal同步。

33.【强制】ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异
常, 即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。
说明: subList 返回的是 ArrayList 的内部类 SubList, 并不是 ArrayList 而是 ArrayList 的一个视图,对
于 SubList 子列表的所有操作最终会反映到原列表上

34.【强制】 Collections 类返回的对象,如: emptyList()/singletonList()等都是 immutable
list,不可对其进行添加或者删除元素的操作。

…..说明:如果查询无结果,返回 Collections.emptyList()空集合对象,调用方一旦进行了添加元素的操作,就
会触发 UnsupportedOperationException 异常

35.【强制】 使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一
致、长度为 0 的空数组

……反例: 直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出
现 ClassCastException 错误。

正例:
List<String> list = new ArrayList<>(2);
list.add("guan");
list.add("bao");
String[] array = list.toArray(new String[0]);

…..说明: 使用 toArray 带参方法,数组空间大小的 length:

1) 等于 0,动态创建与 size 相同的数组,性能最好。

2) 大于 0 但小于 size,重新创建大小等于 size 的数组,增加 GC 负担。

3) 等于 size,在高并发情况下,数组创建完成之后, size 正在变大的情况下,负面影响与上相同。

4) 大于 size,空间浪费,且在 size 处插入 null 值,存在 NPE 隐患

36.【强制】 泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方
法, 而<? super T>不能使用 get 方法,作为接口调用赋值时易出错。

……说明: 扩展说一下 PECS(Producer Extends Consumer Super)原则: 第一、 频繁往外读取内容的,适合
用<? extends T>。 第二、 经常往里插入的,适合用<? super T>

37.【强制】 不要在 foreach 循环里进行元素的 remove/add 操作。 remove 元素请使用
Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。

正例:
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素的条件) {Java 开发手册
13/44
iterator.remove();
}
}
反例:
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}

…..说明: 以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1” 换成“2” ,会是同样的结果吗?
…..https://www.cnblogs.com/luyu1993/p/7148765.html

38.【推荐】 集合初始化时, 指定集合初始值大小。

…..说明: HashMap 使用 HashMap(int initialCapacity) 初始化。

…..正例: initialCapacity = (需要存储的元素个数 / 负载因子) + 1。 注意负载因子(即 loader factor) 默认
为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值) 。

…..反例: HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容量 7 次被
迫扩大, resize 需要重建 hash 表,严重影响性能。

39.【推荐】 使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。

…..说明: keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应
的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。如果是 JDK8,
使用 Map.forEach 方法。

(六) 并发处理

40.【强制】 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这
样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

…..说明: Executors 返回的线程池对象的弊端如下:

…..1) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

…..2) CachedThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE, 可能会创建大量的线程,从而导致OOM.

41.【强制】 在使用阻塞等待获取锁的方式中,必须在 try 代码块之外,并且在加锁方法与 try 代
码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在 finally 中无法解锁。

…..说明一: 如果在 lock 方法与 try 代码块之间的方法调用抛出异常,那么无法解锁,造成其它线程无法成功
获取锁。

…..说明二: 如果 lock 方法在 try 代码块之内,可能由于其它方法抛出异常,导致在 finally 代码块中,
unlock 对未加锁的对象解锁,它会调用 AQS 的 tryRelease 方法(取决于具体实现类),抛出
IllegalMonitorStateException 异常。

…..说明三: 在 Lock 对象的 lock 方法实现中可能抛出 unchecked 异常,产生的后果与说明二相同。

正例:
    Lock lock = new XxxLock();
    // ...
    lock.lock();
    try {
    doSomething();
    doOthers();
    } finally {
    lock.unlock();
    }
反例:
    Lock lock = new XxxLock();
    // ...
    try {
    // 如果此处抛出异常,则直接执行 finally 代码块
    doSomething();
    // 无论加锁是否成功, finally 代码块都会执行
    lock.lock();
    doOthers();
    } finally {
    lock.unlock();
    }

42.【强制】 在高并发场景中,避免使用” 等于” 判断作为中断或退出的条件。

…..说明: 如果并发控制没有处理好,容易产生等值判断被“击穿” 的情况,使用大于或小于的区间判断条件
来代替。

…..反例: 判断剩余奖品数量等于 0 时,终止发放奖品,但因为并发处理错误导致奖品数量瞬间变成了负数,
这样的话,活动无法终止。

43.表达异常的分支时, 少用 if-else 方式, 这种方式可以改写成:

if (condition) {
...
return obj;
}

// 接着写 else 的业务逻辑代码;

…..说明: 如果非使用 if()…else if()…else…方式表达逻辑, 避免后续代码维护困难, 【强制】 请勿超过 3 层。

…..正例: 超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句
即代码逻辑先考虑失败、异常、中断、退出等直接返回的情况,以方法多个出口的方式,解决代码中判断
分支嵌套的问题,这是逆向思维的体现。

示例如下:
    public void findBoyfriend(Man man) {
    if (man.isUgly()) {
    System.out.println("本姑娘是外貌协会的资深会员");
    return;
    }
    if (man.isPoor()) {
    System.out.println("贫贱夫妻百事哀");
    return;
    }
    if (man.isBadTemper()) {
    System.out.println("银河有多远,你就给我滚多远");
    return;
    }
    System.out.println("可以先交往一段时间看看");
    }

(八) 注释规约

43.【强制】 类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用
// xxx 方式。

…..说明: 在 IDE 编辑窗口中, Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注释;在 IDE
中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高阅读效率。

44.【强制】 所有的抽象方法( 包括接口中的方法) 必须要用 Javadoc 注释、除了返回值、参数、
异常说明外,还必须指出该方法做什么事情,实现什么功能。

…..说明: 对子类的实现要求,或者调用注意事项,请一并说明。

45.【强制】 所有的类都必须添加创建者和创建日期。

46.【强制】 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释
使用/ /注释,注意与代码对齐。

47.【强制】 所有的枚举类型字段必须要有注释,说明每个数据项的用途。

二、异常日志

48.【强制】 catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。
对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。

…..说明: 对大段代码进行 try-catch,使程序无法根据不同的异常做出正确的应激反应,也不利于定位问
题,这是一种不负责任的表现。

…..正例: 用户注册的场景中,如果用户输入非法字符, 或用户名称已存在, 或用户输入密码过于简单,在程
序上作出分门别类的判断,并提示给用户。

49.【强制】 有 try 块放到了事务代码中, catch 异常后,如果需要回滚事务,一定要注意手动回
滚事务。

50.【强制】 finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。

…..说明: 如果 JDK7 及以上,可以使用 try-with-resources 方式。

51.【强制】 不要在 finally 块中使用 return。

…..说明: try 块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally 块中的语句,如果此处存
在 return 语句,则在此直接返回,无情丢弃掉 try 块中的返回点。

反例:
    private int x = 0;
    public int checkReturn() {
    try {
    // x 等于 1,此处不返回
    return ++x;
    } finally {Java 开发手册
    25/44
    // 返回的结果是 2
    return ++x;
    }
    }

五、 MySQL 数据库

(一) 建表规约

52.【强制】 表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned
tinyint( 1 表示是, 0 表示否)。

…..说明: 任何字段如果为非负数,必须是 unsigned。

…..注意: POJO 类中的任何布尔类型的变量,都不要加 is 前缀,所以,需要在设置从 is_xxx
到 Xxx 的映射关系。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的命名方式是为了明确其取
值含义与取值范围。

…..正例: 表达逻辑删除的字段名 is_deleted, 1 表示删除, 0 表示未删除。

53.【强制】 表名、字段名必须使用小写字母或数字, 禁止出现数字开头,禁止两个下划线中间
只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重
考虑。

…..说明: MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。因此,数据库名、表
名、字段名,都不允许出现任何大写字母,避免节外生枝。

…..正例: aliyun_admin, rdc_config, level3_name

…..反例: AliyunAdmin, rdcConfig, level_3_name

54.【强制】 禁用保留字,如 desc、 range、 match、 delayed 等, 请参考 MySQL 官方保留字。

55.【强制】 主键索引名为 pk_字段名;唯一索引名为 uk_字段名; 普通索引名则为 idx_字段名。

…..说明: pk_ 即 primary key; uk_ 即 unique key; idx_ 即 index 的简称。

56.【强制】 小数类型为 decimal,禁止使用 float 和 double。

…..说明: 在存储的时候, float 和 double 都存在精度损失的问题,很可能在比较值的时候,得到不正确的
结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数并分开存储。

57.【强制】 如果存储的字符串长度几乎相等,使用 char 定长字符串类型。

58.【强制】 varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长
度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索
引效率。
59.【强制】 表必备三字段: id, create_time, update_time。

…..说明: 其中 id 必为主键,类型为 bigint unsigned、单表时自增、步长为 1。 create_time, update_time
的类型均为 datetime 类型。

(二) 索引规约

60.【强制】 业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。

…..说明: 不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的; 另外,
即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。

61.【强制】 超过三个表禁止 join。需要 join 的字段,数据类型必须绝对一致; 多表关联查询
时,保证被关联的字段需要有索引。

…..说明: 即使双表 join 也要注意表索引、 SQL 性能。

62.【强制】 在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据
实际文本区分度决定索引长度即可。

…..说明: 索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会高达
90%以上,可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度来确定。

63.【强制】 页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。

…..说明: 索引文件具有 B-Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。

(三) SQL 语句

64.【强制】 当某一列的值全是 NULL 时, count(col)的返回结果为 0,但 sum(col)的返回结果
为 NULL,因此使用 sum()时需注意 NPE 问题。
正例: 使用如下方式来避免 sum 的 NPE 问题: SELECT IFNULL(SUM(column), 0) FROM table;

65.【强制】 不要使用 count(列名)或 count(常量)来替代 count(), count()是 SQL92 定义的
标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。

…..说明: count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。

66.【强制】 代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句。

67.【强制】 数据订正(特别是删除、 修改记录操作) 时,要先 select,避免出现误删除,确认无
误才能执行更新语句。

六、工程结构