1、常用規範類
1.1、異常相關
1.1.1、業務異常類
package com.healerjean.proj.exception;
import com.healerjean.proj.enums.ResponseEnum;
/**
* 系統業務異常
*/
public class BusinessException extends RuntimeException {
private int code;
public BusinessException(int code) {
this.code = code;
}
public BusinessException(String message) {
super(message);
this.code = ResponseEnum.邏輯錯誤.code;
}
public BusinessException(int code, String message) {
super(message);
this.code = code;
}
public BusinessException(ResponseEnum responseEnum) {
super(responseEnum.msg);
this.code = responseEnum.code ;
}
public BusinessException(ResponseEnum responseEnum,String message) {
super(message);
this.code = responseEnum.code ;
}
public BusinessException(String message, Throwable cause) {
super(message, cause);
this.code = ResponseEnum.邏輯錯誤.code;
}
public BusinessException(int code ,Throwable e) {
super(e);
this.code = code;
}
public BusinessException(ResponseEnum responseEnum, Throwable t) {
super(responseEnum.msg, t);
this.code = responseEnum.code;
}
public void setCode(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
1.1.2、參數異常類
package com.healerjean.proj.exception;
import com.healerjean.proj.enums.ResponseEnum;
/**
* @author HealerJean
* @ClassName ParameterErrorException
* @date 2019/10/17 16:19.
* @Description 參數錯誤
*/
public class ParameterErrorException extends com.healerjean.proj.exception.BusinessException {
public ParameterErrorException() {
super(ResponseEnum.參數錯誤);
}
public ParameterErrorException(ResponseEnum responseEnum) {
super(ResponseEnum.參數錯誤, responseEnum.msg);
}
public ParameterErrorException(String msg) {
super(ResponseEnum.參數錯誤, msg);
}
}
1.1.3、接口異常類
package com.healerjean.proj.exception;
import com.healerjean.proj.enums.ResponseEnum;
/**
* @author HealerJean
* @ClassName HaoDanKuApiException
* @date 2019/10/15 20:08.
* @Description
*/
public class HaoDanKuApiException extends BusinessException {
public HaoDanKuApiException( ) {
super(ResponseEnum.好單庫口請求異常);
}
public HaoDanKuApiException(String msg) {
super(ResponseEnum.好單庫接口數據異常, msg);
}
public HaoDanKuApiException(Throwable e) {
super(ResponseEnum.好單庫口請求異常, e);
}
}
1.1.4、異常全局處理
package com.healerjean.proj.config;
import com.healerjean.proj.dto.ResponseBean;
import com.healerjean.proj.enums.ResponseEnum;
import com.healerjean.proj.exception.BusinessException;
import com.healerjean.proj.exception.ParameterErrorException;
import com.healerjean.proj.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import javax.servlet.http.HttpServletResponse;
import javax.validation.UnexpectedTypeException;
import java.util.HashMap;
import java.util.Map;
/**
* @author HealerJean
* @version 1.0v
* @ClassName ControllerHandleExceptionConfig
* @date 2019/5/31 20:19.
* @Description
*/
@Slf4j
@ControllerAdvice
public class ControllerHandleConfig {
/**
* 不支持的請求方始
*/
@ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
@ResponseStatus(value = HttpStatus.METHOD_NOT_ALLOWED)
public ResponseBean methodNotSupportExceptionHandler(HttpRequestMethodNotSupportedException e) {
log.error("不支持的請求方式", e);
return ResponseBean.buildFailure(ResponseEnum.不支持的請求方式.code, e.getMessage());
}
/**
* 參數類型錯誤
* 1、(BindException : 比如 Integer 傳入String )
* Field error in object 'demoDTO' on field 'age': rejected value [fasdf]; codes [typeMismatch.demoDTO.age,typeMismatch.age,typeMismatch.java.lang.Integer,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [demoDTO.age,age]; arguments []; default message [age]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.lang.Integer' for property 'age'; nested exception is java.lang.NumberFormatException: For input string: "fasdf"]
*/
@ExceptionHandler(value = {BindException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ResponseBean bindExceptionHandler(BindException e) {
log.error("====參數類型錯誤===", e);
return ResponseBean.buildFailure(ResponseEnum.參數類型錯誤.code, e.getMessage());
}
/**
* 參數格式問題
*/
@ExceptionHandler(value = {MethodArgumentTypeMismatchException.class, HttpMessageConversionException.class, UnexpectedTypeException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ResponseBean httpMessageConversionExceptionHandler(Exception e) {
log.error("====參數格式異常===", e);
return ResponseBean.buildFailure(ResponseEnum.參數格式異常.code, e.getMessage());
}
/**
* 參數錯誤
*/
@ExceptionHandler(value = ParameterErrorException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ResponseBean parameterErrorExceptionHandler(ParameterErrorException e) {
log.error("參數異常------------參數錯誤:code:{},message:{}", e.getCode(), e.getMessage());
return ResponseBean.buildFailure(e.getCode(), e.getMessage());
}
/**
* 業務異常,給前臺返回異常數據
*/
@ExceptionHandler(value = BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ResponseBean businessExceptionHandler(BusinessException e) {
log.error("業務異常------------異常信息:code:{},message{}" ,e.getCode(), e.getMessage());
return ResponseBean.buildFailure(e.getCode(),e.getMessage());
}
/**
* 所有異常報錯
*/
@ExceptionHandler
@ResponseBody
public HttpEntity<ResponseBean> allExceptionHandler(HttpServletResponse response, Exception e) {
log.error("====系統錯誤===", e);
response.setStatus(ResponseEnum.系統錯誤.code);
return returnMessage(ResponseBean.buildFailure(ResponseEnum.系統錯誤));
}
private HttpEntity<ResponseBean> returnMessage(ResponseBean responseBean) {
HttpHeaders header = new HttpHeaders();
header.add("Content-Type", "application/json");
header.add("Charset", "UTF-8");
return new HttpEntity<>(responseBean, header);
}
/**
* 參數非法
* 1、(BindException : 比如 Integer 傳入abc )
*/
// @ExceptionHandler(value = {MethodArgumentTypeMismatchException.class, HttpRequestMethodNotSupportedException.class, HttpMessageConversionException.class, BindException.class, UnexpectedTypeException.class})
// @ResponseBody
// public HttpEntity<ResponseBean> httpMessageConversionExceptionHandler(HttpServletResponse response, Exception e) {
// log.error("====參數格式異常===", e);
// // 等同於 @ResponseStatus(HttpStatus.BAD_REQUEST)
// // 但是setStatus 不能比隨便設置,最好一般情況下不要和HttpStatus 有重複的,這樣有可能會造成沒有輸出Response body
// response.setStatus(ResponseEnum.參數格式異常.code);
// return returnMessage(ResponseBean.buildFailure(ResponseEnum.參數格式異常));
// }
// @ExceptionHandler(value ={HttpMessageConversionException.class, BindException.class} )
// @ResponseBody
// public HttpEntity<ResponseBean> httpMessageConversionExceptionHandler(Exception e) {
// log.error("====參數格式異常===", e);
// return new ResponseEntity<>(ResponseBean.buildFailure(ResponseEnum.參數格式異常),HttpStatus.BAD_REQUEST);
// }
}
1.2、枚舉
1.2.1、響應枚舉
package com.healerjean.proj.enums;
import java.util.Arrays;
import java.util.List;
/**
* @author HealerJean
* @version 1.0v
* @ClassName ResponseEnum
* @date 2019/6/13 20:45.
* @msgcription
*/
public enum ResponseEnum {
正常(200, "訪問正常"),
參數錯誤(301, "參數錯誤"),
參數格式異常(302, "參數格式異常"),
不支持的請求方式(303, "不支持的請求方式"),
參數類型錯誤(304, "參數類型錯誤"),
邏輯錯誤(305, "邏輯錯誤"),
未登陸(306, "未登陸"),
登陸成功(307, "登陸成功"),
重複操作(308, "重複操作"),
非法操作(309, "非法操作"),
請求無法被服務器理解(400, "請求無法被服務器理解"),
未授權(401, "未授權"),
訪問禁止(403, "訪問禁止"),
頁面丟失(404, "頁面丟失"),
系統錯誤(500, "系統錯誤"),
未知錯誤(999, "未知錯誤"),
用戶已經存在(1000, "用戶已經存在"),
用戶不存在(1001, "用戶不存在"),
微信接口請求異常(2001,"微信接口請求異常"),
淘寶接口請求異常(2002,"淘寶接口請求異常"),
淘寶接口數據異常(2003,"淘寶接口數據異常"),
好單庫口請求異常(2004,"好單庫口請求異常"),
好單庫接口數據異常(2005,"好單庫接口數據異常"),
;
public int code;
public String msg;
ResponseEnum(int code, String msg) {
this.code = code;
this.msg = msg;
}
public static boolean checkExist( Integer code){
for (ResponseEnum value : ResponseEnum.values()){
if (value.code == code){
return true;
}
}
return false;
}
public static ResponseEnum toEnum(int code){
for (ResponseEnum value : ResponseEnum.values()){
if (value.code == code){
return value;
}
}
return ResponseEnum.未知錯誤;
}
public static String getMsg(int code){
for (ResponseEnum value : ResponseEnum.values()){
if (value.code == code){
return value.msg;
}
}
return ResponseEnum.未知錯誤.msg;
}
public ResponseEnum value(String enumName){
return valueOf( enumName ) ;
}
public static List<ResponseEnum> getList(){
return Arrays.asList(values());
}
}
1.2.2、業務枚舉
package com.healerjean.proj.enums;
/**
* @author HealerJean
* @ClassName BusinessEnum
* @date 2019/9/30 14:39.
* @Description
*/
public interface BusinessEnum {
/**
* 驗證碼枚舉
*/
enum VerifyCodeTypeEnum {
圖片驗證碼("captcha", "圖片驗證碼"),
註冊郵箱驗證碼("RegistEmail", "註冊郵箱驗證碼"),
找回密碼郵箱驗證碼("RetrievePasswordEmail", "找回密碼郵箱驗證碼"),
;
VerifyCodeTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String code;
public String desc;
public static VerifyCodeTypeEnum toEnum(String code) {
for (VerifyCodeTypeEnum item : VerifyCodeTypeEnum.values()) {
if (item.code.equals(code)) {
return item;
}
}
return null;
}
}
/**
* 模板類型
*/
enum TemplateTypeEnum {
郵件("Email", "郵件"),
;
public String code;
public String desc;
TemplateTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
}
/**
* 模板名字
*/
enum TempleNameEnum {
郵箱驗證("VerifyEmail", "郵箱驗證"),
找回密碼郵箱驗證("PasswordVerifyEmail", "找回密碼郵箱驗證"),
手機號驗證("VerifyPhone", "手機號驗證"),
;
TempleNameEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String code;
public String desc;
public static TempleNameEnum toEnum(String code) {
for (TempleNameEnum item : TempleNameEnum.values()) {
if (item.code.equals(code)) {
return item;
}
}
return null;
}
}
/**
* 菜單類型
*/
enum MenuTypeEnum {
後端菜單("0", "後端菜單"),
前端菜單("1", "前端菜單");
MenuTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String code;
public String desc;
public static MenuTypeEnum toEnum(String code) {
for (MenuTypeEnum value : MenuTypeEnum.values()) {
if (value.code .equals( code)) {
return value;
}
}
return null;
}
}
/**
* 用戶類型
*/
enum UserTypeEnum {
管理人員("manager", "管理人員"),
網站用戶("webuser", "網站用戶");
UserTypeEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String code;
public String desc;
public static MenuTypeEnum toEnum(String code) {
for (MenuTypeEnum value : MenuTypeEnum.values()) {
if (value.code .equals( code)) {
return value;
}
}
return null;
}
}
}
1.2.3、狀態枚舉
package com.healerjean.proj.enums;
/**
* @Description
* @Author HealerJean
* @Date 2019-06-16 01:58.
*/
public enum StatusEnum {
生效("10", "生效"),
廢棄("99", "廢棄");
StatusEnum(String code, String desc) {
this.code = code;
this.desc = desc;
}
public String code;
public String desc;
}
1.2.4、下拉菜單枚舉
package com.duodian.admore.data;
import java.io.Serializable;
/**
* 下拉列表用
*/
public class LabelValueBean implements Serializable{
private static final long serialVersionUID = -1211726511402154326L;
private String label;
private String value;
private Boolean checked = false;
public LabelValueBean() {
}
public LabelValueBean(String label, String value) {
this.label = label;
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public Boolean getChecked() {
return checked;
}
public void setChecked(Boolean checked) {
this.checked = checked;
}
}
1.3、響應Bean
package com.healerjean.proj.dto;
import com.healerjean.proj.enums.ResponseEnum;
import com.healerjean.proj.utils.JsonUtils;
/**
* 返回對象
*/
public class ResponseBean {
private ResponseBean() {
}
public static ResponseBean buildSuccess() {
ResponseBean responseBean = new ResponseBean();
responseBean.setSuccess(true);
responseBean.setCode(ResponseEnum.正常.code);
responseBean.setDate(System.currentTimeMillis() + "");
return responseBean;
}
public static ResponseBean buildSuccess(String msg) {
ResponseBean responseBean = new ResponseBean();
responseBean.setSuccess(true);
responseBean.setCode(ResponseEnum.正常.code);
responseBean.setResult(msg);
responseBean.setDate(System.currentTimeMillis() + "");
return responseBean;
}
public static ResponseBean buildSuccess(Object result) {
ResponseBean responseBean = new ResponseBean();
responseBean.setSuccess(true);
responseBean.setCode(ResponseEnum.正常.code);
responseBean.setResult(result);
responseBean.setDate(System.currentTimeMillis() + "");
return responseBean;
}
public static ResponseBean buildSuccess(String msg, Object result) {
ResponseBean responseBean = new ResponseBean();
responseBean.setSuccess(true);
responseBean.setCode(ResponseEnum.正常.code);
responseBean.setMsg(msg);
responseBean.setResult(result);
responseBean.setDate(System.currentTimeMillis() + "");
return responseBean;
}
public static String buildSensitivitySuccess(String msg, Object result) {
return JsonUtils.toJsonStringWithSensitivity(buildSuccess(msg, result));
}
public static String buildSensitivitySuccess(Object result) {
return JsonUtils.toJsonStringWithSensitivity(buildSuccess(result));
}
public static ResponseBean buildFailure() {
ResponseBean responseBean = new ResponseBean();
responseBean.setSuccess(false);
responseBean.setCode(ResponseEnum.系統錯誤.code);
responseBean.setDate(System.currentTimeMillis() + "");
return responseBean;
}
public static ResponseBean buildFailure(String msg) {
ResponseBean responseBean = new ResponseBean();
responseBean.setSuccess(false);
responseBean.setCode(ResponseEnum.系統錯誤.code);
responseBean.setMsg(msg);
responseBean.setDate(System.currentTimeMillis() + "");
return responseBean;
}
public static ResponseBean buildFailure(ResponseEnum responseEnum) {
ResponseBean responseBean = new ResponseBean();
responseBean.setSuccess(false);
responseBean.setCode(responseEnum.code);
responseBean.setMsg(responseEnum.msg);
responseBean.setDate(System.currentTimeMillis() + "");
return responseBean;
}
public static ResponseBean buildFailure(int code, String msg) {
ResponseBean responseBean = new ResponseBean();
responseBean.setSuccess(false);
responseBean.setCode(code);
responseBean.setMsg(msg);
responseBean.setDate(System.currentTimeMillis() + "");
return responseBean;
}
public static ResponseBean buildFailure(ResponseEnum responseEnum, String msg) {
ResponseBean responseBean = new ResponseBean();
responseBean.setSuccess(false);
responseBean.setCode(responseEnum.code);
responseBean.setMsg(msg);
responseBean.setDate(System.currentTimeMillis() + "");
return responseBean;
}
private boolean success;
private Object result = "{}";
private String msg = "";
private int code;
private String date;
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
2、代碼規範
2.1、方法、實體名字:
小米
manager dao 層
queryBeanSingle
queryBeanPage
queryBeanList
queryBeanPageLike
queryBeanListLike
2.1.1、查詢
- 單個記錄查詢
根據某幾個簡單屬性查詢
查詢數據庫 findBeanNameBy
不查詢數據庫 getBeanNameBy
查詢數據庫 findBeanName
不查詢數據庫 getBeanName
- 多個記錄查詢
根據某幾個簡單屬性查詢
查詢數據庫 qureyBeanNameBy
不查詢數據庫 dataBeanNameBy
查詢數據庫分頁 qureyBeanNamePage
查詢數據庫集合 qureyBeanNameList
不查詢數據庫分頁 dataBeanNamePage
不查詢數據庫集合 dataBeanNameList
2.1.2、刪除
更新status字段
deleteBean
直接刪除庫裏的數據
deleteDBeanName
2.1.4、接口請求 rquest/response
請求data
請求參數的data爲 ReqRecordData
返回單個實體
返回結果的data爲 RspRecordModel
返回集合
返回結果的data爲 RspRecordList
2.2、註釋、日誌規範
2.2.1、controller日誌
- -------8個-
- 頭部是controller說明,日誌內容寫上頭部以及功能日誌說明
/**
* @author HealerJean
* @ClassName UserController
* @date 2019/10/18 14:10.
* @Description 用戶管理
*/
public class UserController extends BaseController {
public ResponseBean register(@RequestBody UserDTO userDTO) {
log.info("用戶管理---------用戶註冊---------請求參數:{}", userDTO);
log.info("用戶管理---------用戶註冊---------請求參數:{},響應結果:{}",userDTO, userDTO);
}
/**
* @author HealerJean
* @ClassName UserController
* @date 2019/10/18 14:10.
* @Description 系統管理-字典管理
*/
public ResponseBean getDictType(@PathVariable Long id) {
log.info("系統管理-字典管理--------字典類型查詢--------字典類型Id:{}", id);
log.info("系統管理-字典管理--------字典類型查詢--------字典類型Id:{},響應結果:{}", id,dict);
}
2.2.2、 方法註釋和日誌
/**
* 根據合同模板生成合同初始pdf文件,講合同狀態設置爲待確認狀態
* 1、數據校驗
* 1.1、基本數據校驗
* 1.2、校驗合同模板是否存在
* 1.3、校驗簽署人是否完整
* 1.4、校驗簽署方是否真實有效
* 2、把合同簽署各方的信息和模板取出,將變量更替,生成word和pdf
* 4、保存簽署人信息
* 5、合同初始化日誌保存
* 6、刪除臨時文件
*/
void createContractByTeamplate(ContractDTO contractDTO) ;
2.3、異常問題
2.3.1 、工具類異常問題
public class UrlEncodeUtil {
public String encode(String text){
try {
return URLEncoder.encode(text,"gbk" );
}catch (UnsupportedEncodingException e) {
throw new RuntimeException("{}加密失敗", text,e);
}catch (Exception e){
throw new RuntimeException("{}加密失敗”,text, e);
}
}
}
3、數據庫規範
3.1、demo
3.1.1、建表語句
- 是否 nuLl 必須not null ,這樣不方便建立索引,爲了防止爲null,我們可以給賦予初始值,今後建議 default ‘’,給一個空串,空串不佔內存空間,NULL是佔內存空間的
create table test
(
`id` bigint(20) unsigned not null auto_increment comment '主鍵',
uk_name bigint(20) unsigned not null comment 'uk',
idx_name bigint(20) unsigned not null comment 'idx',
ref_item_id bigint(20) unsigned not null comment 'item表主鍵',
status varchar(32) not null comment '產品狀態 字典表 productstatus',
create_user bigint(20) unsigned null default 0 comment '創建人',
create_name varchar(64) null default '' comment '創建人名稱',
create_time datetime not null default current_timestamp comment '創建時間',
update_user bigint(20) unsigned null default 0 comment '更新人',
update_name varchar(64) null default '' comment '更新人名稱',
update_time datetime not null default current_timestamp on update current_timestamp comment '更新時間',
unique index uk_name (uk_name) using btree comment '唯一索引',
index idx_name (idx_name) using btree comment '索引',
primary key (`id`) using btree
) engine = innodb comment '測試表'
3.1.2、建表說明
類型 | 名稱 | 長度 | 解釋 |
---|---|---|---|
bigint | 主鍵 | bigint(20) | |
varchar | 地址 | varchar(128) | |
varchar | 狀態 | varchar(8) | |
varchar | 附件,逗號相隔 | varchar(128) | |
varchar | 備註、描述 | varchar(128/256) | |
varchar | 手機號 | varchar(20) | |
varchar | 名字 | varchar(64) | |
varchar | 郵件 | varchar(64) | |
decimal | 金額 | decimal(20,0) | 以分爲單位 |
decimal | 百分比 | decimal(7,6) | 80% 0.800000 |
int | 數字 | int(10) | |
tinyint | 布爾 | tinyint(1) |
3.2、基礎規範
3.2.1、統一說明
-
MySQL字符集統一使用utf8,默認排序規則:utf8_general_ci
-
使用InnoDB存儲引擎,默認事務隔離級別REPEATABLE-READ(可重複讀)
-
不要使用MySQL存儲過程,視圖,觸發器,Event, InnoDB外鍵約束
-
每個數據表都添加註釋 comment, 每個字段也添加comment
-
不要在數據庫中存儲大圖片或大文件,儘量使用簡單的數據類型,避免使用blob和text類型
-
單表數據量控制在1000W行以內 ,採用合適的分庫分表策略,例如十庫百表
3.2.2、字段設計
- 表示狀態字段(0-255)的使用TINYINT UNSINGED ; 0避免成爲有效狀態值,非負的數字類型字段,都添加上UNSINGED,
- 時間字段使用時間日期類型,避免使用字符串類型存儲,日期使用DATE類型,年使用YEAR類型,日期時間可使用DATETIME和TIMESTAMP
- 字符串VARCHAR(N), 其中N表示字符個數,請儘量減少N的大小
- 字段都設置爲NOT NULL, 爲字段提供默認值,如’’和’0’ ;
- 主鍵儘量保持增長趨勢,建議使用id的生成器,避免使用表的自增列
3.2.3、sql使用規範
- 避免使用join,子查詢等SQL
1.對於mysql,不推薦使用子查詢和join是因爲本身join的效率就是硬傷,一旦數據量很大效率就很難保證,強烈推薦分別根據索引單表取數據,然後在程序裏面做join,merge數據,導致性能下降
2.子查詢就更別用了,效率太差,執行子查詢時,MYSQL需要創建臨時表,查詢完畢後再刪除這些臨時表,所以,子查詢的速度會受到一定的影響,這裏多了一個創建和銷燬臨時表的過程。
3.如果是JOIN的話,它是走嵌套查詢的。小表驅動大表,且通過索引字段進行關聯。如果表記錄比較少的話,還是OK的。大的話業務邏輯中可以控制處理
- 在線業務的update和delete的where中是唯一索引或者主鍵,避免一次修改多條語句的情況,而且這樣鎖住的是一行數據
- 避免在MySQL數據庫中進行計算操作,儘量由業務來處理運算,數據庫,就應該讓它做存儲數據,查詢數據的事情,
- 避免使用select * , 只返回自己需要的字段,枚舉出要返回的字段名稱
- SQL過濾的where條件儘量不使用OR, NOT IN , NOT EXIST
- 使用where IN()過濾時,IN集合個數必須小於500,因爲in的數據少的時候,mysql優化器會可能會使用索引,但是當數據太多以後就不一定了,可以讓MySQL按照ID順序進行查詢,這可能比隨機的關聯要更高效
3.3、表名設計
儘量使用 項目名(scf)_模塊名_表名
項目名,因爲我們可能一個數據庫對應多個項目,這樣容易區分
模塊名,能夠清晰明瞭的知道是哪個模塊的表
3.4、字段設計
使用下劃線,不要使用大小寫組合,原因自己理解吧,兄弟
3.4.1、長度說明
3.4.1.1、數字型
類型 | 字節 | 範圍(有符號) | 範圍(無符號) | 用途 |
---|---|---|---|---|
tintint | 1 | (-128,127) | (0,255) | 小整數值 |
smallint | 2 | (-32 768,32 767) | (0,65 535) | 大整數值 |
mediumint | 3 | (-8 388 608,8 388 607) | (0,16 777 215) | 大整數值 |
int/integer | 4 | (-2 147 483 648,2 147 483 647) | (0,4 294 967 295) | 大整數值 |
bigint | 8 | (-9 233 372 036 854 775 808,9 223 372 036 854 775 807) | (0,18 446 744 073 709 551 615) | 極大整數值 |
float | 4 | (-3.402 823 466 E+38,1.175 494 351 E-38),0,(1.175 494 351 E-38,3.402 823 466 351 E+38) | 0,(1.175 494 351 E-38,3.402 823 466 E+38) | 單精度/浮點數值 |
double | 8 | (1.797 693 134 862 315 7 E+308,2.225 073 858 507 201 4 E-308),0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 0,(2.225 073 858 507 201 4 E-308,1.797 693 134 862 315 7 E+308) | 雙精度/浮點數值 |
decimal | M>D,爲M+2否則爲D+2 | decimal(M,D)依賴於M和D的值 | 依賴於M和D的值 | 小數值 |
3.4.1.2、字符類型
類型 | 字節 | 用途 |
---|---|---|
char | 0-255字節 | 變長字符串 |
varchar | 0-255字節 | 變長字符串 |
tinyblog | 0-255字節 | 不超過 255 個字符的二進制字符串 |
tinytext | 0-255字節 | 短文本字符串 |
blog | 0-65 535字節 | 二進制形式的長文本數據 |
text | 0-65 535字節 | 長文本數據 |
mediumblog | 0-16 777 215字節 16M | 二進制形式的中等長度文本數據 |
mediumtext | 0-16 777 215字節 16M | 中等長度文本數據 |
longblog | 0-4 294 967 295字節 4G | 二進制形式的極大文本數據 |
longtext | 0-4 294 967 295字節 4G | 極大文本數據 |
3.4.1.3、時間類型
類型 | 字節 | 取值範圍 | 用途 | |
---|---|---|---|---|
year | 1 | 1901——2155 | YYYY | 日期值 |
date | 4 | 1000-01-01——9999-12-31 | YYYY-MM-DD | 時間值或持續時間 |
time | 3 | -838:59:59——838:59:59 | HH:MM:SS | 年份值 |
datetime | 8 | 1000-01-01 00:00:00——9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和時間值 |
timestamp | 4 | 19700101080001——20380119111407 | YYYYMMDD HHMMSSsss | 混合日期和時間值,時間戳 |
3.4.2、字段詳解
3.4.2.1、字符 char(M),varcahr(M)
char是一種固定長度的類型,varchar則是一種可變長度的類型,它們的區別是:
-
char(M)類型的數據列裏,每個值都佔用M個字節,如果某個長度小於M,
MySQL就會在它的右邊用空格字符補足
.(在檢索操作中那些填補出來的空格字符將被去掉) -
varchar(M)類型的數據列裏,每個值只佔用剛好夠用的字節再加上一個用來記錄其長度的字節(即總長度爲L+1字節)
注意:一般千萬不要使用text ,這樣從mybatis取出來看似是String類型的,但是在實際使用中卻或出現字符問題
3.4.2.3、日期
1、Local類型的日期
LocalTime 對應 **time **只包括時間
LocalDate 對應 **date ** 只包括日期
LocalDateTime 對應 **timestamp datetime **包括日期和時間
2、timestamp
timestamp 多個日期,如果可能爲空,則建議使用datetime(默認值建議設置爲)
0001-01-01 00:00:00,因爲0000-00-00 00:00:00mysql不能保存,而且會報錯,
普通字段不要設置爲timestamp,timestamp列必須有默認值,默認值可以爲“0000-00-00 00:00:00”,但不能爲null。如果我們在save實體的時候,沒有給相關timestamp設置值,那麼他就會自動由mysql將當前時間設置進去
3、註解使用
@Column(insertable = true,updatable = false)
@ApiModelProperty(hidden = true)
private Date cdate;
@UpdateTimestamp
@ApiModelProperty(hidden = true)
private Date udate;
3.4.2.3、小數設計 decimal
mysql中的decimal字段,聲明語法爲DECIMAL(M,D) D是小數點右側數位0-30,M最大精度數位,1-65
-
D:是小數部分的位數,若插入的值未指定小數部分或者小數部分不足D位則會自動補到D位小數,若插入的值小數部分超過了D爲則會發生截斷,截取前D位小數(四捨五入截取)。
-
M:是整數部分+小數部分=總長度,也即插入的數字整數部分不能超過M-D位,否則不能成功插入,會報超出範圍的錯誤。
規則:先保證小數點,再保證整數,
舉例說明,11615.23653234568這個數存你說的三個格式
decimal:11615
decimal(3):999
decimal(3,2):9.99
decimal(10,5)11615.23653
超出精度範圍的數會被強制進位並只顯示數據類型定義的格式
3.4.2.4、數字
1、mysql 類型有符號範圍和無符號範圍
帶符號和無符號,顧名思義,就是是否有正負之分:
比如8位的二進制,
如果帶符號,需要用1位表示符號(1表示負數,0表示正),剩下7位表示數據.
那麼表示範圍是-128—127(包括-0和+0).如果不帶符號,8位全部表示數據,
那麼表示範圍是 0–256最小負數二進制是1000 0000 → 減一: 0111 1111 取反: 1000 0000 = 128 所以應該爲 - 128
最大負數二進制是1111 1111 → 減一: 1111 1110 取反: 0000 0001 = 1 所以應該爲 - 1
如果帶符號,需要用1位表示符號(1表示負數,0表示正),剩下7位表示數據. 那麼表示範圍是-128—127(包括-0和+0).理解下了的話,就是無符號都是正數 ,所以主鍵自增Id我們一般都設計爲無符號的
`id` bigint(16) unsigned NOT NULL AUTO_INCREMENT,
2、各個數據類型的長度以及默認
整型的每一種都分無符號(unsigned)和有符號(signed)兩種類型,在默認情況整型變量都是有符號的類型
3、int(M) (用於提示開發者長度)
這個長度M
並不代表允許存儲的寬度,int(M)
,也就是int(3
)和int(11)
能夠存儲的數據是一樣的
只有聯合zerofill參數纔能有意義,否則int(3)
和int(11)
沒有任何區別。
- 不加zeroffill沒有區別
create table test_int
(
id int(3) unsigned not null,
uid int(11) unsigned not null,
uuid int unsigned not null
);
#插入數據
insert into test_int
values (4294967295, 4294967295, 4294967295);
#查詢數據,發現沒有什麼區別
select * from test_int;
+------------+------------+------------+
| id | uid | uuid |
+------------+------------+------------+
| 4294967295 | 4294967295 | 4294967295 |
+------------+------------+------------+
1 row in set (0.00 sec)
- 有了zeroffill 不足會自動補0
create table test_int1
(
id int(3) unsigned zerofill not null,
uid int(11) unsigned zerofill not null,
uuid int unsigned zerofill not null
);
#插入數據
insert into test_int1
values (4294967295, 4294967295, 4294967295);
insert into test_int1
values (1, 4294967295, 110000);
#查詢數據 發現前面的不足長度的右0了,當然不能使用idea測試,idea沒有顯示0
mysql> select * from test_int1;
+------------+-------------+------------+
| id | uid | uuid |
+------------+-------------+------------+
| 4294967295 | 04294967295 | 4294967295 |
| 001 | 04294967295 | 0000110000 |
+------------+-------------+------------+
2 rows in set (0.02 sec)
- 當使用zerofill 時,默認會自動加unsigned(無符號),zerofill默認爲int(10)
create table test_int2
(
id int(3) unsigned zerofill not null,
uid int zerofill not null,
uuid int unsigned zerofill not null
);
# 下面的不能執行成功,以爲無符號的都是正數
insert into test_int2
values (1, -4294967295, 110000);
insert into test_int2
values (1, 12345678, 110000);
mysql> select * from test_int2;
+-----+------------+------------+
| id | uid | uuid |
+-----+------------+------------+
| 001 | 0012345678 | 0000110000 |
+-----+------------+------------+
3.4.2.5、boolean
boolean值用1代表TRUE,0代表FALSE。boolean在mysql裏的類型爲tinyint(1)。mysql裏有四個常量:true,false,TRUE,FALSE分別代表1,0,1,0。
private Boolean loan;
tinyint(1) NOT NULL COMMENT '是否借款 true/false 1/0',
3.5、索引設計
- 普通索引前綴:idx_索引字段名,唯一索引前綴:uk_索引字段名
- 每個表必須顯示指定主鍵,主鍵儘量爲一個字段,且爲數字類型,避免使用字符串
- 主鍵儘量保持增長趨勢,建議使用id的生成器,而不使用數據庫自增(這個很難,我這裏還是自增的)
- 重要的SQL或調用頻率高的SQL
update/select/delete的where條件列字段都要添加索引
order by , group by, distinct的字段都要添加索引
- 組合索引創建時,把區分度(選擇性)高的字段放在前面;根據SQL的特性,調整組合索引的順序
- 禁止對索引列進行函數運算和數學運算
- 每個表的索引個數儘量少於5個,避免創建重複冗餘索引;每個組合索引儘量避免超過3個字段
**唯一索引添加之後,如果是邏輯刪除的,如果有可能恢復,記得還原id,沒有添加唯一索引,則按照正常的刪除即可 **
**不過一般情況下,我們這種下面這種沒有業務的是可以恢復的,如果是設計到用戶名,一般情況下我個人的理解是註銷的用戶,數據進行遷移,當前數據庫中沒有,然後就可以恢復了 **
1、沒有索引刪除的
/**
* 添加字典類型
*
* @return
*/
@Override
public void addDictType(DictionaryTypeDTO typeDTO, LoginUserDTO loginUserDTO) {
SysDictionaryTypeQuery query = new SysDictionaryTypeQuery();
query.setTypeKey(typeDTO.getTypeKey());
query.setStatus(StatusEnum.生效.code);
SysDictionaryType type = sysDictionaryTypeManager.findByQueryContion(query);
if (type != null) {
throw new BusinessException(ResponseEnum.字典類型已存在);
}
type = new SysDictionaryType();
type.setCreateUser(loginUserDTO.getUserId());
type.setCreateName(loginUserDTO.getRealName());
type.setTypeKey(typeDTO.getTypeKey());
type.setTypeDesc(typeDTO.getTypeDesc());
type.setStatus(StatusEnum.生效.code);
type.setUpdateUser(loginUserDTO.getUserId());
type.setUpdateName(loginUserDTO.getRealName());
sysDictionaryTypeManager.insertSelective(type);
}
/**
* 刪除字典類型
*/
@Override
public void deleteDictType(Long id, LoginUserDTO loginUserDTO) {
SysDictionaryType type = sysDictionaryTypeManager.findById(id);
if (type == null) {
throw new BusinessException(ResponseEnum.字典類型不存在);
}
type.setStatus(StatusEnum.廢棄.code);
type.setUpdateUser(loginUserDTO.getUserId());
type.setUpdateName(loginUserDTO.getRealName());
sysDictionaryTypeManager.updateSelective(type);
}
/**
* 修改字典類型
*/
@Override
public void updateDictType(DictionaryTypeDTO typeDTO, LoginUserDTO loginUserDTO) {
SysDictionaryTypeQuery query = new SysDictionaryTypeQuery();
query.setTypeKey(typeDTO.getTypeKey());
query.setStatus(StatusEnum.生效.code);
SysDictionaryType typeExist = sysDictionaryTypeManager.findByQueryContion(query);
//判斷是是否已經存在數據
if (typeExist != null && !typeExist.getId().equals(typeDTO.getId()) ) {
throw new BusinessException(ResponseEnum.字典類型已存在);
}
SysDictionaryType type = BeanUtils.dtoToDictionaryType(typeDTO);
type.setUpdateUser(loginUserDTO.getUserId());
type.setUpdateName(loginUserDTO.getRealName());
sysDictionaryTypeManager.updateSelective(type);
}
2、有索引刪除的代碼
/**
* 添加域名
*/
@Override
public DomainDTO addDomain(DomainDTO domainDTO, LoginUserDTO loginUserDTO) {
AlimamaInfoDTO alimamaInfoDTO = loginUserDTO.getAlimamaInfo();
SysDomainQuery domainQuery = new SysDomainQuery();
domainQuery.setRefAlimamaInfoId(alimamaInfoDTO.getAlimamaInfoId());
domainQuery.setType(domainDTO.getType());
SysDomain domain = sysDomainManager.findByQueryContion(domainQuery);
if (domain != null) {
if (domain.getStatus().equals(StatusEnum.生效.code)) {
throw new BusinessException(ResponseEnum.域名已存在);
} else {
domain.setRefAlimamaInfoId(alimamaInfoDTO.getAlimamaInfoId());
domain.setStatus(StatusEnum.生效.code);
domain.setValue(domainDTO.getValue());
domain.setCreateUser(loginUserDTO.getUserId());
domain.setCreateName(loginUserDTO.getRealName());
domain.setUpdateUser(loginUserDTO.getUserId());
domain.setUpdateName(loginUserDTO.getRealName());
sysDomainManager.updateSelective(domain);
}
} else {
domain = new SysDomain();
domain.setRefAlimamaInfoId(alimamaInfoDTO.getAlimamaInfoId());
domain.setType(domainDTO.getType());
domain.setValue(domainDTO.getValue());
domain.setStatus(StatusEnum.生效.code);
domain.setCreateUser(loginUserDTO.getUserId());
domain.setCreateName(loginUserDTO.getRealName());
domain.setUpdateUser(loginUserDTO.getUserId());
domain.setUpdateName(loginUserDTO.getRealName());
sysDomainManager.save(domain);
}
domainDTO.setDomainId(domain.getId());
return domainDTO;
}
/**
* 修改域名
*/
@Override
public void updateDomain(DomainDTO domainDTO, LoginUserDTO loginUserDTO) {
AlimamaInfoDTO alimamaInfoDTO = loginUserDTO.getAlimamaInfo();
SysDomainQuery domainQuery = new SysDomainQuery();
domainQuery.setRefAlimamaInfoId(alimamaInfoDTO.getAlimamaInfoId());
domainQuery.setType(domainDTO.getType());
SysDomain domain = sysDomainManager.findByQueryContion(domainQuery);
if(domain != null ){
if(!domain.getId().equals(domainDTO.getDomainId())){
if(domain.getStatus().equals(StatusEnum.生效.code)){
throw new BusinessException(ResponseEnum.域名已存在);
}else {//前提必須是status,否則會出問題
domain.setStatus(StatusEnum.生效.code);
domain.setValue(domainDTO.getValue());
domain.setUpdateUser(loginUserDTO.getUserId());
domain.setUpdateName(loginUserDTO.getRealName());
sysDomainManager.updateSelective(domain);
}
}else {
domain.setValue(domainDTO.getValue());
domain.setUpdateUser(loginUserDTO.getUserId());
domain.setUpdateName(loginUserDTO.getRealName());
sysDomainManager.updateSelective(domain);
}
}else {
throw new BusinessException(ResponseEnum.域名不存在);
}
}
/**
* 刪除域名
*/
@Override
public void deleteDomain(DomainDTO domainDTO, LoginUserDTO loginUserDTO) {
AlimamaInfoDTO alimamaInfoDTO = loginUserDTO.getAlimamaInfo();
SysDomain domain = sysDomainManager.findByIdAndAlimamaId(domainDTO.getDomainId(), alimamaInfoDTO.getAlimamaInfoId());
if(domain == null ){
throw new BusinessException(ResponseEnum.域名不存在);
}
if(domain.getStatus().equals(StatusEnum.廢棄.code)){
throw new BusinessException(ResponseEnum.重複操作);
}
domain.setStatus(StatusEnum.廢棄.code);
domain.setUpdateUser(loginUserDTO.getUserId());
domain.setUpdateName(loginUserDTO.getRealName());
sysDomainManager.updateSelective(domain);
}
3.5、sql規範
select o.createTime,
o.clickTime,
c.itemTitle,
o.itemId,
o.orderNo,
c.shopTitle,
o.estimateAmount,
o.payAmount,
c.commissionRatio,
o.orderStatus
from user_order o left join
coupon_taoke_data c on c.id = o.taokeId
where 1=1
4、接口文檔規範
4.1、多點
4.1.1、 查詢排重接口
接口詳情 | |
---|---|
地址 | http://www.baidu.com (正式環境) |
請求方式 | GET |
參數 | 是否必填 | 說明 |
---|---|---|
idfa | 是 | 廣告標識符,只支持單個查詢 |
source | 是 | 渠道來源,具體值在接入時再進行分配 |
返回結果 | 格式 | JSON |
---|---|---|
狀態碼 | 10000 | success(調用成功) |
10001 | param error(參數錯誤) | |
10002 | query failed(查詢失敗) | |
10010 | access prohibited(訪問拒絕) |
具體返回結果舉例:
1、查詢成功
{
"state": 10000,
"message": "success",
"data": {
"BD239708-2874-417C-8292-7E335A537FAD": 1 //已經存在
}
}
{
"state": 10000,
"message": "success",
"data": {
"BD239708-2874-417C-8292-7E335A537FAD": 0 //不存在
}
}
- 接口調用失敗
{
"state": 10010,
"message": "access prohibited",
"data": [
]
}
4.2、小米
4.2.1、角色列表查詢
說明
- 測試調用地址:/api/roles
- 調用方式:GET
請求參數
參數名稱 | 參數類型 | 參數長度 | 是否必需 | 說明 | 備註 |
---|---|---|---|---|---|
pageSize | 整數 | 否 | 每頁顯示數量 | 默認10 | |
pageNo | 整數 | 否 | 當前查看頁碼 | 默認1 | |
roleName | 字符串 | 64 | 否 | 角色名稱 | |
systemCode | 字符串 | 32 | 否 | 系統CODE | |
isPage | 布爾 | 4 | 否 | 是否分頁 | true/false |
請求報文樣例
{
"pageSize": 1,
"pageNo": 1,
"roleName": "",
"systemCode": "scf-manager",
"isPage": true
}
響應參數
參數名稱 | 參數類型 | 參數長度 | 是否必需 | 說明 | 備註 |
---|---|---|---|---|---|
msg | 字符串 | 255 | 是 | 返回結果 | |
total | 數字 | 否 | 總數 | ||
pageNo | 數字 | 否 | 頁數 | ||
totalPage | 數字 | 否 | 總頁數 | ||
pageSize | 數字 | 否 | 每頁數量 | ||
datas | Role數組 | 否 | 返回的數據信息 |
Role 數據結構
參數名稱 | 參數類型 | 參數長度 | 是否必需 | 說明 | 備註 |
---|---|---|---|---|---|
id | 數字 | 16 | 是 | id | |
roleName | 字符串 | 64 | 是 | 角色名稱 | |
systemCode | 字符串 | 64 | 是 | 系統CODE | |
status | 字符串 | 8 | 是 | 狀態 | |
desc | 字符串 | 255 | 否 | 描述 |
響應報文樣例
{
"msg": "角色列表查詢成功",
"total": 2,
"pageNo": 1,
"totalPage": 1,
"datas": [
{
"id": 1,
"roleName": "後臺管理員",
"systemCode": "scf-manager",
"status": "10"
},
{
"id": 4,
"roleName": "測試角色哦",
"systemCode": "scf-manager",
"status": "10",
"desc": "真的是測試"
}
],
"pageSize": 10
}
返回碼解析
返回碼 | 含義 | 備註 |
---|---|---|
200 | 成功 |