一、编程规范
1.好代码的原则
参考Kent Beck 的简单设计四原则
- 通过所有测试(Passes its tests):强调的是外部需求,这是代码实现最重要的
- 尽可能消除重复 (Minimizes duplication):代码的模块架构设计,保证代码的正交性,保证代码更容易修改
- 尽可能清晰表达 (Maximizes clarity):代码的可阅读性,保证代码是容易阅读的
- 更少代码元素 (Has fewer elements):保证代码是简洁的,在简洁和表达力之间,我们更看重表达力
以上四个原则的重要程度依次降低, 这组定义被称做简单设计原则。
注意一些常见的代码坏味道:
-
大量重复代码(抽共用方法,设计模式)
-
方法参数过多(可封装成一个DTO对象)
-
方法过长(抽小函数)
-
判断条件太多(优化if…else)
-
不处理没用的代码
-
不注重代码格式
-
避免过度设计
2.代码注释规范
注释是我们披荆斩棘历经磨难翻越需求这座大山时,留下的踪迹和收获的经验教训,这些宝贵的知识除了证明我们曾经存在过,也提醒着后来的人们殷鉴不远、继往开来。
注释除了说明作用、逻辑之外。还有一个很重要的原因:当业务逻辑过于复杂,代码过于庞大的时候,注释就变成了一道道美化环境、分离与整理逻辑思路的路标。这是很重要的一点,它能有效得帮助我们免于陷入代码与业务逻辑的泥沼之中。
/**
* 开始抽奖方法
* 保存中奖信息、奖励用户积分等
* @param luckDrawDTO
* @return ResponseDTO 返回中奖信息
*/
public ResponseDTO<String> startLuckDraw(LuckDrawDTO luckDrawDTO) {
// -------------- 1、校验抽奖活动基本信息 ------------------------
xxx伪代码一顿操作
// -------------- 2、新增抽奖记录 -------------------------------
xxx伪代码一顿操作
// -------------- 3、如果需要消耗积分,则扣除钢镚积分 -------------
xxx伪代码一顿操作
// -------------- 4、获取奖品信息,开始翻滚吧 --------------------
xxx伪代码一顿操作
return ResponseDTO.succ(luckDrawPrizeVO);
}
注意:注释和代码的一致性
3.关于文件存储
使用文件存储服务时 规定文件路径按 /业务桶名称/子业务1/日期20220101/重命令后的文件名称.文件类型
上传文件 防止文件过大 判断文件类型 防止恶意后缀
大文件考虑进行分片、断点上传
4.接口实现过程中,注意大文件、大事务、大对象
-
读取大文件时,不要Files.readAllBytes直接读取到内存,这样会OOM的,建议使用BufferedReader一行一行来。
-
大事务可能导致死锁、回滚时间长、主从延迟等问题,开发中尽量避免大事务。
-
注意一些大对象的使用,因为大对象是直接进入老年代的,可能会触发fullGC
5.日志打印好,接口的关键代码,要有日志保驾护航
应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架(SLF4J、JCL–Jakarta Commons Logging)中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。推荐使用 SLF4J。
public void transfer(TransferDTO transferDTO){
log.info("invoke tranfer begin");
//打印入参
log.info("invoke tranfer,paramters:{}",transferDTO);
try {
res= transferService.transfer(transferDTO);
}catch(Exception e){
log.error("transfer fail,account:{}",
transferDTO.getAccount())
log.error("transfer fail,exception:{}",e);
}
log.info("invoke tranfer end");
}
6.根据不同业务场景,合理使用异步或并行调用
业务成功后的通知类-异步调用
查询或执行多个业务数据-并行调用
7.接口的功能定义要具备单一性
接口的功能单一、明确。如暂存和提交接口,不应放在一起。
8.考虑接口的幂等性
用户多次请求所产生的影响与一次请求执行的影响效果相同
9.注意线程安全和控制好代码锁的粒度
在高并发情况下,注意线程安全问题
Hashmap、Arraylist、LinkedList、TreeMap等都是线性不安全的;
Vector、Hashtable、ConcurrentHashMap等都是线性安全的
能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
10.接口限流
大流量请求的情况下,限制新的请求对系统的访问,保证系统的稳定性。
可以使用Guava的RateLimiter
单机版限流,也可以使用Redis
分布式限流,还可以使用阿里开源组件sentinel
限流
11.方法名约定
从某种意义上来说,任何的业务操作,落到数据的层面,都是对数据的CRUD(增删改查),因此在写业务代码的时候,会经常碰到关于CRUD的命名,就拿查询来说,fetch, retrieve, get, find, query等等都能表示查询的意思,为了命名的一致性和统一性,为了保证每个概念对应一个词,我们有必要对CRUD的命名做一个约定。
比如,就对CRUD的命名做了如下约定:
CRUD操作 | 方法名约定 |
---|---|
新增 | create |
添加 | add |
删除 | remove(App和Domain层)delete(Infrastructure层) |
修改 | update |
查询(返回单个结果) | get |
查询(返回多个结果) | list |
分页查询 | page |
统计 | count |
业务命名最好不用直接用CRUD,除非其行为有非常强的CRUD语义,比如用addContact表示添加联系人,removeContact表示删除联系人是可以接受的。但是如果你用createOrder和deleteOrder来表示下单和取消订单是不合适的,在业务层,更贴切的命名应该是placeOrder和cancelOrder。
12.线程池隔离
创建线程或线程池时请指定有意义的线程名称,方便出错时回溯
线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
进行线程池隔离,重要业务分配多一点的核心线程,保护重要业务。需要对线程池进行管理和监控。
13.关于唯一ID生成
使用全局发号器按特定规则生成唯一ID
使用雪花ID等分布式ID
数据表主键需遵循有序性
14.保证接口安全性
用户认证(token或者session)
注意资源权限控制
15.恰当使用缓存
将经常访问切不经常变更的热点数据放入缓存中,可以承载更多的请求,提升查询效率,减少数据库的压力。
16.异常日志及错误码
错误码的制定原则:快速溯源、沟通标准化。
异常主要分为系统异常和业务异常,系统异常是指不可预期的系统错误,如网络连接,服务调用超时等,是可以retry(重试)的;
而业务异常是指有明确业务语义的错误,再细分的话,又可以分为参数异常和业务逻辑异常,参数异常是指用户过来的请求不准确,逻辑异常是指不满足系统约束,比如客户已存在。业务异常是不需要retry的。
错误码主要有3部分组成:类型+场景+自定义标识
17.参数校验
用户请求传入的任何参数必须做有效性验证。如参数类型,日期范围,金额必须不能等于负数等等
说明:忽略参数校验可能导致:
⚫ page size 过大导致内存溢出
⚫ 恶意 order by 导致数据库慢查询
⚫ 缓存击穿
⚫ SSRF
⚫ 任意重定向
⚫ SQL 注入,Shell 注入,反序列化注入
⚫ 正则输入源串拒绝服务 ReDoS
18.使用平台资源必须实现正确的防重放的机制
譬如创建流程、短信、邮件等
防重放的机制:如数量限制、疲劳度控制、验证码校验,避免被滥刷而导致资损。
19.防止sql注入
1. 使用预编译机制
尽量用预编译机制,少用字符串拼接的方式传参,它是sql注入问题的根源。
2. 要对特殊字符转义
有些特殊字符,比如:%作为like语句中的参数时,要对其进行转义处理。
3. 要捕获异常
需要对所有的异常情况进行捕获,切记接口直接返回异常信息,因为有些异常信息中包含了sql信息,包括:库名,表名,字段名等。攻击者拿着这些信息,就能通过sql注入随心所欲的攻击你的数据库了。目前比较主流的做法是,有个专门的网关服务,它统一暴露对外接口。用户请求接口时先经过它,再由它将请求转发给业务服务。这样做的好处是:能统一封装返回数据的返回体,并且如果出现异常,能返回统一的异常信息,隐藏敏感信息。此外还能做限流和权限控制。
4. 使用代码检测工具
使用sqlMap等代码检测工具,它能检测sql注入漏洞。
5. 要有监控
需要对数据库sql的执行情况进行监控,有异常情况,及时邮件或短信提醒。
6. 数据库账号需控制权限
对生产环境的数据库建立单独的账号,只分配DML相关权限,且不能访问系统表。切勿在程序中直接使用管理员账号。
7.代码review
建立代码review机制,能找出部分隐藏的问题,提升代码质量。
8. 使用其他手段处理
对于不能使用预编译传参时,要么开启druid的filter防火墙,要么自己写代码逻辑过滤掉所有可能的注入关键字。建议使用druid的filter防火墙,性能良好
20 合理使用订阅和发布模式对系统业务进行解耦
1 spring 中的 ApplicationEvent类
import org.springframework.context.ApplicationEvent;
@EventListener
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
2 EventBus
2 使用消息中间件
21 常用工具类库
不要重复造轮子
apache commons工具类库
commons-lang3 字符串操作
commons-collections 集合工具类
common-beanutils 操作对象
common-beanutils 操作对象
等....
Google Guava 工具类库
想学Google Guava看这篇就够了
https://zhuanlan.zhihu.com/p/165149899
Google 的核心 Java 库 guava 常用工具类
https://blog.csdn.net/wangmx1993328/article/details/103533060
集合操作(list 双向map)
String工具类
Guava本地缓存Cache
Guava限流RateLimiter
Guava发布/订阅EventBus
国产Hutool工具包
Hutool — 🍬A set of tools that keep Java sweet.
注意:
Hutool包使用那个功能引入那个包
建议优先使用apache commons和Google Guava
使用Hutool工具类时请先阅读源码(单元测试未覆盖,似乎可能有小坑)
使用Hutool工具类时,请使用门面模式(Hutool)
22 业务并发导致死锁
业务并发导致死锁是一个常见的问题,特别是在高并发的情况下。以下是一些通用的解决方案:
- 优化数据库设计:通过合理的数据库设计,可以减少死锁的发生。例如,使用合适的索引、避免长事务等。
- 减少锁的范围:在代码中,尽量减少锁的范围,只在必要的时候加锁。例如,只在修改数据时加锁,而不是在查询数据时也加锁。
- 使用乐观锁:乐观锁是一种不加锁的并发控制方法,通过版本号或时间戳等机制来保证数据的一致性。在高并发的情况下,乐观锁可以减少锁的竞争,提高系统的并发性能。
- 使用分布式锁:分布式锁是一种在分布式系统中实现锁的机制,可以避免单点故障和锁的竞争。例如,使用Redis等分布式缓存来实现分布式锁。
- 使用异步处理:在特殊业务逻辑处理时,可以考虑使用异步处理的方式,将业务逻辑放到消息队列中,异步处理后再返回结果。这样可以避免长时间的锁竞争,提高系统的并发性能。
总之,针对不同的业务场景,可以采用不同的解决方案来避免死锁的发生。
23 定时任务
使用 @Scheduled() 注解的定时器时,需要配置线程池,任务如果执行卡住,那么当达到最大线程数时,定时器将不执行所有任务
24 while循环注意
while循环时注意死循环,最好给一个最多执行的次数上限
25 优雅的关闭资源和执行的任务
请开启springboot 优雅关闭功能
正确关闭线程池
26 大量数据返回时应减少传输数据的大小
减少字段名的长度,用字母去代替 a b c …
可以使用 GZip工具对数据进行压缩转换为二进制传输,使用时在转为JSON字符串
开启nginx的GZip功能
合理使用缓存(redis 、本地缓存)
27 尽量减少项目启动的时间
通过减少不必要的依赖
减少不必要的bean扫描
使用javaConfig注册bean
使用改造starter定义自动加载
28 合理使用sping提供的事务
@Transactional事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。
声明式事务是一种在应用代码中通过注解或其他方式声明事务边界的方式,它独立于任何具体的事务管理API,比如JDBC,Hibernate,JPA等。Spring框架的@Transactional注解就是一个典型的声明式事务管理的方式。
通常,在工作中我们建议使用Spring的声明式事务,原因如下:
简单易用:相较于编程式事务管理,声明式事务管理更简单,只需要在需要事务管理的方法上添加@Transactional注解即可,无需在代码中手动控制事务的开启,提交和回滚。
分离关注点:声明式事务管理使得应用代码和事务管理逻辑分离,有利于代码的清晰和可读性。
全面支持:Spring框架的声明式事务管理支持多种事务管理策略,比如传播行为,超时,回滚策略等,可以满足大多数应用的需求。
灵活的事务属性配置:可以在方法级别和类级别配置不同的事务属性,比如只读,超时,传播行为等。
便于单元测试:由于事务管理逻辑被抽取到Spring的事务管理器中,使得事务的管理和控制的逻辑更易于测试。
然而,有些情况下你可能不推荐使用声明式事务:
复杂的事务需求:如果你的应用有复杂的事务需求,比如嵌套事务,分布式事务等,使用编程式事务可能更直观和灵活。
性能问题:虽然Spring的声明式事务对性能的影响较小,但在一些对性能要求非常高的场景下,可能需要考虑手动管理事务以获取最佳的性能。
特定数据库和JPA提供商:一些特定的数据库或JPA提供商可能会有自己的事务管理策略或限制,在这种情况下,可能需要手动管理事务。
总的来说,对于大多数应用来说,Spring的声明式事务是一个很好的选择。但在特定的情况下,你可能需要根据应用的实际情况来选择是否使用声明式事务。
30 @Transactional 和 @DSTransactional 混用可能会造成事务死锁
两者都可以控制事务处理。但不能混用!尤其是在多数据源的情况下,可能会造成事务死锁。
这个问题是怎么发现的呢?在某一次死锁的排查中,在查询数据库的锁的时候,发现对某一个表的锁是一个不同的 sessionId, 和这个事务更新其它表的 sessionId 不一样!代码刚好是在一个事务中同时用到了上面个注解!
public AService {
@Resource
private BService bservice;
@DSTransactional
public void complete(...) {
// 其它操作
bservice.updateBusiness();
}
}
public BService {
@Transactional(rollback = Throwable.class)
public void updateBusiness(...) {
// 其它操作
}
}
31 使用多数据源 @DSTransactional
/**
* 多数据源实现事务一致性使用@DSTransactional注解而不是@Transactional注解,类上面也不要添加@Transactional,不然会导致
*数据源无法切换
*/
@DSTransactional
@DS("master") //注意:@DSTransactional需要在方法上显示指定@DS不然也会报错找不到xxx
public void exe() {
}
32 导出功能
1 注意导出的数量大小,可以限制导出的数量 查询条件
2 如果是从数据库中一次性读取大量的数据,读取时会占用一个数据库连接池的连接,
可以使用多线程分页查询,mybatis-plus流式查询
3 使用成熟的框架 如 easyExcel
流式查询避免数据量过大导致OOM
流式查询避免数据量过大导致OOM
33 善用Javadoc标签
1 @see 添加超链接
2 @link
3
/**
* <ul>
* <li> sss
* <li> sss
* <li> sss
* </ul>
*/
4 @description 说明
34 特殊注释标记
特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描, 经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
1) 待办事宜(TODO):( 标记人,标记时间,[预计处理时间]) 表示需要实现,但目前还未实现的功能。 这实际上是一个 Javadoc 的标签,目前的 Javadoc 还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个 Javadoc 标签)。
2) 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间]) 在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况
35 mybatis-plus执行in时防止为空
防止出现 ORA-00936: 缺失表达式 错误
if(为空){
return ;
}
wrapper.in()
二、前后端规约
1.前后端交互的 API,需要明确协议、域名、路径、请求方法、请求内容、状态码、响应体
说明:
1) 协议:生产环境必须使用 HTTPS。
2) 路径:每一个 API 需对应一个路径,表示 API 具体的请求地址:
a) 代表一种资源,只能为名词,推荐使用复数,不能为动词,请求方法已经表达动作意义。
b) URL 路径不能使用大写,单词如果需要分隔,统一使用下划线。
c) 路径禁止携带表示请求内容类型的后缀,比如".json",“.xml”,通过 accept 头表达即可。
3) 请求方法:对具体操作的定义,常见的请求方法如下:
a) GET:从服务器取出资源。
b) POST:在服务器新建一个资源。
c) PUT:在服务器更新资源。
d) DELETE:从服务器删除资源。
4) 请求内容:URL 带的参数必须无敏感信息或符合安全要求;body 里带参数时必须设置 Content-Type。
5) 响应体:响应体 body 可放置多种数据类型,由 Content-Type 头来确定。
6)返回的数据格式,应该尽量使用JSON,如下
规则 | 说明 |
---|---|
数据格式 | application/json |
字符编码 | utf-8 |
数据结构 | {“status”: 状态码,“msg”: 错误信息, “data”: 数据} |
状态码status为200代表正常,其它代表不正常。不正常的信息msg会提示
2.前后端数据列表相关的接口返回,如果为空,则返回空数组[]或空集合{}。
说明:此条约定有利于数据层面上的协作更加高效,减少前端很多琐碎的 null 判断。
3.服务端发生错误时,返回给前端的响应信息必须包含 HTTP 状态码,errorCode、 errorMessage、用户提示信息四个部分。
说明:四个部分的涉众对象分别是浏览器、前端开发、错误排查人员、用户。
其中输出给用户的提示信息
要求:简短清晰、提示友好,引导用户进行下一步操作或解释错误原因,提示信息可以包括错误原因、上
下文环境、推荐操作等。 errorCode:错误码。errorMessage:简要描述后端出错原因,便于错误排 查人员快速定位问题,注意不要包含敏感数据信息。
正例:常见的 HTTP 状态码如下
1) 200 OK: 表明该请求被成功地完成,所请求的资源发送到客户端。
2) 401 Unauthorized: 请求要求身份验证,常见对于需要登录而用户未登录的情况。
3) 403 Forbidden:服务器拒绝请求,常见于机密信息或复制其它登录用户链接访问服务器的情况。
4) 404 Not Found: 服务器无法取得所请求的网页,请求资源不存在。
5) 500 Internal Server Error: 服务器内部错误。
4.在前后端交互的 JSON 格式数据中,所有的 key 必须为小写字母开始的
lowerCamelCase 风格,符合英文表达习惯,且表意完整。
正例:errorCode / errorMessage / assetStatus / menuList / orderList / configFlag
反例:ERRORCODE / ERROR_CODE / error_message / error-message / errormessage /
ErrorMessage / msg
5.对于需要使用超大整数的场景,服务端一律使用 String 字符串类型返回,禁止使用 Long 类型
6.HTTP 请求通过 URL 传递参数时,不能超过 2048 字节。
7.HTTP 请求通过 body 传递内容时,必须控制长度,超出最大长度后,后端解析会出错
说明:nginx 默认限制是 1MB,tomcat 默认限制为 2MB,当确实有业务需要传较大内容时,可以通过调
大服务器端的限制。
9.服务端返回的数据,使用 JSON 格式而非 XML
10.前后端的时间格式统一为"yyyy-MM-dd HH:mm:ss",统一为 GMT
11.在接口路径中不要加入版本号,版本控制在 HTTP 头信息中体现,有利于向前兼容
12.接口注意返回的数据量,如果数据量大需要分页
一个接口返回报文,不应该包含过多的数据量。过多的数据量不仅处理复杂,并且数据量传输的压力也非常大。因此数量实在是比较大,可以分页返回,如果是功能不相关的报文,那应该考虑接口拆分。
13 注意CSRF安全验证
什么是CSRF?
专业解释:跨站请求伪造(Cross-site request forgery),也被称为 one-click attack 或者
session riding,通常缩写为 CSRF 或者 XSRF,是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。
通俗易懂的例子: 就是我在一个安全的官方网站点了别人的一个恶意链接,由于我在安全网站的登录还没过期,或者是还没登出,我点这个恶意链接的时候是会携带浏览器中的cookie一起发给恶意链接的后端服务器的,恶意链接的后端就根据我发过去的cookie,拿到存在cookie中的sessin_Id,然后他这个恶意的后端伪造一个url请求,url里面带了一个刚刚拿到的session_id和一些业务参数,去正规网站里面用我的身份去进行一些操作,比如转账等,这就是一次跨域攻击。
使用JWT进行身份认证,可以防止CSRF
14 注意防止xss攻击
15 jwt应该存储在那里
Cookie
服务端可以将JWT令牌通过Cookie发给浏览器,浏览器在请求服务端接口时会自动在Cookie头中带上JWT令牌,服务端对Cookie头中的JWT令牌进行检验即可实现身份验证。但它容易受到CSRF攻击的影响。
解决的方法是通过设置Cookie的SameSite属性为Strict。跨站时不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。
Cookie除了易受CSRF攻击还有XSS攻击。黑客可以通过JS脚本读取Cookie中的信息。为了防止这一点,可以设置Cookie的属性为HttpOnly。
localStorage(推荐)
localStorage也可以存储JWT令牌,这种方法不易受到 CSRF 的影响。但是和Cookie不同的是它不会自动在请求中携带令牌,需要通过代码来实现。不过这样会受到XSS攻击。另外如果用户不主动清除JWT令牌,它将永远存储到localStorage。
sessionStorage
sessionStorage大部分特性类似localStorage,不过它的生命周期不同于localStorage,它是会话级存储。关闭页面或浏览器后会被清除。
三、RESTful API设计规范
1.API 与用户的通信协议,总是使用 HTTPS 协议
2.版本号
命名版本号可以解决版本不兼容问题,在设计 RESTful API 的一种实用的做法是使用版本号。一般情况下,我们会在 url 中保留旧版本号,并同时兼容多个版本。例如:https://api.example.com/v1/
3.资源路径(接口命名)
路径又称"终点"(endpoint),表示API的具体网址。
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。
理清资源的层次结构,比如业务针对的范围是学校,那么学校会是一级资源(/school),老师(/school/teachers),学生(/school/students)就是二级资源。
GET /zoos:列出所有动物园
POST /zoos:新建一个动物园
GET /zoos/ID:获取某个指定动物园的信息
PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)
PATCH /zoos/ID:更新某个指定动物园的信息(提供该动物园的部分信息)
DELETE /zoos/ID:删除某个动物园
GET /zoos/ID/animals:列出某个指定动物园的所有动物
DELETE /zoos/ID/animals/ID:删除某个指定动物园的指定动物
4.请求方法
对具体操作的定义,常见的请求方法如下:
a) GET:从服务器取出资源。
b) POST:在服务器新建一个资源。
c) PUT:在服务器更新资源。
d) DELETE:从服务器删除资源。
5.状态码
常见:
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
502 网关错误
503 Service Unavailable
504 网关超时
四 、数据库
1.表达是与否概念的字段,必须使用 is_xxx 的方式命名
说明:任何字段如果为非负数,必须是 unsigned tinyint (1 表示是,0 表示否)。
注意:POJO 类中的任何布尔类型的变量,都不要加 is 前缀,所以,需要在设置从 is_xxx 到
Xxx 的映射关系。数据库表示是与否的值,使用 tinyint 类型,坚持 is_xxx 的命名方式是为了明确其取值含
义与取值范围。
3.表字段禁用系统关键字
如 desc、range、match、delayed 等
4.索引名
主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名
5.表必备三字段
主键 id,创建时间 create_time, 更新时间 update_time
6.不得使用外键与级联
不得使用外键与级联,一切外键概念必须在应用层解决
7.in操作注意
in操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在1000 个之内
8.表查询select中
在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明
9.用 resultMap 当返回参数
不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要 定义;反过来,每一个表也必然有一个与之对应。
说明:配置映射关系,使字段与 DO 类解耦,方便维护。
10.sql.xml 配置参数使用
sql.xml 配置参数使用:#{},#param# 不要使用${} 此种方式容易出现 SQL 注入。
11.更新数据表记录时
更新数据表记录时,必须同时更新记录对应的 update_time 字段值为当前时间。
12.数据表命名
表名、字段名必须使用小写字母或数字禁止出现数字开头,禁止两个下划线中间只出现数字
相同业务的数据表前缀一致
备份数据库名使用正式库名加上备份时间组成
13.代码中使用到数据库连接时
将连接信息url username password 进行加密处理