目录
友情提醒:
先看文章目录,大致了解文章知识点结构,点击文章目录可直接跳转到文章指定位置。注意:大部分内容是摘抄整理的
第一章、Java中的规范
1.1)类命名采用驼峰命名法
1.1.1)实体类命名规范
阿里巴巴Java开发手册中的DO、DTO、BO、AO、VO、POJO定义
分层领域模型规约:
DO( Data Object):与数据库表结构一一对应,通过DAO层向上传输数据源对象。
DTO( Data Transfer Object):数据传输对象,Service或Manager向外传输的对象。
BO( Business Object):业务对象。 由Service层输出的封装业务逻辑的对象。
AO( Application Object):应用对象。 在Web层与Service层之间抽象的复用对象模型,极为贴近展示层,复用度不高。
VO( View Object):显示层对象,通常是Web向模板渲染引擎层传输的对象。
POJO( Plain Ordinary Java Object):在本手册中, POJO专指只有setter/getter/toString的简单类,包括DO/DTO/BO/VO等。
Query:数据查询对象,各层接收上层的查询请求。 注意超过2个参数的查询封装,禁止使用Map类来传输。
1.1.2)其他层中类的命名规范
属性和类(实体)的命名约定是采用驼峰命名法
1、Controller必须根据业务进行命名,驼峰,Controller结尾
2、Service必须根据业务进行命名,驼峰,Service结尾
3、implement必须根据业务进行命名,驼峰,业务+service+impl结尾
4、辅助类的命名,根据功能进行命名,如果是工具类,utils或者util进行结尾
例如:ChgInfoController
例如:ChgInfoService
例如:ChgInfoServiceImpl
例如:ChgUtil
1.2)函数(方法)命名使用驼峰命名
辅助函数命名
例如构建对象,请用build开头,表示只用于构建对象,并且返回,请使用 private修饰
public class Car {
private String make;
private String model;
private String registration;
// 构造函数
public Car(String make, String model, String registration) {
this.make = make;
this.model = model;
this.registration = registration;
}
// 辅助函数用于创建Car对象
private static Car buildCar(String make, String model, String registration) {
// 在辅助函数中执行对象的创建逻辑
return new Car(make, model, registration);
}
public static void main(String[] args) {
// 使用辅助函数创建Car对象
Car car = buildCar("Toyota", "Camry", "ABC123");
System.out.println("Car make: " + car.make);
System.out.println("Car model: " + car.model);
System.out.println("Car registration: " + car.registration);
}
}
Controller层/Service层函数命名
Controller层方法的命名规约detail/query/count/create/remove/modify 作前缀
Service层方法的命名规约find/query/count/create/remove/modify 作前缀
例如,"getUserById","createNewUser"
DAO层函数命名
Dao层方法的命名规约find/query/count/insert/delete/update作前缀
复数形式使用queryXXs/updateXXs/insertXXs进行命名
DAO所有传参如果是单个参数,请使用@param注解进行注明
所有XXMapper.xml获取参数,请使用@param(“XX”) 命名, #{XX}获取,${XX}会有SQL注入的风险,具体细节请参考Mysql手册`跟’的区别
1.3)变量/枚举/常量命名
变量:使用驼峰
状态类的变量,请使用int类型
boolean类型的变量,请使用has开头,请勿使用is开头,在某些序列化框架下,可能会导致反序列化失败
常量、枚举使用“_”分割,且每个单词全大写
枚举命名请使用业务+Enum进行命名
接口常量命名请使用业务+Constant进行命名(尽量使用枚举来定义常量)
1.4)函数参数
尽量使用对象进行传递,严禁使用MAP进行传参
如果要进行单个参数传递,最好不要操作5个参数,参数太多,看起来太混乱
1.5)注释
类注释
/**
* 项目名:wang-order-center
* 创建人: XX
* 创建时间:2018/8/13 15:45
* 类名:OrderController
* 类描述: 订单控制器
*/
函数注释
函数参数,一定要加上 @param 这样,别人在调用的时候,ide会进行提示,如果涉及到枚举 加上注解 @see 可以跳转到枚举
/**
* 创建人:XXX
* 创建时间:2018-08-20 20:46:04
* 描述: 购物车创建订单
* @param userId 用户ID
* @param subUserId 用户ID
* @param addrId 地址ID
* @param cartProductIds 购物车ID集合
* @param fahuoTimeLimitedMode 截单模式 0 立即截单, 1 根据活动时间截单
*/
代码注释
代码注释,进行别使用/**/的形式,注释不需要像写作文那样详细,只需要介绍是做什么的就好
// 构建订单对象
1.6)代码
Controller尽量只做转发,不做具体逻辑实现,所有逻辑实现交由service进行
service的接口函数,尽量功能单一,职责越小,越便于维护跟扩展
如果涉及到写等动作,请在函数上加上@transaction注解/或者自己用事务块包裹
service如果不需要捕获异常,可以往上层抛,订单中心已经在全局做了异常处理
尽量做到所有的check在入参就应该进行判断
如果遇到需要构建对象,大量 set函数不应该放置主流程,抽离出来一个辅助函数,直接调用辅助函数即可,辅助函数注释写好,主流程调用辅助函数加上功能注释
代码内部尽量不使用多层for循环调用,在不注意的情况,多层for循环嵌套构建对象,很容易写出内存泄漏的代码
尽量不使用匿名内部类,使用lambda,因为现在jdk已经是1.8版本,支持Java8的特性
如果一段代码,超过两个地方调用,说明需要进行抽象,然后对抽象进行实现
函数跳转层级尽量不要过深,因为是业务代码,跳转太深,很容易把人绕晕了
如果一个函数出现特别多的if else 考虑把if else建成枚举,然后枚举内进行判断返回
日志
所有service层可以直接抛出异常,不需要捕获异常处理,已经配置好全局异常处理,只需要抛出错误信息就好
业务错误打印日志,尽量别打印error打印warning级别日志就好,所有抛出异常上面,尽量打印错误日志的参数
日志打印,尽量使用一下示例打印 // 正确打印 LOGGER.info(“订单ID错误,订单ID:{}”,order.getId()); // 错误打印 LOGGER.info(“订单ID错误,订单ID:”+order.getId());
日志是常量,所以请使用以下定义 private static finale Logger LOGGER = LoggerFactory.get(XX.class);
日志引用,禁止引用具体实现,请引用 slf4j接口
请求路径
尽量使用Restful风格
/liveId/productId URL参数,请使用 @PathVariable(“liveId”)String liveId 指定名称绑定
查询尽量使用Get请求方式,如果参数在URL传递过多,可以不使用Get请求,使用POST请求,请把参数写进请求体内
所有写的操作一律使用POST请求,参数放入请求体内,公共参数,以后会让客户端放入请求头内
幂等
所有的核心写接口,例如,创建订单,支付,取消订单等,一定一定要做幂等,幂等可以在所有的check后也可以在函数调用就进行判断,但是一定是在逻辑进行前
幂等接口,一定要跟调用方统一状态,达成共识
往往幂等会伴随着并发同时进行,因为网络传输,机器性能,请求处理,事务提交带有太多的不确定性,所以要加以控制,所以redis尽量只做并发控制跟短时间内的幂等,请勿太依赖redis来进行做幂等,在redis锁后,插入前请进行DB查询操作,查看是否已经写入过,如果已经写入,拒绝请求
锁
单机锁,如果没有竞争资源,不要使用锁
尽量使用 Lock接口作为锁,别使用Synchronize作为锁,后者作为重量级锁,太影响性能
对锁的使用不清楚,严禁私自使用,尽量多请教会的人,避免写出死锁或者活锁
分布式锁,目前只是用了redis的setnx操作作为分布式锁的实现,使用分布式锁,一定一定要设置过期时间或者在锁代码块执行完毕,手动移除锁,否则出现锁不能释放,你就悲剧了………
目前setnx操作,使用的是jedis连接池来实现的,没有使用spring-data提供的redisTemplate是因为,后者的setnx操作并非一个原子操作,运气不好的情况,极有可能出现setnx操作了,setexprie失败,导致锁不过期,但是jedis使用,切勿忘记,使用完毕,归还连接,jedis是不会再你调用完毕主动归还连接的
所有锁,请在check完毕或者check之前进行锁定资源,请勿在最后锁定,进行无用的写
锁定的资源,必须是唯一的,例如,订单ID,订单编号等
第二章、MySQL数据库中的规范
2.1)数据库规范
1、库名必须有意义。因为在linux系统下mysql数据库区分大小写,Windows系统下不区分,为了避免出现问题,全小写
-- 使用小写字母的数据库名称示例
CREATE DATABASE hrdb;
2、一个项目一个数据库,多个项目慎用同一个数据库。
3、数据库和表要使用统一的字符集(如:UTF8),统一字符集可以避免由于字符集转换产生的乱码,MySQL中的UTF8字符集汉字点3个字节,ASCII码占用1个字节。
字符集:utf8mb4、排序规则:utf8mb4_general_ci
2.2)数据表规范
1、表名全小写,多个单词使用下划线分割,使用单数名词,英文全拼,必须有意义做到见名识义。
使用全小写是因为在linux系统下mysql数据库区分大小写,Windows系统下不区分。
"customer" 而不是 "customers"
"customer" 而不是 "Customer"
"aliyun_admin"而不是"AliyunAdmin"
2、明细表的名称为:主表的名称+字符dtl(detail缩写)
采购定单的名称为:po_order,则采购定单的明细表为:po_orderdtl
3、临时库/表必须以tmp为前缀并以日期为后缀。备份库/表必须以bak为前缀并以日期为后缀。通用表要加前缀all
临时表:temp_orders_20220101, temp_data_20220101
临时库:temp_db_20220101, temp_workspace_20220101
备份表:bak_data_20220101
备份库:bak_workspace_20220101
通用表:all_user。
4、数据库表要用自增id,一定是主键且为自动增长,建议主键是整型,最好是unsigned bigint类型
5、禁止在表中建立预留字段
6、外键、触发器不要有。 数据的完整性靠程序来保证。
触发器和存储过程容易将业务逻辑和DB耦合在一起。
7、创建时间create_time、修改时间update_time、创建人create_by、修改人update_by字段,每个表都必须有!
注意:create_time和update_time一定要用数据的时间戳,自动生成。不要通过代码去操作这两个字段。
2.3)字段名规范
1、列名使用全大写或者全小写,多个单词使用下划线’_'分隔,因为Linux系统上,对于列名和列别名,不区分大小写。
"order_date" 或 "customer_name"。
"ORDER_DATE"或"CUSTOMER_NAME"
反例:userID、houseID。
反例:username、userid、isgood。
2、列名使用有意义的名称:用英文单词的全拼尽量避免使用缩写,除非是广泛认可的缩写,以确保字段名的清晰和易懂。
例如 "customer_id" 代表顾客ID。
"user_id"代表用户ID
反例:cus_id、uid、pid。
3、列名中避免使用保留字:确保字段名不是MySQL的保留字,以避免潜在的冲突和错误。
如name、desc、range、match、delayed等。参考MySQL官方保留字。
4、列名中包含表名,但命名字段时不要重复表的名称,而是使用有意义的缩写
在名employe的表中应该为"emp_name"
反例:"employee_name"
5、列名表达是与否概念必须使用is_xxx的方式命名,数据类型是unsigned tinyint(1表示是,0表示否)。坚持is_xxx的命名方式是为了明确其取值含义与取值范围
正例:表达逻辑删除的字段名is_deleted,1表示删除,0表示未删除。
说明1:如果一个字段存储的是数值,并且是非负数,要设置为unsigned(无符号的)。注意:POJO类中的任何布尔类型的变量,都不要加is前缀,而是设置从is_xxx字段到布尔类型变量xxx的映射关系。
说明2:系统设定取值范围很小且固定的场景,例如表示是与否的值,使用tinyint类型。
2.4)数据库字段类型规范
1、字段必须填写描述信息。
2、如果存储的字符串长度几乎相等,则使用char定长字符串类型。
例如,11位手机号,邮编(postcode)。
这种固定长度的纯数字,也不要用int类型或long类型,因为只有数字参与了运算,才用数值型。
3、小数类型为decimal,禁止使用float和double类型。
在存储时,float和double类型存在精度损失的问题,很可能在比较值的时候,得到不正确的结果。如果存储的数据范围超过decimal的范围,那么建议将数据拆成整数和小数并分开存储。
4、整型int定义中不添加长度
比如使用INT,而不是INT(4)。
5、varchar是可变长字符串,不预先分配存储空间,长度不要超过5000个字符
禁止使用varchar类型作为主键。
6、长度超过5000个字符定义字段类型为text,独立出来一张表,用主键来对应,避免影响其他字段的索引效率。
7、禁止使用blob、text类型保留大文本、文件、图片
建议使用其他方式存储,MySQL只保存指针信息。
8、一列需要占很大空间的字段,一定要单独拎出来,不要和常用信息放一张表。
举个例子: 文章的信息和文章的内容,这一定要分成两个表。否则会给你的文章性能带来极大的挑战。因为很多情况下,查看文章列表,根本不需要查看到文章的内容。
9、所有字段在设计时,必须有默认值,避免使用NULL字段
NULL字段很难查询优化、NULL字段的索引需要额外空间、NULL字段的复合索引无效
字符型的默认值为一个空字符值串‘’,数值型的默认值为数值0,逻辑型的默认值为数值0。
10、系统中所有逻辑型中数值0表示为“假”,数值1表示为“真”,datetime、smalldatetime类型的字段没有默认值,必须为NULL。
除以下数据类型timestamp、image、datetime、smalldatetime、uniqueidentifier、binary、sql_variant、varbinary外
11、多表中的相同列,必须保证列定义一致。
12、不建议使用ENUM类型,使用TINYINT来代替。