JAVA 開發(fā)規(guī)范
v1.0.0 2019/09/06
本篇規(guī)范基于阿里巴巴、華為的開發(fā)手冊,添加了我們團隊的開發(fā)風格規(guī)范,補充了一些細節(jié)。感謝前人的經驗和付出,讓我們可以有機會站在巨人的肩膀上眺望星辰大海。
規(guī)范不是為了約束和禁錮大家的創(chuàng)造力,而是為了幫助大家能夠在正確的道路上,盡可能的避免踩坑和跑偏。
規(guī)范可以讓我們無論單槍匹馬還是與眾人同行的時候都能得心應手。
規(guī)范可以讓我們在面對日益變態(tài)的需求和做代碼接盤俠的時候,更優(yōu)雅從容。
規(guī)則并不是完美的,通過約束和禁止在特定情況下的特性,可能會對代碼實現(xiàn)造成影響。
但是我們制定規(guī)則的目的:為了大多數(shù)程序員小伙伴可以得到更多的好處,如果在團隊實際運作中認為某個規(guī)則無法遵循或有更好的做法,希望大家可以共同改進該規(guī)范。
一、編程規(guī)范
1、好代碼的原則
我們參考 Kent Beck 的簡單設計四原則來指導我們的如何寫出優(yōu)秀的代碼,如何有效地判斷我們的代碼是優(yōu)秀的。
- 通過所有測試(Passes its tests):強調的是外部需求,這是代碼實現(xiàn)最重要的
- 盡可能消除重復 (Minimizes duplication):代碼的模塊架構設計,保證代碼的正交性,保證代碼更容易修改
- 盡可能清晰表達 (Maximizes clarity):代碼的可閱讀性,保證代碼是容易閱讀的
- 更少代碼元素 (Has fewer elements):保證代碼是簡潔的,在簡潔和表達力之間,我們更看重表達力
以上四個原則的重要程度依次降低, 這組定義被稱做簡單設計原則。
2、項目命名規(guī)范
全部采用小寫方式, 以中劃線分隔。
正例:mall-management-system / order-service-client / user-api
反例:mall_management-system / mallManagementSystem / orderServiceClient
3、TODO/FIXME 規(guī)范
TODO/TBD(to be determined) 注釋一般用來描述已知待改進、待補充的修改點,并且加上作者名稱。
FIXME 注釋一般用來描述已知缺陷,它們都應該有統(tǒng)一風格,方便文本搜索統(tǒng)一處理。如:
// TODO <author-name>: 補充XX處理
// FIXME <author-name>: XX缺陷
4、方法參數(shù)規(guī)范
無論是 controller,service,manager,dao 亦或是其他的代碼,每個方法最多 3 個參數(shù),如果超出 3 個參數(shù)的話,要封裝成 javabean 對象。
- 方便他人調用,降低出錯幾率。尤其是當參數(shù)是同一種類型,僅僅依靠順序區(qū)分,稍有不慎便是災難性后果,而且排查起來也極其惡心。
- 保持代碼整潔、清晰度。當一個個方法里充斥著一堆堆參數(shù)的時候,再堅強的人,也會身心疲憊。
反例:
/**
* 使用證書加密數(shù)據工具方法
*
* @param param
* @param password 加密密碼
* @param priCert 私鑰
* @param pubCert 公鑰
* @return 返回加密后的字符串
*/
public String signEnvelop(JdRequestParam param, String password, String priCert, String pubCert){}
5、注釋規(guī)范
5-1、注釋和代碼一樣重要
注釋是我們披荊斬棘歷經磨難翻越需求這座大山時,留下的蹤跡和收獲的經驗教訓,這些寶貴的知識除了證明我們曾經存在過,也提醒著后來的人們殷鑒不遠、繼往開來。
注釋除了說明作用、邏輯之外。還有一個很重要的原因:當業(yè)務邏輯過于復雜,代碼過于龐大的時候,注釋就變成了一道道美化環(huán)境、分離與整理邏輯思路的路標。這是很重要的一點,它能有效得幫助我們免于陷入代碼與業(yè)務邏輯的泥沼之中。
正例:
/**
* 開始抽獎方法
* 保存中獎信息、獎勵用戶積分等
* @param luckDrawDTO
* @return ResponseDTO 返回中獎信息
*/
public ResponseDTO<String> startLuckDraw(LuckDrawDTO luckDrawDTO) {
// -------------- 1、校驗抽獎活動基本信息 ------------------------
xxx偽代碼一頓操作
// -------------- 2、新增抽獎記錄 -------------------------------
xxx偽代碼一頓操作
// -------------- 3、如果需要消耗積分,則扣除鋼镚積分 -------------
xxx偽代碼一頓操作
// -------------- 4、獲取獎品信息,開始翻滾吧 --------------------
xxx偽代碼一頓操作
return ResponseDTO.succ(luckDrawPrizeVO);
}
5-2、注釋和代碼的一致性
注釋并不是越多越好,當注釋過多,維護代碼的同時,還需要維護注釋,不僅變成了一種負擔,也與我們當初添加注釋的初衷背道而馳。
首先:大家應該通過清晰的邏輯架構,好的變量命名來提高代碼可讀性;需要的時候,才輔以注釋說明。注釋是為了幫助閱讀者快速讀懂代碼,所以要從讀者的角度出發(fā),按需注釋。注釋內容要簡潔、明了、無二義性,信息全面且不冗余。
其次:無論是修改、復制代碼時,都要仔細核對注釋內容是否正確。只改代碼,不改注釋是一種不文明行為,破壞了代碼與注釋的一致性,會讓閱讀者迷惑、費解,甚至誤解。
反例:
// 查詢部門
EmployeeDTO employee = employeeDao.listByDeptId(deptId);
5-3、方法注釋
方法要盡量通過方法名自解釋,不要寫無用、信息冗余的方法頭,不要寫空有格式的方法頭注釋。
方法頭注釋內容可選,但不限于:功能說明、返回值,用法、算法實現(xiàn)等等。尤其是對外的方法接口聲明,其注釋,應當將重要、有用的信息表達清楚。
正例:
/**
* 解析轉換時間字符串為 LocalDate 時間類
* 調用前必須校驗字符串格式 否則可能造成解析失敗的錯誤異常
*
* @param dateStr 必須是 yyyy-MM-dd 格式的字符串
* @return LocalDate
*/
public static LocalDate parseYMD(String dateStr){}
反例:
/**
* 校驗對象
*
* @param t
* @return String
*/
public static <T> String checkObj(T t);
反例中出現(xiàn)的問題:
- 方法注釋沒有說明具體的作用、使用事項。
- 參數(shù)、返回值,空有格式沒內容。這是非常重要一點,任何人調用任何方法之前都需要知道方法對參數(shù)的要求,以及返回值是什么。
二、項目規(guī)范
1、代碼目錄結構
統(tǒng)一的目錄結構是所有項目的基礎。
src 源碼目錄
|-- common 各個項目的通用類庫
|-- config 項目的配置信息
|-- constant 全局公共常量
|-- handler 全局處理器
|-- interceptor 全局連接器
|-- listener 全局監(jiān)聽器
|-- module 各個業(yè)務
|-- |--- employee 員工模塊
|-- |--- role 角色模塊
|-- |--- login 登錄模塊
|-- third 三方服務,比如redis, oss,微信sdk等等
|-- util 全局工具類
|-- Application.java 啟動類
2、common 目錄規(guī)范
common 目錄用于存放各個項目通用的項目,但是又可以依照項目進行特定的修改。
src 源碼目錄
|-- common 各個項目的通用類庫
|-- |--- anno 通用注解,比如權限,登錄等等
|-- |--- constant 通用常量,比如 ResponseCodeConst
|-- |--- domain 全局的 javabean,比如 BaseEntity,PageParamDTO 等
|-- |--- exception 全局異常,如 BusinessException
|-- |--- json json 類庫,如 LongJsonDeserializer,LongJsonSerializer
|-- |--- swagger swagger 文檔
|-- |--- validator 適合各個項目的通用 validator,如 CheckEnum,CheckBigDecimal 等
3、config 目錄規(guī)范
config 目錄用于存放各個項目通用的項目,但是又可以依照項目進行特定的修改。
src 源碼目錄
|-- config 項目的所有配置信息
|-- |--- MvcConfig mvc的相關配置,如interceptor,filter等
|-- |--- DataSourceConfig 數(shù)據庫連接池的配置
|-- |--- MybatisConfig mybatis的配置
|-- |--- .... 其他
4、module 目錄規(guī)范
module 目錄里寫項目的各個業(yè)務,每個業(yè)務一個獨立的頂級文件夾,在文件里進行 mvc 的相關劃分。
其中,domain 包里存放 entity, dto, vo,bo 等 javabean 對象
src
|-- module 所有業(yè)務模塊
|-- |-- role 角色模塊
|-- |-- |--RoleController.java controller
|-- |-- |--RoleConst.java role相關的常量
|-- |-- |--RoleService.java service
|-- |-- |--RoleDao.java dao
|-- |-- |--domain domain
|-- |-- |-- |-- RoleEntity.java 表對應實體
|-- |-- |-- |-- RoleDTO.java dto對象
|-- |-- |-- |-- RoleVO.java 返回對象
|-- |-- employee 員工模塊
|-- |-- login 登錄模塊
|-- |-- email 郵件模塊
|-- |-- .... 其他
5、 domain 包中的 javabean 命名規(guī)范
1) javabean 的整體要求:
- 不得有任何的業(yè)務邏輯或者計算
- 基本數(shù)據類型必須使用包裝類型
(Integer, Double、Boolean 等) - 不允許有任何的默認值
- 每個屬性必須添加注釋,并且必須使用多行注釋。
- 必須使用
lombok簡化getter/setter方法 - 建議對象使用
lombok的@Builder ,@NoArgsConstructor,同時使用這兩個注解,簡化對象構造方法以及set方法。
正例:
@Builder
@NoArgsConstructor
@Data
public class DemoDTO {
private String name;
private Integer age;
}
// 使用示例:
DemoDTO demo = DemoDTO.builder()
.name("yeqiu")
.age(66)
.build();
2)數(shù)據對象;XxxxEntity,要求:
- 以
Entity為結尾(阿里是為 DO 為結尾) - Xxxx 與數(shù)據庫表名保持一致
- 類中字段要與數(shù)據庫字段保持一致,不能缺失或者多余
- 類中的每個字段添加注釋,并與數(shù)據庫注釋保持一致
- 不允許有組合
- 項目內的日期類型必須統(tǒng)一,建議使用
java.util.Date,java.sql.Timestamp,java.time.LocalDateTime其中只一。
3)傳輸對象;XxxxDTO,要求:
- 不可以繼承自
Entity -
DTO可以繼承、組合其他DTO,VO,BO等對象 -
DTO只能用于前端、RPC 的請求參數(shù)
3)視圖對象;XxxxVO,要求:
- 不可繼承自
Entity -
VO可以繼承、組合其他DTO,VO,BO等對象 -
VO只能用于返回前端、rpc 的業(yè)務數(shù)據封裝對象
4)業(yè)務對象 BO,要求:
- 不可以繼承自
Entity -
BO對象只能用于service,manager,dao層,不得用于controller層
三、MVC 規(guī)范
1、整體分層
- controller 層
- service 層
- manager 層
- dao 層
2、 controller 層規(guī)范
1) 只允許在 method 上添加 RequestMapping 注解,不允許加在 class 上(為了方便的查找 url,放到 url 不能一次性查找出來)
正例:
@RestController
public class DepartmentController {
@GetMapping("/department/list")
public ResponseDTO<List<DepartmentVO>> listDepartment() {
return departmentService.listDepartment();
}
反例:
@RequestMapping ("/department")
public class DepartmentController {
@GetMapping("/list")
public ResponseDTO<List<DepartmentVO>> listDepartment() {
return departmentService.listDepartment();
}
2)不推薦使用 rest 命名 url, 只能使用 get/post 方法。url 命名上規(guī)范如下:
雖然 Rest 大法好,但是有時并不能一眼根據 url 看出來是什么操作,所以我們選擇了后者,這個沒有對與錯,只有哪個更適合我們的團隊。
/業(yè)務模塊/子模塊/動作
正例:
GET /department/get/{id} 查詢某個部門詳細信息
POST /department/query 復雜查詢
POST /department/add 添加部門
POST /department/update 更新部門
GET /department/delete/{id} 刪除部門
3)每個方法必須添加 swagger 文檔注解 @ApiOperation ,并填寫接口描述信息,描述最后必須加上作者信息 @author 哪吒 。
正例:
@ApiOperation("更新部門信息 @author 哪吒")
@PostMapping("/department/update")
public ResponseDTO<String> updateDepartment(@Valid @RequestBody DeptUpdateDTO deptUpdateDTO) {
return departmentService.updateDepartment(deptUpdateDTO);
}
4)controller 負責協(xié)同和委派業(yè)務,充當路由的角色,每個方法要保持簡潔:
- 不做任何的業(yè)務邏輯操作
- 不做任何的參數(shù)、業(yè)務校驗,參數(shù)校驗只允許使用@Valid 注解做簡單的校驗
- 不做任何的數(shù)據組合、拼裝、賦值等操作
正例:
@ApiOperation("添加部門 @author 哪吒")
@PostMapping("/department/add")
public ResponseDTO<String> addDepartment(@Valid @RequestBody DepartmentCreateDTO departmentCreateDTO) {
return departmentService.addDepartment(departmentCreateDTO);
}
5)只能在 controller 層獲取當前請求用戶,并傳遞給 service 層。
因為獲取當前請求用戶是從 ThreadLocal 里獲取取的,在 service、manager、dao 層極有可能是其他非 request 線程調用,會出現(xiàn) null 的情況,盡量避免
@ApiOperation("添加員工 @author yandanyang")
@PostMapping("/employee/add")
public ResponseDTO<String> addEmployee(@Valid @RequestBody EmployeeAddDTO employeeAddDTO) {
LoginTokenBO requestToken = SmartRequestTokenUtil.getRequestUser();
return employeeService.addEmployee(employeeAddDTO, requestToken);
}
3、 service 層規(guī)范
1)合理拆分 service 文件,如果業(yè)務較大,請拆分為多個 service。
如訂單業(yè)務,所有業(yè)務都寫到 OrderService 中會導致文件過大,故需要進行拆分如下:
-
OrderQueryService訂單查詢業(yè)務 -
OrderCreateService訂單新建業(yè)務 -
OrderDeliverService訂單發(fā)貨業(yè)務 -
OrderValidatorService訂單驗證業(yè)務
2)謹慎處理 @Transactional 事務注解的使用,不要簡單對 service 的方法添加個 @Transactional 注解就覺得萬事大吉了。應當合并對數(shù)據庫的操作,盡量減少添加了@Transactional方法內的業(yè)務邏輯。
@Transactional 注解內的 rollbackFor 值必須使用異常的基類 Throwable.class
對于@Transactional 注解,當 spring 遇到該注解時,會自動從數(shù)據庫連接池中獲取 connection,并開啟事務然后綁定到 ThreadLocal 上,如果業(yè)務并沒有進入到最終的 操作數(shù)據庫環(huán)節(jié),那么就沒有必要獲取連接并開啟事務,應該直接將 connection 返回給數(shù)據庫連接池,供其他使用(比較難以講解清楚,如果不懂的話就主動去問)。
反例:
@Transactional(rollbackFor = Throwable.class)
public ResponseDTO<String> upOrDown(Long departmentId, Long swapId) {
// 驗證 1
DepartmentEntity departmentEntity = departmentDao.selectById(departmentId);
if (departmentEntity == null) {
return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
}
// 驗證 2
DepartmentEntity swapEntity = departmentDao.selectById(swapId);
if (swapEntity == null) {
return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
}
// 驗證 3
Long count = employeeDao.countByDepartmentId(departmentId)
if (count != null && count > 0) {
return ResponseDTO.wrap(DepartmentResponseCodeConst.EXIST_EMPLOYEE);
}
// 操作數(shù)據庫 4
Long departmentSort = departmentEntity.getSort();
departmentEntity.setSort(swapEntity.getSort());
departmentDao.updateById(departmentEntity);
swapEntity.setSort(departmentSort);
departmentDao.updateById(swapEntity);
return ResponseDTO.succ();
}
以上代碼前三步都是使用 connection 進行驗證操作,由于方法上有@Transactional 注解,所以這三個驗證都是使用的同一個 connection。
若對于復雜業(yè)務、復雜的驗證邏輯,會導致整個驗證過程始終占用該 connection 連接,占用時間可能會很長,直至方法結束,connection 才會交還給數(shù)據庫連接池。
對于復雜業(yè)務的不可預計的情況,長時間占用同一個 connection 連接不是好的事情,應該盡量縮短占用時間。
正例:
DepartmentService.java
public ResponseDTO<String> upOrDown(Long departmentId, Long swapId) {
DepartmentEntity departmentEntity = departmentDao.selectById(departmentId);
if (departmentEntity == null) {
return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
}
DepartmentEntity swapEntity = departmentDao.selectById(swapId);
if (swapEntity == null) {
return ResponseDTO.wrap(DepartmentResponseCodeConst.NOT_EXISTS);
}
Long count = employeeDao.countByDepartmentId(departmentId)
if (count != null && count > 0) {
return ResponseDTO.wrap(DepartmentResponseCodeConst.EXIST_EMPLOYEE);
}
departmentManager.upOrDown(departmentSort,swapEntity);
return ResponseDTO.succ();
}
DepartmentManager.java
@Transactional(rollbackFor = Throwable.class)
public void upOrDown(DepartmentEntity departmentEntity ,DepartmentEntity swapEntity){
Long departmentSort = departmentEntity.getSort();
departmentEntity.setSort(swapEntity.getSort());
departmentDao.updateById(departmentEntity);
swapEntity.setSort(departmentSort);
departmentDao.updateById(swapEntity);
}
將數(shù)據在 service 層準備好,然后傳遞給 manager 層,由 manager 層添加@Transactional 進行數(shù)據庫操作。
3)需要注意的是:注解 @Transactional 事務在類的內部方法調用是不會生效的
反例:如果發(fā)生異常,saveData方法上的事務注解并不會起作用
@Service
public class OrderService{
public void createOrder(OrderCreateDTO createDTO){
this.saveData(createDTO);
}
@Transactional(rollbackFor = Throwable.class)
public void saveData(OrderCreateDTO createDTO){
orderDao.insert(createDTO);
}
}
Spring采用動態(tài)代理(AOP)實現(xiàn)對bean的管理和切片,它為我們的每個class生成一個代理對象。只有在代理對象之間進行調用時,可以觸發(fā)切面邏輯。而在同一個class中,方法A調用方法B,調用的是原對象的方法,而不通過代理對象。所以Spring無法攔截到這次調用,也就無法通過注解保證事務了。簡單來說,在同一個類中的方法調用,不會被方法攔截器攔截到,因此事務不會起作用。
解決方案:
- 可以將方法放入另一個類,如新增
manager層,通過spring注入,這樣符合了在對象之間調用的條件。 - 啟動類添加
@EnableAspectJAutoProxy(exposeProxy = true),方法內使用AopContext.currentProxy()獲得代理類,使用事務。
SpringBootApplication.java
@EnableAspectJAutoProxy(exposeProxy = true)
@SpringBootApplication
public class SpringBootApplication {}
OrderService.java
public void createOrder(OrderCreateDTO createDTO){
OrderService orderService = (OrderService)AopContext.currentProxy();
orderService.saveData(createDTO);
}
4)service是具體的業(yè)務處理邏輯服務層,盡量避免將web層某些參數(shù)傳遞到service中。
反例:
public ResponseDTO<String> handlePinganRequest(HttpServletRequest request){
InputStreamReader inputStreamReader = new InputStreamReader(request.getInputStream(), "GBK");
BufferedReader reader = new BufferedReader(inputStreamReader);
StringBuilder sb = new StringBuilder();
String str;
while ((str = reader.readLine()) != null) {
sb.append(str);
}
if(!JSON.isValid(msg)){
return ResponseDTO.wrap(ResponseCodeConst.ERROR_PARAM);
}
PinganMsgDTO PinganMsgDTO = JSON.parseObject(msg,PinganMsgDTO.class);
// 示例結束
}
反例中出現(xiàn)的問題:
- 反例中把
HttpServletRequest傳遞到service中,是為了獲取Request流中的字符信息,然后才是真正的業(yè)務處理。按照分層的初衷:將代碼、業(yè)務邏輯解耦,正確的做法應該是handlePinganRequest方法將String字符作為參數(shù)直接處理業(yè)務,將從Request中獲取字符的操作放入controller中。 - 另一個壞處是不方便做單元測試,還得一個
new一個HttpServletRequest并制造一個InputStream,然而這樣做并不能模擬到真實的業(yè)務情景及數(shù)據。
4、 manager 層規(guī)范
manager 層的作用(引自《阿里 java 手冊》):
- 對第三方平臺封裝的層,預處理返回結果及轉化異常信息;
- 對 Service 層通用能力的下沉,如緩存方案、中間件通用處理;
- 與 DAO 層交互,對多個 DAO 的組合復用。
5、 dao 層規(guī)范
優(yōu)先使用 mybatis-plus 框架。如果需要多個數(shù)據源操作的,可以選擇使用 SmartDb 框架。
1)所有 Dao 繼承自 BaseMapper
2)禁止使用 Mybatis-plus 的 Wrapper 條件構建器
3)禁止直接在 mybatis xml 中寫死常量,應從 dao 中傳入到 xml 中
3)建議不要使用星號 * 代替所有字段
正例:
NoticeDao.java
Integer noticeCount(@Param("sendStatus") Integer sendStatus);
---------------------------------------------
NoticeMapper.xml
<select id="noticeCount" resultType="integer">
select
count(1)
from t_notice
where
send_status = #{sendStatus}
</select>
反例:
NoticeDao.java
Integer noticeCount();
---------------------------------------------
NoticeMapper.xml
<select id="noticeCount" resultType="integer">
select
count(1)
from t_notice
where
send_status = 0
</select>
3)dao層方法命名規(guī)范
- 獲取單個對象的方法用
get做前綴。 - 獲取多個對象的方法用
list做前綴。 - 獲取統(tǒng)計值的方法用
count做前綴。 - 插入的方法用
save/insert做前綴。 - 刪除的方法用
remove/delete做前綴。 - 修改的方法用
update做前綴。
建議:dao層方法命名盡量以sql語義命名,避免與業(yè)務關聯(lián)。
正例:
List<PerformanceDTO> listByMonthAndItemId(@Param("month") String month, @Param("itemId") Integer itemId);
反例:
List<PerformanceDTO> getInternalData(@Param("month") String month, @Param("itemId") Integer itemId);
反例中出現(xiàn)的不規(guī)范操作:
- get代表單個查詢,批量查詢的應該 list 開頭。
- 命名與業(yè)務關聯(lián),局限了dao方法的使用場景和范圍,降低了方法的復用性,造成他人困惑以及重復造輪子。
6、boolean類型的屬性命名規(guī)范
類中布爾類型的變量,都不要加is,否則部分框架解析會引起序列化錯誤。反例:定義為基本數(shù)據類型 Boolean isDeleted;的屬性,它的方法也是 isDeleted(),RPC在反向解析的時候,“以為”對應的屬性名稱是 deleted,導致屬性獲取不到,進而拋出異常。
這是阿里巴巴開發(fā)手冊中的原文,我們團隊的規(guī)定是:boolean 類型的類屬性和數(shù)據表字段都統(tǒng)一使用 flag 結尾。雖然使用 isDeleted,is_deleted 從字面語義上更直觀,但是比起可能出現(xiàn)的潛在錯誤,這點犧牲還是值得的。
正例:
deletedFlag,deleted_flag,onlineFlag,online_flag
7、
四、數(shù)據庫 規(guī)范
1 建表規(guī)范
表必備三字段:id, create_time, update_time
- id 字段 Long 類型,單表自增,自增長度為 1
- create_time 字段 datetime 類型,默認值 CURRENT_TIMESTAMP
- update_time 字段 datetime 類型,默認值 CURRENT_TIMESTAMP, On update CURRENT_TIMESTAMP
2 枚舉類表字段注釋需要將所有枚舉含義進行注釋
修改或增加字段的狀態(tài)描述,必須要及時同步更新注釋。
如下表的 sync_status 字段 同步狀態(tài) 0 未開始 1同步中 2同步成功 3失敗。
正例:
CREATE TABLE `t_change_data` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`sync_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT '同步狀態(tài) 0 未開始 1同步中 2同步成功 3失敗',
`sync_time` DATETIME NULL DEFAULT NULL COMMENT '同步時間',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`change_data_id`)
)
反例:
CREATE TABLE `t_change_data` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`sync_status` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT '同步狀態(tài) ',
`sync_time` DATETIME NULL DEFAULT NULL COMMENT '同步時間',
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`change_data_id`)
)
3 合理結合業(yè)務給表字段添加索引和唯一索引
具體索引規(guī)范請參照《阿里巴巴 Java 開發(fā)手冊》索引規(guī)約
五、其他
1、代碼提交規(guī)范
- 提交前應該冷靜、仔細檢查一下,確保沒有忘記加入版本控制或不應該提交的文件。
- 提交前應該先編譯一次(idea里ctrl+F9),防止出現(xiàn)編譯都報錯的情況。
- 提交前先更新pull一次代碼,提交前發(fā)生沖突要比提交后發(fā)生沖突容易解決的多。
- 提交前檢查代碼是否格式化,是否符合代碼規(guī)范,無用的包引入、變量是否清除等等。
- 提交時檢查注釋是否準確簡潔的表達出了本次提交的內容。
2、maven項目
- pom禁止出現(xiàn)相同 groupId,artifactId 的依賴配置。
- 項目名稱應該與 artifactId 保持一致。
- 定期檢查jar包依賴關系,及時排除解決沖突的jar包。
3、保持項目整潔
使用git,必須添加 .gitignore 忽略配置文件。
不要提交與項目無關的內容文件:idea配置、target包等。
本文由博客群發(fā)一文多發(fā)等運營工具平臺 OpenWrite 發(fā)布