CloudJavaBackendSummaries
1、開發(fā)環(huán)境eclipse工程,引入jw倉庫的jw-base,3-base,3-common工程,引入cw倉庫的自己的工程,以及cw倉dto庫中31開頭工程,方Concurrent便調(diào)試(因為現(xiàn)在機(jī)器配置較低,建議刪除McAfee,停掉其他殺毒軟件,和防火墻)
啟動jvm,增加參數(shù),-Dlog4j.home=D:/sqlCmd,其中"log4j.home"配置自己機(jī)器路徑
2、每個功能操作必須對應(yīng)一個API,不能復(fù)用(比如,刪除單條,批量刪除),A層每個功能一定要拆開。
ps:增加了Controller方法操作所對應(yīng)系統(tǒng)功能code注解@OperFunCode(funCode="xxx-xxx-001")
3、除了包名、工程名、A層controller類的RequestMapping之外,不要用工程名命名任何東西。
ps:A層controller類的URL命名,參照如下/bs/3510010/GrpCrmProfile
刪除無用A層方法,及其后續(xù)P層、I層、D層代碼生成的方法?。?!
4、xdo.cfg文件在啟動時候生成,會根據(jù)當(dāng)前工程位置生成,因此不要上傳
4、清除暫時不用的代碼,只刪除代碼生成的,如果有在代碼生成后,修改有用信息的,均不刪除ps:要記得修改相應(yīng)的P層和A層的配置文件
5、entity實體生成后,根據(jù)業(yè)務(wù)需要修改其繼承父實現(xiàn)類、以及實現(xiàn)的接口類。目前提供的接口類匯總?cè)缦拢?br> BaseEntity:CreatedBy\CreatedByUid\CreatedByCD\CreatedDateBaseEntityWithUnit:CreatedBy\CreatedByUid\CreatedByCd\CreatedDate\CreatedUnitUid\CreatedUnitCdBusinessBaseEntity:CreatedBy\CreatedByUid\CreatedByCd\CreatedDate\ModifiedBy\ModifiedByUid\ModifiedByCd\ModifiedDateBusinessBaseEntityWithUnit:CreatedBy\CreatedByUid\CreatedByCd\CreatedDate\CreatedUnitUid\CreatedUnitCd\ModifiedBy\ModifiedByUid\ModifiedByCd\ModifiedDate\ModifiedUnitUid\ModifiedUnitCdChainUidEntity:ChainUidDomainObject:id\versionOucdEntity:UnitUidOuPoscdEntity:UnitUidPos
6、Junit至少覆蓋增刪改查,盡量提高I層和P層核心代碼的覆蓋率,另外,需把測試的json實例,寫個txt文件放在測試類同級目錄中。
編寫ConcurrentTest,測試一下并發(fā)情況【尤其是自己寫的核心業(yè)務(wù)】
7、公共靜態(tài)枚舉,統(tǒng)一使用com.jw.base.framework.core.Constant,目前包含如下:
狀態(tài)枚舉 0:無效 1:有效(默認(rèn))休眠標(biāo)識 0:正常(默認(rèn)) 1:休眠保密標(biāo)識 0:不保密(默認(rèn)) 1:保密性別 0:男(默認(rèn)) 1:女 2:未知默認(rèn)標(biāo)志 0:非默認(rèn) 1:默認(rèn)常用標(biāo)志 0:非常用(默認(rèn)) 1:常用聯(lián)系方式類型 0:電話,1:QQ,2:EMAIL,3:微信建立方式 1:酒店 2:網(wǎng)站 3:線下會員系統(tǒng) 4:CRS
null標(biāo)識:1:null 0:not null
寫枚舉時,枚舉類型,及其包含屬性必須編寫相應(yīng)注釋;根據(jù)需要設(shè)置default值(若有必要)或拋出異常;要有isEquals方法、以及getEnumConsNm【switch case用】
靜態(tài)變量定義要功能閉包。
8、小數(shù)數(shù)字entity統(tǒng)一用java.math.BigDecimal,整數(shù)統(tǒng)一用Integer,字符統(tǒng)一用String,日期統(tǒng)一用java.util.Date
ps:不要用com.ibm.icu.math.BigDecimal,要用java.math.BigDecimal
9、所有的相同類型的包裝類對象之間值的比較,全部使用 equals 方法比較。說明: 對于Integer var = ?在-128至127之間的賦值,Integer對象是在 IntegerCache.cache 產(chǎn)生,會復(fù)用已有對象,這個區(qū)間內(nèi)的 Integer 值可以直接使用==進(jìn)行 判斷,但是這個區(qū)間之外的所有數(shù)據(jù),都會在堆上產(chǎn)生,并不會復(fù)用已有對象,這是一個大坑, 推薦使用 equals 方法進(jìn)行判斷。
10、entity的@Column中nullable = false或true,要和數(shù)據(jù)庫保持一致(數(shù)據(jù)庫變更要通知開發(fā)人員)
11、注意變量名的拼寫(部分單詞中某些字符前后顛倒)
12、新增方法,I層需統(tǒng)一設(shè)置默認(rèn)值,不能依賴前端傳值(如是否休眠、是否有效、是否黑名單)
13、注意不要忽略A層validator的校驗,要寫全面完整
ps:ValidConstant,命名統(tǒng)一按照:VALID_ + 模塊英文名稱(表名) + 序號(2位:類型(1非空、2長度) + 3位:流水號)
例如:
/** 作廢日期為空 /public static final String VALID_GRPCRMCORPCONTRACT_001 = "VALID_GRPCRMCORPCONTRACT_001";/* 作廢原因為空 /public static final String VALID_GRPCRMCORPCONTRACT_002 = "VALID_GRPCRMCORPCONTRACT_002";/* 所屬單位不可為空 /public static final String VALID_GRPCRMCORPCONTRACT_003 = "VALID_GRPCRMCORPCONTRACT_003";/* 開始日期不可大于結(jié)束日期 /public static final String VALID_GRPCRMCORPCONTRACT_004 = "VALID_GRPCRMCORPCONTRACT_004";/* 合同類型不可為空 */public static final String VALID_GRPCRMCORPCONTRACT_005 = "VALID_GRPCRMCORPCONTRACT_005";
14、3105001工程中ValidConstant、LogConstant按照業(yè)務(wù)分包放
15、對象復(fù)制不使用BeanUtil.copyPropertiesExceptNull,統(tǒng)一使用entity中copyentity方法
16、dto中不再用現(xiàn)成的entity做定義,用新定義的dto(結(jié)合頁面元素),新增模塊package
dto的子包,包名要全部小寫,不要已大寫字母開頭命名
17、所有sql語句select列表,默認(rèn)order by 自增主鍵
18、帶條件和排序查詢,統(tǒng)一使用增強(qiáng)的enhanceQueryListByProerties方法,暫不允許使用批量更新方法,批量更新業(yè)務(wù)實現(xiàn),需先enhanceGet,再逐條保存
19、日期查詢sql統(tǒng)一使用如下方式:(禁止在where條件屬性中使用函數(shù))
select cast(字段 as timestamp) from where cast('2015-10-12 12:02:45' as timestamp) <= 字段;統(tǒng)一使用DateUtil.DATE_TIME_FORMAT(yyyy-MM-dd HH:mm:ss,大寫HH代表24小時制)
20、悲觀鎖統(tǒng)一使用lock類鎖表,各個業(yè)務(wù)實現(xiàn)自己的鎖表類
Map<String,String> paramMap = new HashMap<String, String>();paramMap.put(BsSysUser.class.getName(), bsSysUserRequestFormDto.getSubmitData().getId().toString());lockProvider.lock(paramMap); 鎖表用法
21、改子表,保存后,更新主表version表
22、DTO中每條業(yè)務(wù)記錄都需帶著“操作標(biāo)示”(統(tǒng)一放在entity的subdto中)
23、要優(yōu)先使用目前提供的工具類
StringUtil:isnull、isnotnull、spilt
集合工具統(tǒng)一用import org.apache.commons.collections4.CollectionUtils;
24、新增修改需為兩個方法,不能復(fù)用一個
25、統(tǒng)一用CodeBean.java返回給前端key + code + name
26、checkbox類似數(shù)組存儲,不用逗號隔開,用統(tǒng)一的子表(多項表)
27、ResponseFormDto返回結(jié)果數(shù)據(jù)中統(tǒng)一叫resultData
28、要使spring中校驗生效,需增加調(diào)用doValidator
29、A層@OperSysType注解,暫不用打,不用注解該方法調(diào)用哪個數(shù)據(jù)源;【考慮到以后校驗,可能以后會打,統(tǒng)一再重構(gòu)】
S層增加@OperSysType注解,說明本方法提供對應(yīng)數(shù)據(jù)源類型;S層@OperSysType注解,增加屬性isSlave,默認(rèn)false,切換為主庫,如果為true,切換從庫PCM標(biāo)示切換到PCM庫,HPT標(biāo)示根據(jù)操作酒店對應(yīng)數(shù)據(jù)庫切換、GRP標(biāo)示根據(jù)當(dāng)前集團(tuán)對應(yīng)數(shù)據(jù)庫切換ps:目前dto中專了sourceNm,以sourceNm切,優(yōu)先級最高;第二優(yōu)先級為S層上的注解,第三優(yōu)先級為A層注解(A層注解咱不用打)
30、A層@DrptAPI注解中增加屬性listParam,dtoResponse,如下:
@DrptAPI(infnm="列表獲取數(shù)據(jù)",infdrpt="列表獲取數(shù)據(jù)",dtoParam="BsSysUserQueryDto",dtoResponse="com.jw.common.framework.m0002.f001.dto.DataTableDto",listParam="id,staff_id,staff_code,user_name,user_name_en,login_account,modified_date,pwd,created_date,language_type")@DrptAPI(infnm="根據(jù)ID獲取詳細(xì)信息",infdrpt="根據(jù)ID獲取詳細(xì)信息",dtoParam="IdRequestDto",dtoResponse="com.jw.hms.example.m31xx.f001.dto.BsSysUserResponseFormDto")
31、@DrptAPI注解必須書寫全參數(shù)@DrptAPI(infnm="列表獲取數(shù)據(jù)",infdrpt="列表獲取數(shù)據(jù)",dtoParam="GrpRolePermissionQueryDto",dtoResponse="com.jw.common.framework.m0002.f001.dto.DataTableDto",listParam="...")
32、共同參數(shù)統(tǒng)一調(diào)用CmmParameterProvider中的如下方法
獲取數(shù)據(jù)字典List(過濾后的字典集合)public DictionaryResponseDto querylistDictionaryList(DictionaryQueryDto dictQueryDto) throws Exception獲取數(shù)據(jù)字典key對應(yīng)的描述(單條描述)public DictionaryResponseDto querylistDictDescription(DictionaryQueryDto dictQueryDto) throws Exception
P層遠(yuǎn)程調(diào)用P層,所有調(diào)用查參數(shù)統(tǒng)一用:字典表獲取 PlfCmmParameterServiceImpl.queryListToDictionary
ps:PSM庫沒有參數(shù)表
33、列表查詢方法(P層)統(tǒng)一使用queryList開頭、查復(fù)雜記錄方法(P層)統(tǒng)一使用queryIds開頭、查報表方法(P層)統(tǒng)一使用queryReport開頭。
34、查詢sql,當(dāng)前操作酒店條件,統(tǒng)一使用ShareSession.getSessionUnitUid_EXCU。
35、sql的like查詢,要根據(jù)業(yè)務(wù)需要,增加前后百分號;in用List,order by多個用List;
36、PMS和餐飲的entity需implements OucdEntity,字段可以任意起名,但需在成員變量上打上如下注解@FieldUnitcd(systyp=Constant.SYS_TYP_PMS),并實現(xiàn)getUnitUidOu(),和setUnitUidOu方法。A層給RequestCommonDto中的UnitUid賦值,P層新增要增加@CheckUnitcd(systyp=Constant.SYS_TYP_PMS)。
37、CRM和GROUP中,前臺需要選擇酒店,錄入新增記錄場景,其對應(yīng)entity需implements PoscdEntity,字段可以任意起名,但需在成員變量上打上如下注解@FieldUnitcd(systyp=Constant.SYS_TYP_GRP),并實現(xiàn)getUnitUidPos(),和setUnitUidPos方法
系統(tǒng)會只在保存時,給其賦值,賦值來源ShareSession.getSessionUnitUid_EXCU(),該值由A層初始化賦值中獲?。≧equestCommonDto中的UnitUid)--dto.getRequestCommonDto().setUnitUid("xxxxxxxxx");P層之前的AuthorityFilter,會將RequestCommonDto中的UnitUid,set到threadlocal中。統(tǒng)一的CheckUnitcdAdvice攔截進(jìn)行同登錄人能操作酒店進(jìn)行校驗P層僅僅新增要增加@CheckUnitcd(systyp=Constant.SYS_TYP_GRP
38、查詢列表頁面,傳入查詢條件的dto,不建議是個通用的map,并且要做必要的spring @Valid
39、用@Deprecated注釋的程序元素,不鼓勵程序員使用這樣的元素,通常是因為它很危險或存在更好的選擇。在使用不被贊成的程序元素或在不被贊成的代碼中執(zhí)行重寫時,編譯器會發(fā)出警告。
java.lang.Deprectated是J2SE 5.0中標(biāo)準(zhǔn)的Annotation型態(tài)之一,它對編譯器說明某個方法已經(jīng)不建議使用,如果有人試圖使用或重新定義該方法,必須提出警示訊息。
40、檢查建的表,應(yīng)該沒有默認(rèn)值,并且可以為空字段應(yīng)為DEFAULT NULL
41、前端聯(lián)調(diào)機(jī)器192.168.18.35(Administrator\123456),把在本機(jī)開發(fā)環(huán)境maven install的R和RA工程的target下的war包放到相應(yīng)webapp下重啟服務(wù)
ps:35部署記得清緩存,D:\java\apache-tomcat-8.5.4\work
ps:R和RA工程必須,clean + install,這樣其.war包才是最新的
42、代碼注釋原則:凡同示例代碼不同邏輯,均需編寫必要代碼注釋做相應(yīng)說明(方法說明,應(yīng)寫明業(yè)務(wù),不能僅僅些表名英文)
43、無用的老代碼,不要保留,建議全部刪掉(git上會有版本,可以找回),尤其注意功能調(diào)整變化造成的已無用的代碼
44、@DrptController、@DrptAPI、@DrptField等接口自定義系統(tǒng)注解,要增加必要業(yè)務(wù)說明
ps:dto注解DrptField增加屬性nullable、length、precision,并且其中fieldnm放字段英文名
45、A層@Log注解中的logCD,應(yīng)用對應(yīng)靜態(tài)類靜態(tài)變量進(jìn)行賦值
46、A層Controller中try\catch捕獲異常處理類中增加請求dto參數(shù),供log4j日志記錄用(bug調(diào)試用)
ExceptionTools.handleException(exception, dto, this.getRequest(), this.getResponse());
47、>微服務(wù)層必須注解事務(wù)@Transactional,在get,is等讀操作微服務(wù)中必須@Transactional(readOnly = true),即從這一點(diǎn)設(shè)置的時間點(diǎn)開始(時間點(diǎn)a)到這個事務(wù)結(jié)束的過程中,其他事務(wù)所提交的數(shù)據(jù),該事務(wù)將看不見(查詢中不會出現(xiàn)別人在時間點(diǎn)a之后提交的數(shù)據(jù))(如果你一次執(zhí)行單條查詢語句,則沒有必要啟用事務(wù)支持,數(shù)據(jù)庫默認(rèn)支持SQL執(zhí)行期間的讀一致性)
如果你一次執(zhí)行多條查詢語句,例如統(tǒng)計查詢,報表查詢,在這種場景下,多條查詢SQL必須保證整體的讀一致性,否則,在前條SQL查詢之后,后條SQL查詢之前,數(shù)據(jù)被其他用戶改變,則該次整體的統(tǒng)計查詢將會出現(xiàn)讀數(shù)據(jù)不一致的狀態(tài),此時,應(yīng)該啟用事務(wù)支持。>@Transactional 只能被應(yīng)用到public方法上, 對于其它非public的方法,如果標(biāo)記了@Transactional也不會報錯,但方法沒有事務(wù)功能。>在具體的類(或類的方法)上使用 @Transactional 注解,而不要使用在類所要實現(xiàn)的任何接口上。>用 spring 事務(wù)管理器,由spring來負(fù)責(zé)數(shù)據(jù)庫的打開,提交,回滾.默認(rèn)遇到運(yùn)行期異常(throw new RuntimeException("注釋");)會回滾,即遇到不受檢查(unchecked)的異常時回滾;而遇到需要捕獲的異常(throw new Exception("注釋");)不會回滾,即遇到受檢查的異常(就是非運(yùn)行時拋出的異常,編譯器會檢查到的異常叫受檢查例外或說受檢查異常)時,需我們指定方式來讓事務(wù)回滾 要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常})
48、變量命名,不要以_開頭
49、盡量不要在字段中存入null值,null值可用-1替代,在oracle中,null跟''是一樣的,oracle都會理解為是null值;在mysql、PostgreSQL中,null與''是不相同的,''會插入一個空字符串
50、@SuppressWarnings。該批注的作用是給編譯器一條指令,告訴它對被批注的代碼元素內(nèi)部的某些警告保持靜默。如:@SuppressWarnings("deprecation")表示不顯示使用了不贊成使用的類或方法時的警告。
51、注意各個業(yè)務(wù)RequestFormDto中g(shù)etLogDiffer,要和界面保持一致,以便后面頁面還原使用(dto中g(shù)etLogDiffer方法不能都給注釋掉,要按照頁面元素進(jìn)行相應(yīng)編碼)
52、dto中g(shù)etLogDiffer方法,不要用@JsonIgnore注解,或@JsonIgnoreProperties,這兩個是jackson來處理json串轉(zhuǎn)換的問題的(有些時候我們經(jīng)常有忽略某些屬性的情況),不是FastJson的,改用@JSONField(serialize=false),或用FastJson提供的各種filter機(jī)制來實現(xiàn)
53、不要用JsonUtil,改用FastJsonUtil,否則轉(zhuǎn)換整形會有小數(shù)點(diǎn)(Gson默認(rèn)將int和long型數(shù)據(jù)轉(zhuǎn)換為double)
ps:fastjson解析json時報錯default constructor not found. class.
類需要有一個空的構(gòu)造函數(shù)public JwMessage() {super();}
54、getLogDiffer中setPfieldvalue值,如果是Integet不要加.toString(),統(tǒng)一使用StringUtil.nullToEmpty方法處理,如下:改為:ib.setPfieldvalue(StringUtil.nullToEmpty(getOriginData().getSeq()));大家要根據(jù)代碼生成,根據(jù)不同類型,做相應(yīng)處理
55、代碼注釋建議使用javadoc的/** xxxxxxxxx /,不用/ xxxxxxxxx / a、單選注釋:符號是:// b、塊注釋: 符號是: / / 可以跨多行 c、javadoc注釋: 符號是: / */ 可以跨多行, 生成javadoc時,這樣的注釋會被生成標(biāo)準(zhǔn)的javaapi注釋。
56、redis調(diào)用(批量、同時操作多個key值的)方法如mset、msetnx、mget、rename、renamenx、smove、sinter、sdiff、sunion、zunionstore、時,key值前面拼接上Constant.KEY_SLOTkeySlot算法中,如果key包含{},就會使用第一個{}內(nèi)部的字符串作為hash key,這樣就可以保證擁有同樣{}內(nèi)部字符串的key就會擁有相同slot。
57、G是帶泛型的,非G是非泛型的產(chǎn)品baseDAO實現(xiàn):BaseProductJdbcDaoImpl.java(BaseProductJdbcDaoImpl 增加三個with語句拼接方法)BaseProductProvider原子服務(wù)接口:BaseProductGProvider.javaBaseProductProvider原子服務(wù)接口:BaseProductProvider.javaBaseProductService微服務(wù)接口:BaseProductGService.javaBaseProductService微服務(wù)接口:BaseProductService.javaBaseProductProviderImpl原子服務(wù)實現(xiàn):BaseProductGProviderImpl.javaBaseProductProviderImpl原子服務(wù)實現(xiàn):BaseProductProviderImpl.javaBaseProductServiceImpl微服務(wù)實現(xiàn):BaseProductGServiceImpl.javaBaseProductServiceImpl微服務(wù)實現(xiàn):BaseProductServiceImpl.java
58、D層DaoQueryMysqlImpl、DaoQueryOracleImpl、DaoQuerySqlServerImpl先刪掉,生成的不放到工程代碼中
59、修改為統(tǒng)一只用日志框架SLF4J中的API【門面模式的日志框架】,有利于維護(hù)各個類的日志處理方式
import org.slf4j.Logger;import org.slf4j.LoggerFactory;private static final Logger logger = LoggerFactory.getLogger(Abc.class);
60、對trace、debug、info級別的日志輸出,必須使用條件輸出形式或者使用占位符方式if(logger.isDebugEnabled()){logger.debug("Processing trade with id:"+id+",symbol:"+symbol);\條件輸出}或者logger.debug("Processing trade with id:{},symbol:{}", id, symbol);\占位符方式
61、log4j按照日志級別輸出(分包記錄,后續(xù)完善)${log4j.home}/log4j_31xx001R_info.log【請求的header信息 + requestbody信息 + tranceid鏈信息】${log4j.home}/log4j_31xx001R_error.log【controller捕獲返回給給前端的異常dot】另外,tomcat控制臺信息,查看apache-tomcat-8.5.4\logs下的catalina.*.log
62、金額、房間數(shù)、人數(shù)等放置默認(rèn)值"0",字符串的根據(jù)業(yè)務(wù)需要放置默認(rèn)值,不建議有值為null
63、接口自定義系統(tǒng),包名注意,按照變化及時修改(dto)
64、dto和entity中,成員變量,均要運(yùn)用駝峰命名法(處敏感信息外)
entity中Integer類型字段打上@Size注解 保存時會報 HV000030: No validator could be found for type: java.lang.Integer.這個錯誤,應(yīng)該打@Max注解ps:一定注意String改為Integer時,要進(jìn)行相應(yīng)修改
65、所有GRP的表,應(yīng)該createtype字段,統(tǒng)一在A層放值ps:檢查所有表,createtype字段是否建全
66、修改字段名、字段類型、字段長度等,建議全部工程Search一下,該字段名 + 字段名的駝峰命名,確認(rèn)修改完整ps:如果涉及框架,要和劉博文溝通
67、單表新增不用鎖記錄,修改鎖當(dāng)前記錄;主子表情況,子表新增或修改鎖主表,并且修改時鎖自身;
68、在做功能前查看一下,是否已有人對應(yīng)相應(yīng)業(yè)務(wù)表做了代碼生成(Ctrl+Shift+R)
69、發(fā)番統(tǒng)一獲取,在P層注入I層發(fā)番類@Resourceprivate CmmComIdProvider cmmComIdProvider;cmmComIdProvider.getNumberId(GrpCrmCorpConst.CONTRACT_NO_KZNO)其中GrpCrmCorpConst.CONTRACT_NO_KZNO,需在各自庫中cmm_number表中,配置相應(yīng)發(fā)番規(guī)則
按照該《系統(tǒng)配置.xlsx》中發(fā)番配置,統(tǒng)一從com.jw.base.framework.core.Constant獲取發(fā)番類型
70、注入類,用@Resource,默認(rèn)安照名稱進(jìn)行裝配,名稱可以通過name屬性進(jìn)行指定,變量名要與類名一致@Resourceprivate HptRsvRateCalendarService hptRsvRateCalendarService;
71、測試要記得清臟數(shù)據(jù),如grp庫中createtyp中值為null,以前代碼問題造成的臟數(shù)據(jù)等
ps:貨幣統(tǒng)一用NUMERIC(16,4)
72、p層,RPC遠(yuǎn)程調(diào)用其他P層(統(tǒng)一在R層配置,方便管理)
P層調(diào)用時要把commondto中屬性值賦值上,如sessionkey、token等
ps:
P層配置服務(wù)提供方接口
A層配置服務(wù)消費(fèi)方接口【需要把RA工程的Eclipse中Jetty配置勾去“Compile Scope”,不影響開發(fā)的熱部署】
若RA工程的Eclipse中Jetty配置勾上“Compile Scope”,把A層工程target勾掉,并且把“spring-web”的jar包勾掉勾上的JRebel將監(jiān)聽【monitored】classes文件的changes
73、對于大表,不要用hibernate查詢,用sql。Hibernate: select ... from XXX order by id desc limit ? offset ? 手拼SQL不用參數(shù)化: select ... from XXX order by id desc limit 30 offset 0 select里如果用參數(shù),將極慢,如果直接拼到sql里,就快很多,差好幾個數(shù)量級
74、jdbc查詢sql,只能用于列表、報表,不允許使用在事務(wù)中(jdbc同hibernate不是一個鏈接)新提供了下面幾個org.hibernate.Session的方法public List<Map<?, ?>> findBySqlToMap(final String sql);【現(xiàn)有query方法做復(fù)雜查詢不方便時使用】public List<Map<?,?>> findBySqlToMap(final String sql, final Object[] params);【現(xiàn)有query方法做復(fù)雜查詢不方便時使用,也適用于有返回結(jié)果集的存儲過程或查詢語句】public Integer executeSql(final String sql);【只在select insert等特殊情況下用,此方法不能用于執(zhí)行存儲過程】
拼寫select insert語句執(zhí)行,也要參數(shù)化?。。?!
75、queryListByProerties 修改比較值操作 List list = grpCrmProfileProvider.queryListByProerties(new String[] { "createdDate" }, new Object[] { new HsqlCompare(Constant.ENUM_COMPARE.Larger, Calendar.getInstance().getTime()) });傳入 new HsqlCompare(Constant.ENUM_COMPARE.Larger, Calendar.getInstance().getTime()) 對象
76、一個事務(wù)中,如業(yè)務(wù)需要一次性創(chuàng)建多對象,建議executeSql執(zhí)行select insert語句,提高速度
77、使用各自賬戶進(jìn)行測試
用戶名/密碼:admin/1 (單位代碼UNIT001,語種:中文){"chainCd":"CHAIIN001","unitCd":"UNIT001","systemInfo":"YWRtaW4sMQ==","dbType":"0","sysId":"GRP"}
用戶名/密碼:1/1 (單位代碼UNIT001,語種:中文){"chainCd":"CHAIIN001","unitCd":"UNIT001","systemInfo":"MSwx","dbType":"0","sysId":"GRP"}
用戶名/密碼:2/1 (單位代碼UNIT001,語種:中文){"chainCd":"CHAIIN001","unitCd":"UNIT001","systemInfo":"Miwx","dbType":"0","sysId":"GRP"}
用戶名/密碼:4/1 (單位代碼UNIT002,語種:英文){"chainCd":"CHAIIN001","unitCd":"UNIT002","systemInfo":"NCwx","dbType":"0","sysId":"GRP"}
用戶名/密碼:5/1 (單位代碼UNIT002,語種:英文){"chainCd":"CHAIIN001","unitCd":"UNIT002","systemInfo":"NSwx","dbType":"0","sysId":"GRP"}
用戶名/密碼:6/1 (單位代碼UNIT002,語種:英文){"chainCd":"CHAIIN001","unitCd":"UNIT002","systemInfo":"Niwx","dbType":"0","sysId":"GRP"}
用戶名/密碼:7/1 (單位代碼UNIT003,語種:日文){"chainCd":"CHAIIN001","unitCd":"UNIT003","systemInfo":"Nywx","dbType":"0","sysId":"GRP"}
用戶名/密碼:8/1 (單位代碼UNIT003,語種:日文){"chainCd":"CHAIIN001","unitCd":"UNIT003","systemInfo":"OCwx","dbType":"0","sysId":"GRP"}
用戶名/密碼:9/1 (單位代碼UNIT003,語種:日文){"chainCd":"CHAIIN001","unitCd":"UNIT003","systemInfo":"OSwx","dbType":"0","sysId":"GRP"}
78、字典表如下
共同參數(shù):plf_cmm_parameter, plf_cmm_param_multilang(PCM、GRP、PMS)集團(tuán)參數(shù):cmm_base_param, cmm_base_param_multilang(PCM、GRP、PMS)酒店參數(shù):cmm_unit_param, cmm_unit_param_multilang(PCM、GRP、PMS)pos參數(shù): cmm_pos_param, cmm_pos_param_multilang(GRP、PMS)系統(tǒng)參數(shù):plf_cmm_config, plf_cmm_config_multilang(PCM、GRP、PMS)
配合朱倩梳理現(xiàn)有參數(shù)表數(shù)據(jù),梳理后會刪掉pcm庫和pms庫中10張參數(shù)表,后續(xù)需要增加參數(shù)數(shù)據(jù),統(tǒng)一找朱倩,需要增加類型(修改EXCEL),統(tǒng)一找韓松
按照《業(yè)務(wù)-參數(shù)定義.xlsx》梳理共同參數(shù)、酒店集團(tuán)參數(shù)等,調(diào)用參數(shù)類型統(tǒng)一修改為com.jw.base.framework.core.Constant中靜態(tài)變量ps:有之前部分共同參數(shù)修改為了參數(shù)、酒店集團(tuán)參數(shù)
79、CountDownLatch和CyclicBarrier線程同步的輔助工具,通過它可以做到使一條線程一直阻塞等待,直到其他線程完成其所處理的任務(wù)。CountDownLatch:用給定的計數(shù)初始化CountDownLath。調(diào)用countDown()方法計數(shù)減 1,在計數(shù)被減到 0之前,調(diào)用await方法會一直阻塞。減為 0之后,則會迅速釋放所有阻塞等待的線程,并且調(diào)用await操作會立即返回。CyclicBarrier:用計數(shù) N 初始化CyclicBarrier, 每調(diào)用一次await,線程阻塞,并且計數(shù)+1(計數(shù)起始是0),當(dāng)計數(shù)增長到指定計數(shù)N時,所有阻塞線程會被喚醒。繼續(xù)調(diào)用await也將迅速返回。
CountDownLatch用法之一:初始化CountDownLatch startSwitch = new CountDownLatch(1),以及CountDownLatch stopSwitch = new CountDownLatch(groupNum),先在線程池中初始化要并行的線程,每個線程中startSwitch開關(guān)先阻塞,在主線程中startSwitch.countDown()(計數(shù)-1),開始所有線程池中線程(解鎖獲得資源),同時主線程stopSwitch.await()阻塞,每個線程池中線程完成各自工作后stopSwitch.countDown()計數(shù)器減一,最后一個子線程完成工作后,喚醒主線程繼續(xù)。CyclicBarrier用法之一:初始化CyclicBarrier startSwitch = new CyclicBarrier(groupNum + 1),以及CyclicBarrier stopSwitch = new CyclicBarrier(groupNum),先在線程池中初始化要并行的線程,每個線程中startSwitch開關(guān)先阻塞(計數(shù)+1),在主線程中startSwitch.await()(計數(shù)+1),開始所有線程池中線程(當(dāng)計數(shù)增長到指定計數(shù)N時,所有阻塞線程會被喚醒),同時主線程service.shutdown(),在等待所有線程池中線程運(yùn)行完成,每個線程池中線程完成各自工作后stopSwitch.await()阻塞(計數(shù)+1),最后一個子線程完成工作后,主線程service.shutdown()完成并繼續(xù)。
80、線程池創(chuàng)建至少是通過靜態(tài)成員變量方式,不能是局部成員變量,后續(xù)修改為通過ThreadPoolExecutor方式
81、框架提供了攔截器進(jìn)行統(tǒng)一的限流 + 系統(tǒng)參數(shù)cfg.properties定時重新加載【訪問路徑計數(shù)是否大于限流值】
82、線程不安全的(和公共資源相關(guān)的),getUUID,獲取賬號,主鍵,獲得密鑰key,減庫存,搶優(yōu)惠券(用到某些數(shù)據(jù)結(jié)構(gòu),隊列或集合,參照如下結(jié)構(gòu)使用)(方法內(nèi)部定義的變量,不涉及線程安全,通常設(shè)計線程安全的變量一般都是成員變量)
HashMap非線程安全,改用java.util.concurrent.concurrentHashMap,或HashTable(HashTable不允許插入空值,HashMap允許),或者使用Collection工具類將集合包裝成線程安全的集合(pubilc static Map m = Collections.synchronizedMap(new HashMap()),此方法在并發(fā)級別不高是也夠用)
ArrayList非線程安全,雖然Vector是線程安全的,但Vector效率低,因此不建議使用(ArrayList和Vector都是使用“數(shù)組”作為其內(nèi)部實現(xiàn))可以考慮使用java.util.concurrent.CopyOnWriteArrayList(在讀多寫少的場景,次List性能非常好,對于它來說,讀取是完全不用加鎖的,寫入也不會阻塞讀取操作,只有寫入和寫入之間需要進(jìn)行同步等待),所謂CopyOnWrite就是在寫入操作時,進(jìn)行一次自我復(fù)制,即,當(dāng)List需要修改時,并不修改原有內(nèi)容(這對于保證當(dāng)前在讀線程的數(shù)據(jù)一致性非常重要),而是對原有數(shù)據(jù)進(jìn)行一次復(fù)制,將修改內(nèi)容寫入副本中,寫完后,再將修改完的副本替換原有數(shù)據(jù),這樣就保證寫操作不會影響讀了。
LinkedList非線程安全,使用鏈表的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)了List,可以使用Collections.synchronizedList()方法來包裝任意List(pubilc static List<String> l = Collections.synchronizedList(new LinkedList<String>())
StringBuffer是線程安全,StringBuilder是線程不安全的(這句話是不對的),StringBuffer只是說他的方法是排他的,也就是說,StringBuffer每一次append是線程安全的,但眾多次append的組合并不是線程安全的(在方法上加synchronized關(guān)鍵字),如果換成StringBuilder,甚至有可能append一半,就讓位其他線程
83、JDK并發(fā)包中,有一個atomic包,里面實現(xiàn)了一些直接使用CAS操作的線程安全的類型無鎖(原子)線程安全整數(shù):AtomicInteger,對于整數(shù)的封裝無鎖(原子)數(shù)組:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray無鎖(原子)的對象引用:AtomicReference,對于普通對象的引用帶有時間戳的對象引用:AtomicStampReference讓普通變量也能享受原子操作:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
84、之后我們前后端接口的敲定遵循契約精神,按如下流程進(jìn)行:1、數(shù)據(jù)庫設(shè)計完成后韓松、趙翔、博文、后端開發(fā)一起商量所需接口個數(shù)和每個接口需要實現(xiàn)的功能。2、后端項目經(jīng)理按照接口數(shù)和工作量分配負(fù)責(zé)開發(fā)每個接口的后端人員。3、后端相關(guān)人員進(jìn)行所負(fù)責(zé)接口的定義,包括參數(shù)說明及返回值說明。4、定義好的接口給博文審核,確保技術(shù)實現(xiàn)上無問題且實現(xiàn)方式最優(yōu)。5、博文審核過的接口給趙翔審核,確??梢酝耆珜崿F(xiàn)前端業(yè)務(wù)需求。6、審核通過的接口進(jìn)入接口定義系統(tǒng),包括輸入輸出結(jié)構(gòu)定義,此時形成契約。7、后端人員進(jìn)行接口開發(fā)及單元測試,注意接口健壯性、容錯(如數(shù)據(jù)庫字段變更不應(yīng)影響契約)和方便性(如允許傳入最少的調(diào)入?yún)?shù))。8、開發(fā)完成的接口提交測試環(huán)境,崔俊依據(jù)接口系統(tǒng)生成自動化測試腳本,并進(jìn)行返回值驗證,判斷返回鍵值是否符合契約。9、測試通過的接口通知前端進(jìn)行聯(lián)調(diào),否則退回后端進(jìn)行修改,使之遵守契約。
85、"push git 411"問題,如下:error: RPC failed; result=22, HTTP code = 411fatal: The remote end hung up unexpectedlyps:以上發(fā)生在push命令中,有可能是push的文件過大導(dǎo)致解決方法:修改倉庫下.git/config 文件>windows:在 .git/config 文件中加入[http]postBuffer = 524288000>Linux:git config http.postBuffer 524288000
86、配置統(tǒng)一放置,便于自動化部署,所有配置信息均匯總于cfg.properties,以后修改為從xdiamond中獲取
增加com.jw.base.framework.core.config.PropertyConfigurer,通用獲取cfg.properties屬性,以后修改為從xdiamond中獲取
app-include.xml中propertyConfigurer,從org.springframework.beans.factory.config.PropertyPlaceholderConfigurer修改為自定義子類PropertyConfigurer
但realpath還是從自定義servlet:StartUpServlet中獲取,web.xml中明確定義log4jConfigLocation【spring】
cfg.properties中增加
log4j.home=D:/sqlCmd【log4j日志路徑】log4j.filename=3171001R【各個工程名】不用在工程啟動參數(shù)中增加-Dlog4j.home=D:/sqlCmd
重寫org.springframework.web.util.Log4jConfigListener
<listener><listener-class>com.jw.base.framework.core.listener.Log4jConfigListener</listener-class></listener>其中增加System.setProperty("log4j.home",(String)PropertyConfigurer.getContextProperty("log4j.home"));System.setProperty("log4j.filename",(String)PropertyConfigurer.getContextProperty("log4j.filename"));
MQ配置,Redis配置文件放到相應(yīng)base工程中
87、寫枚舉時,枚舉類型,及其包含屬性必須編寫相應(yīng)注釋;根據(jù)需要設(shè)置default值(若有必要)或拋出異常;要有isEquals方法、以及getEnumConsNm
88、部署tomcat注意配置,一種是指向工程方式【webapps】下不要放置工程war包<Context path="/3171001R" docBase="D:\java\3171001R-1.0.0-SNAPSHOT" debug="0" crossContext="true" /><Context path="/" docBase="D:\java\3160001RA-1.0.0-SNAPSHOT" debug="0" crossContext="true" />
89、druid配置initialSize配置合適就行,但maxActive盡量設(shè)置大些// 防止創(chuàng)建超過maxActive數(shù)量的連接if (activeCount + poolingCount >= maxActive) {empty.await();continue;}連接太多了的時候,在empty條件上等待,就是等空了再運(yùn)行
90、在工程中增加禁止爬蟲爬取的robots.txt配置文件
91、上線一定要根據(jù)需要設(shè)置druid的maxActive,最大并發(fā)數(shù)
注意調(diào)整好服務(wù)器時間~~
92、dubbo異步調(diào)用定義如下<dubbo:reference id="bsSysUserServiceAsync" interface="com.jw.hms.example.m31xx.f001.microservice.BsSysUserService" timeout="50000" retries="0" check="false" registry="localzk" async="true" sent="true" />A層或P層充當(dāng)消費(fèi)者調(diào)用DataTableDto data2 = bsSysUserService.queryListAsync(bsSysUserQueryDto, page);System.err.println("立即返回為null:"+data2); //拿到調(diào)用的Future引用,當(dāng)結(jié)果返回后,會被通知和設(shè)置到此Future。 Future<DataTableDto> pFuture = RpcContext.getContext().getFuture(); //如果data已返回,直接拿到返回值,否則線程wait,等待data返回后,線程會被notify喚醒。 data2 = pFuture.get();System.out.println("返回異步值"+data2);
PS:注意dubbo異步調(diào)用傳遞性問題
ServiceA異步調(diào)ServiceB,ServiceB再同步調(diào)ServiceC,此時ServiceC會當(dāng)異步調(diào)用。但是,如果后續(xù)還有同步調(diào)用,則因B調(diào)C為同步,則就會正常同步調(diào)用了。
93、多線程并發(fā),建議使用實現(xiàn)Callable接口,與Future + 線程池結(jié)合使用private static ExecutorService service1 = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(Constant.BLOCKING_QUEUE_NUMBER));Callable<DataTableDto> task1 = () -> {try {System.out.println("excute=================task1");Thread.sleep(2000);return bsSysUserServiceRef.queryListAsync(bsSysUserDto, page);} catch (Exception e) {throw e;} finally {}};Future<DataTableDto> result1 = service1.submit(task1);System.out.println("result1================="+result1.get());
94、框架中提供的NamedParameterJdbcTemplate檢索查詢,有兩個,一個是帶分頁的,一個是不帶分頁的,帶分頁的會查詢兩次【其中有一次是統(tǒng)計總條數(shù)】,建議根據(jù)需要用對應(yīng)方法,非必要的,不要用分頁的方法
95、ArrayList底層是動態(tài)數(shù)組,默認(rèn)初始化大小為10,自動擴(kuò)充容量是原有容量的1.5倍+1,通過底層的復(fù)制方法將原有數(shù)據(jù)復(fù)制,因此 如果數(shù)據(jù)量很大,那么造成數(shù)組重新分配的次數(shù)會增加,但對于一般的數(shù)據(jù)量下,1千需要分配 11次,1萬一級需要分配17次,10萬 需要分配23次,100萬需要分配28次。所以應(yīng)根據(jù)實際情況,大致分配一個初始化的容量還是有必要的。但是如果你初始容量太大,而數(shù)據(jù)增長很慢,那么就在浪費(fèi)內(nèi)存了。如何取舍,還是看具體的應(yīng)用場景。
96、HashMap底層是數(shù)組+鏈表,默認(rèn)初始化大小為16,加載因子為0.75,即容量超過16*0.75=12時自動擴(kuò)容,HashMap在擴(kuò)容時,新數(shù)組的容量將是原來的2倍,由于容量發(fā)生變化,原有的每個元素需要重新計算bucketIndex,再存放到新數(shù)組中去,也就是所謂的rehash。在設(shè)置初始容量時應(yīng)該考慮到映射中所需的條目數(shù)及其加載因子,以便最大限度地降低 rehash 操作次數(shù)。
97、創(chuàng)建dblinkcreate extension dblink;select * from pg_extension;select dblink_connect('test1_dblink','dbname=jw_platform_test1 host=192.168.18.206 port=5432 user=jw_test1 password=123456');通過dblink查詢select id,login_account fromdblink('test1_dblink','select id,login_account from bs_sys_user where id=10') as t1(id int, login_account varchar);通過dblink進(jìn)行insert selectinsert into jw_test2.bs_sys_user(id,login_account) select id,login_account fromdblink('test1_dblink','select id,login_account from bs_sys_user where id=10') as t1(id int, login_account varchar);根據(jù)不同當(dāng)前人不同酒店\集團(tuán),切換對應(yīng)dblink標(biāo)示
98、增加異步消費(fèi)者接口定義<dubbo:reference id="bsxxxxxxxxxxxAsync" interface="com.jw.hms.example.m3xxx.f001.microservice.BsxxxxxxxxxxService" timeout="60000" retries="0" check="false" registry="localzk" async="true" sent="true" return="false" />
99、報表制作注意事項:a、繪制報表,中文注意字體,建議先設(shè)置好所需樣式,單元格設(shè)置其樣式b、不用國際化,不同文字繪制不同報表c、圖片使用fastDFS的URLd、手工設(shè)置每個單元格高度、寬度,使上下單元格對其,可避免導(dǎo)出excel出現(xiàn)單元格擠在一起的情況【按照每個單元格對其方式畫,空白地方用空白label填充,方便對齊】以@開頭和@結(jié)尾,如:"@chainUid@、@unitUid@,系統(tǒng)域宏替換【中間只可以有字符\數(shù)字\下劃線】以#開頭和#結(jié)尾,如:"#resv_no#",做主子SQL替換,并自動填充值【中間只可以有字符\數(shù)字\下劃線】以&開頭和&結(jié)尾,如:&account.floor_id|param_id|FLOOR|param_drpt|account.floor_name&,做參數(shù)替換,并自動填充值【中間除了可以有字符\數(shù)字\下劃線,還可以有.和|】【其中參數(shù)類型,如FLOOR,參見《SVN:\新產(chǎn)品研發(fā)\platform\03.設(shè)計開發(fā)\02.數(shù)據(jù)庫設(shè)計\業(yè)務(wù)-參數(shù)定義.xlsx》】以$開頭和$結(jié)尾,$ and account.arr_dt >= cast(:arr_dt_begin as timestamp) |arr_dt_begin|EQUALS$,做SQL查詢條件的拼接和替換e、html報表多頁,每頁都是帶表頭和頁數(shù)信息的f、穿透可以使用"JavaScript:alert("+$F{id}+")"方式調(diào)用js腳本【a標(biāo)簽】j、圖片URL,動態(tài)填充【圖片一定要勾選上“Is lazy”這個選項】h、子報表URL,動態(tài)填充【相對路徑】i、字典填充【以&開頭和&結(jié)尾,如:&account.floor_id|param_id|FLOOR|param_drpt|account.floor_name&】j、非字典查詢條件where拼接【以$開頭和$結(jié)尾,$ and account.arr_dt >= cast(:arr_dt_begin as timestamp) |arr_dt_begin|EQUALS$】查詢條件關(guān)鍵字:EQUALS:等值IN-LIST:IN集合PRE-LIKE/SUF-LIKE/ALL-LIKE:前%/后%/前后%模糊查詢k、字典查詢條件where拼接【$account.floor_name|floor_name|FILTER.ALL-LIKE$】查詢條件關(guān)鍵字:FILTER.EQUALS:等值FILTER.IN-LIST:IN集合FILTER.PRE-LIKE/FILTER.SUF-LIKE/FILTER.ALL-LIKE:前%/后%/前后%模糊查詢ps:SQL數(shù)據(jù)源不拼接order by語句,排序在ireport中設(shè)置l、條件值,寫死拼接,放在xxx域中,用于展示【字典】m、查詢條件接口【填充字典內(nèi)容】n、parameters框架封裝共享session參數(shù)o、目前框架封裝如下16種中文字體SIMFANG【仿宋】、simhei【黑體】、simkai【楷體】、SIMLI【隸書】、simsun【宋體】、SIMYOU【幼圓】、STCAIYUN【華文彩云】、STFANGSO【華文仿宋】、STHUPO【華文琥珀】、STKAITI【華文楷體】、STLITI【華文隸書】、STSONG【華文宋體】、STXIHEI【華文細(xì)黑】、STXINGKA【華文行楷】、STXINWEI【華文新魏】、STZHONGS【華文中宋】p、非參數(shù)類型房含 【grp_rsv_package】 【PARA_PACKAGE】房含分組【grp_rsv_package_group】【PARA_PACKAGE_GROUP】房型 【grp_hk_unit_roomtype】 【PARA_ROOMTYPE】交易代碼【grp_fin_base_trncode】 【PARA_TRNCODE】部門 【grp_cmm_department】 【PARA_DEPT】班組 【?】 【PARA_?】職員 【grp_cmm_employee】 【PARA_EMP】職員角色【grp_cmm_role】 【PARA_ROLE】銷售員 【grp_cmm_saler】 【PARA_SALER】操作員 【grp_cmm_user】 【PARA_USER】
100、threadlocal的remove()將當(dāng)前線程局部變量的值刪除,目的是為了減少內(nèi)存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當(dāng)線程結(jié)束后,對應(yīng)該線程的局部變量將自動被垃圾回收,所以顯式調(diào)用該方法清除線程的局部變量并不是必須的操作,但它可以加快內(nèi)存回收的速度。threadlocal里面使用了一個存在弱引用的map,當(dāng)釋放掉threadlocal的強(qiáng)引用以后,map里面的value卻沒有被回收.而這塊value永遠(yuǎn)不會被訪問到了. 所以存在著內(nèi)存泄露. 最好的做法是將調(diào)用threadlocal的remove方法。dubbo服務(wù)提供方,線程池,線程結(jié)束并不被銷毀,需要必須顯式的去回收,人工調(diào)用remove(),否則內(nèi)存泄漏
101、entity上增加注解,@UniqueKeyEntity,說明本實體的唯一標(biāo)示是哪個字段,所有模型類都需要增加注解
102、所有需要記日志的dto,都增加實現(xiàn)LogOrigin接口相關(guān)代碼
103、 獲取客戶端訪問IP,用這個從頭獲取X-Real-IP
做強(qiáng)壯處理如果X-Real-IP為null,則用request.getRemoteAddr
104、手工插入數(shù)據(jù)庫,番號增加,則需把redis做相應(yīng)增加
手工造數(shù)據(jù)到數(shù)據(jù)庫,則主鍵ID序列需要做相應(yīng)增加
105、grp庫、pms庫、pcm庫中cmm_number表手工增加數(shù)據(jù),需要加大該表序列
106、日志庫所有表都按照日志時間進(jìn)行了分區(qū),查詢必須帶著相應(yīng)分區(qū)字段
107、@Override是偽代碼,表示重寫,但如果方法名稱相同而參數(shù)列表不同(返回類型可以相同也可以不同),那么只是方法的重載,而非重寫。
不要按照上面的這種寫法寫了哦
108、根據(jù)業(yè)務(wù)定義初始化數(shù)據(jù)庫連接池,目前約定如下,
每50個酒店左右一個庫,在增加到接近酒店數(shù)是,新建一套grp_dev、pms_dev、log_dev、stat_dev
所有酒店共享一個pcm_dev、cms_dev、jw_reportools
只有一套練習(xí)庫,包括:grp_dev、pms_dev、log_dev、stat_dev
只有一套測試庫,包括:grp_dev、pms_dev、log_dev、stat_dev
R層工程啟動,建立默認(rèn)數(shù)據(jù)庫連接,并讀取現(xiàn)有數(shù)據(jù)庫連接配置,建立所有數(shù)據(jù)庫連接
默認(rèn)數(shù)據(jù)庫連接:
maxActive=20【池最大20個鏈接】initialSize=1 【初始一個鏈接】minIdle=1 【池最小1個鏈接】
每個plf_ops_dbinfo中配置的業(yè)務(wù)庫連接,配置如下
開發(fā)環(huán)境 生產(chǎn)環(huán)境
initialSize.pcm=1 initialSize.pcm=10minIdle.pcm=1 minIdle.pcm=1maxActive.pcm=1000 maxActive.pcm=1000
109、select o from com.jw.hms.cmm.m3130.f010.entity.GrpRsvRate o where 1=1 and o.chainUid=:chainUid and o.unitUid=:unitUid and o.statusFlg=:statusFlg and o.rateCd=:rateCd上面第一條HSQL,查出的對象,Hibernate一級緩存起來了下面第二個SQl執(zhí)行前,怕數(shù)據(jù)庫上面查出的數(shù)據(jù)已經(jīng)被更改,query.list()進(jìn)行了update,以保證一致性select o from com.jw.hms.cmm.m3130.f010.entity.GrpRsvRate o where 1=1 and o.chainUid=:chainUid and o.unitUid=:unitUid and o.rateId=:rateId因此,大家在寫代碼時,請注意,查詢出對象能復(fù)用就復(fù)用,盡量不要多次查詢,會有一致性問題
110、檢查一下自己發(fā)番的KEY用的對不對哦,
別復(fù)制過來忘了改,用的其他的流水號!
111、對List、Set、Map在迭代的時候如果同時對其進(jìn)行修改就會拋出java.util.ConcurrentModificationException異常,原因調(diào)用list.remove()方法導(dǎo)致modCount和expectedModCount的值不一致。注意,像使用for-each進(jìn)行迭代實際上也會出現(xiàn)這種問題。不要使用Itr類中的remove()方法,雖然在單線程下不會報錯,但在多線程下仍會拋錯,因此建議用并發(fā)容器CopyOnWriteArrayList和ConcurrentHashMap代替ArrayList和Vector和HashMap。
CopyOnWriteArrayList注意事項
(1) CopyOnWriteArrayList不能使用Iterator.remove()進(jìn)行刪除。(2) CopyOnWriteArrayList使用Iterator且使用List.remove(Object);會出現(xiàn)如下異常:java.lang.UnsupportedOperationException: Unsupported operation removejava.util.concurrent.CopyOnWriteArrayList$ListIteratorImpl.remove
例如:List<string> list =
new
CopyOnWriteArrayList<string>();
for(String item : list){ String tem = item + "..."; list.remove(item); list.add(tem); }
112、去掉無用的System.out.println(),增加必要的logger.info ()和logger.error(),用于上線后,復(fù)雜業(yè)務(wù)方便跟蹤,快速有效解決問題。
113、根據(jù)下述索引建議,優(yōu)化SQL語句
(1)負(fù)向條件查詢不能使用索引select * from order where status!=0 and stauts!=1not in/not exists都不是好習(xí)慣可以優(yōu)化為in查詢:select * from order where status in(2,3)
(2)前導(dǎo)模糊查詢不能使用索引select * from order where desc like '%XX'而非前導(dǎo)模糊查詢則可以:select * from order where desc like 'XX%'
(3)數(shù)據(jù)區(qū)分度不大的字段不宜使用索引select * from user where sex=1原因:性別只有男,女,每次過濾掉的數(shù)據(jù)很少,不宜使用索引。
PS:特別說明一下區(qū)分度1: 重復(fù)度越高,區(qū)分度越小,索引效果越不好2: 重復(fù)度越低,區(qū)分度越高,索引效果越好,但帶來的影響也越大--增刪改變慢,并間接影響查詢速度.慣用手法: 截取不同長度,并測試其區(qū)分度select count(distinct left(word,6))/count(*) from dict;截取word字段長度,從1開始截取,計算字符前綴沒有重復(fù)的字符占全部數(shù)據(jù)的比例對于一般的系統(tǒng)應(yīng)用: 區(qū)別度能達(dá)到0.1-0.2,索引的性能就可以接受
(4)在屬性上進(jìn)行計算不能命中索引select * from order where YEAR(date) < = '2017'即使date上建立了索引,也會全表掃描,可優(yōu)化為值計算:select * from order where date < = CURDATE()或者:select * from order where date < = '2017-01-01'
(5)如果業(yè)務(wù)大部分是單條查詢,使用Hash索引性能更好,例如用戶中心select * from user where uid=?select * from user where login_name=?原因:B-Tree索引的時間復(fù)雜度是O(log(n))Hash索引的時間復(fù)雜度是O(1)
(6)允許為null的列,查詢有潛在大坑單列索引不存null值,復(fù)合索引不存全為null的值,如果列允許為null,可能會得到“不符合預(yù)期”的結(jié)果集select * from user where name != 'shenjian'如果name允許為null,索引不存儲null值,結(jié)果集中不會包含這些記錄。
(7)復(fù)合索引最左前綴,并不是值SQL語句的where順序要和復(fù)合索引一致用戶中心建立了(login_name, passwd)的復(fù)合索引select * from user where login_name=? and passwd=?select * from user where passwd=? and login_name=?都能夠命中索引select * from user where login_name=?也能命中索引,滿足復(fù)合索引最左前綴select * from user where passwd=?不能命中索引,不滿足復(fù)合索引最左前綴
PS:多說一下聯(lián)合索引聯(lián)合索引能夠滿足最左側(cè)查詢需求,例如(a, b, c)三列的聯(lián)合索引,能夠加速a | (a, b) | (a, b, c) 三組查詢需求。這也就是為何不建立(passwd, login_name)這樣聯(lián)合索引的原因,業(yè)務(wù)上幾乎沒有passwd的單條件查詢需求,而有很多l(xiāng)ogin_name的單條件查詢需求。最左前綴:顧名思義,就是最左優(yōu)先,上例中我們創(chuàng)建了a_b_c多列索引,相當(dāng)于創(chuàng)建了(a)單列索引,(a,b)組合索引以及(a,b,c)組合索引。 a ab abc標(biāo)準(zhǔn)寫法 ba bac bca cab cba acb這五種引擎會自動優(yōu)化成ab和abc的查詢順序查索引。
(8)使用ENUM而不是字符串ENUM保存的是TINYINT,別在枚舉中搞一些“中國”“北京”“技術(shù)部”這樣的字符串,字符串空間又大,效率又低。
(9)如果明確知道只有一條結(jié)果返回,limit 1能夠提高效率select * from user where login_name=?可以優(yōu)化為:select * from user where login_name=? limit 1原因:你知道只有一條結(jié)果,但數(shù)據(jù)庫并不知道,明確告訴它,讓它主動停止游標(biāo)移動
(10)把計算放到業(yè)務(wù)層而不是數(shù)據(jù)庫層,除了節(jié)省數(shù)據(jù)的CPU,還有意想不到的查詢緩存優(yōu)化效果select * from order where date < = CURDATE()這不是一個好的SQL實踐,應(yīng)該優(yōu)化為:$curDate = date('Y-m-d');$res = mysql_query('select * from order where date < = $curDate');原因:釋放了數(shù)據(jù)庫的CPU多次調(diào)用,傳入的SQL相同,才可以利用查詢緩存
(11)強(qiáng)制類型轉(zhuǎn)換會全表掃描select * from user where phone=13800001234不會命中索引
114、業(yè)務(wù)表為Date的字段,在做equals比較時要尤其注意
entity.getField().equals(java.util.Date),是無法比較的
原因:從數(shù)據(jù)庫里查詢的這個時間字段的類型,已經(jīng)被Hibernate默認(rèn)轉(zhuǎn)化為:java.sql.Timestamp類型,同java.util.Date類型比較會由于類型不一致,而直接返回false
建議:
1、都轉(zhuǎn)成String 再比較
2、Timestamp轉(zhuǎn)成Date
3、Date轉(zhuǎn)成Timestamp
4、java.util.Date.equals(entity.getField())
115、DAO封裝增加了
private void enhanceSaveEntiy_cache(E entity) throws Exception
private void enhanceUpdateEntiy_cache(E entity) throws Exception
以減少類反射的次數(shù),大家用到相應(yīng)類反射地方,可以參考
116、每次上傳后要統(tǒng)計代碼數(shù)量,供品質(zhì)量化使用
一段時間內(nèi)的差分代碼行數(shù)統(tǒng)計【統(tǒng)計JW + CW兩個倉庫】git log --author="bwliu" --since=3.day.ago --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 + $2 } END { printf "added lines: %s removed lines : %s total lines: %s \n",add,subs,loc }'
一段時間內(nèi)的新歸代碼行數(shù)統(tǒng)計自己根據(jù)近期新增文件統(tǒng)計
一段時間內(nèi)的差分代碼母代碼行數(shù)統(tǒng)計【修改代碼總行數(shù)】先打印某人某段時間提交文件查看行數(shù)命令git log --author="bwliu" --since=1.day.ago --pretty=tformat: --name-status | gawk '{print "wc -l """"D:/_git/jw-source/"""$2}'
然后執(zhí)行打印出來的查看條數(shù)的腳本
117、通過ID查名字的,都統(tǒng)一用如下接口
118、正式環(huán)境無感知迭代發(fā)布流程梳理如下:
1、導(dǎo)流把需要發(fā)布的集中中一個點(diǎn)的流量導(dǎo)到其他節(jié)點(diǎn)中去2、暫停一分鐘后,停前后端服務(wù)3、更新此次迭代的最新數(shù)據(jù)庫結(jié)構(gòu),緩存結(jié)構(gòu)及相應(yīng)數(shù)據(jù)4、發(fā)布后端程序5、發(fā)布前端程序6、啟動前后端服務(wù)7、測試后,流量導(dǎo)回到新發(fā)布服務(wù)上8、其他服務(wù)重復(fù)上述過程
灰度發(fā)布流程梳理如下:1、導(dǎo)流把需要發(fā)布的需調(diào)整灰度策略(目前可以根據(jù)請求頭上集團(tuán)UID和酒店UID進(jìn)行切換)節(jié)點(diǎn)的流量導(dǎo)到其他節(jié)點(diǎn)中去2、暫停一分鐘后,停前后端服務(wù)3、更新此次迭代的最新數(shù)據(jù)庫結(jié)構(gòu),緩存結(jié)構(gòu)及相應(yīng)數(shù)據(jù)4、發(fā)布后端程序5、發(fā)布前端程序6、啟動前后端服務(wù)7、測試后,流量導(dǎo)回到新發(fā)布服務(wù)上
119、正式環(huán)境GIT開發(fā)、測試、發(fā)布流程
120、query.list()的FlushMode默認(rèn)為AUTO,當(dāng)session設(shè)置為FlushMode.AUTO時,hibernate在進(jìn)行查詢的時候會判斷緩存中的數(shù)據(jù)是否為臟數(shù)據(jù),是則刷數(shù)據(jù)庫,不是則不刷
Hibernate session FlushMode有五種屬性:1、NEVEL:已經(jīng)廢棄了,被MANUAL取代了2、MANUAL:如果FlushMode是MANUAL或NEVEL,在操作過程中hibernate會將事務(wù)設(shè)置為readonly,所以在增加、刪除或修改操作過程中會出現(xiàn)如下錯誤org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition;解決辦法:配置事務(wù),spring會讀取事務(wù)中的各種配置來覆蓋hibernate的session中的FlushMode3、AUTO設(shè)置成auto之后,當(dāng)程序進(jìn)行查詢、提交事務(wù)或者調(diào)用session.flush()的時候,都會使緩存和數(shù)據(jù)庫進(jìn)行同步,也就是刷新數(shù)據(jù)庫4、COMMIT提交事務(wù)或者session.flush()時,刷新數(shù)據(jù)庫;查詢不刷新5、ALWAYS:每次進(jìn)行查詢、提交事務(wù)、session.flush()的時候都會刷數(shù)據(jù)庫
ALWAYS和AUTO的區(qū)別:當(dāng)hibernate緩存中的對象被改動之后,會被標(biāo)記為臟數(shù)據(jù)(即與數(shù)據(jù)庫不同步了)。當(dāng) session設(shè)置為FlushMode.AUTO時,hibernate在進(jìn)行查詢的時候會判斷緩存中的數(shù)據(jù)是否為臟數(shù)據(jù),是則刷數(shù)據(jù)庫,不是則不刷,而always是直接刷新,不進(jìn)行任何判斷。很顯然auto比always要高效得多。
121、所有dto和entity的描述和name要標(biāo)示正確
fieldnm是字段名,后面是中文描述這邊入ES轉(zhuǎn)化,要用這個的要不就轉(zhuǎn)化亂了
122、postgresql正則表達(dá)式使用:
操作符
描述
例子
~
匹配正則表達(dá)式,大小寫相關(guān)
'thomas' ~ '.thomas.'
~*
匹配正則表達(dá)式,大小寫無關(guān)
'thomas' ~* '.Thomas.'
!~
不匹配正則表達(dá)式,大小寫相關(guān)
'thomas' !~ '.Thomas.'
!~*
不匹配正則表達(dá)式,大小寫無關(guān)
'thomas' !~* '.vadim.'
正則表達(dá)式原子
原子
描述
(re)
(這里的 re 是任何正則表達(dá)式) 匹配一個對 re 的匹配,有可報告的匹配信息
(?:re)
同上,但是匹配不會被報告 (一個"不捕獲"圓括弧) (只在 ARE 中有)
.
匹配任意單個字符
[chars]
一個 方括弧表達(dá)式, 匹配任意的字符(參閱 Section 9.7.3.2 獲取更多細(xì)節(jié))
*k*
(這里的 k 是非字母數(shù)字字符) 匹配一個當(dāng)作普通字符看待的特定字符, 比如,\ 匹配一個反斜杠
*c*
這里的 c 是一個字母數(shù)字 (可能跟著其它字符),它是一個逃逸, 參閱 Section 9.7.3.3(僅存在于 ARE; 在 ERE 和 BRE 中,它匹配 c)
{
如果后面跟著一個字符,而不是數(shù)字, 那么就匹配左花括弧{;如果跟著一個數(shù)字, 那么它是范圍的開始(見下面)
x
這里的 x 是一個沒有其它特征的單個字符, 則匹配該字符
正則表達(dá)式量詞
量詞
匹配
一個匹配 0 或者更多個原子的序列
一個匹配 1 或者更多個原子的序列
?
一個匹配 0 個或者 1 個原子的序列
{m}
一個正好匹配 m 個原子的序列
{m,}
一個匹配m 個或者更多原子的序列
{m,n}
一個匹配 m 到 n 個(包含兩端) 原子的序列;m 不能比 n 大
*?
- 的非貪婪模式
+?
- 的非貪婪模式
??
? 的非貪婪模式
{m}?
{m} 的非貪婪模式
{m,}?
{m,} 的非貪婪模式
{m,n}?
{m,n} 的非貪婪模式
正則表達(dá)式約束
約束
描述
^
匹配字串的開頭
$
匹配字串的結(jié)尾
(?=re)
正前瞻 匹配任何匹配 re 的 子字串起始點(diǎn)(只在 ARE 中有)
(?!re)
負(fù)前瞻 匹配任何不匹配 re 的子字串的起始點(diǎn)。(只在 ARE 中有)
上面只列出正則表達(dá)式的一些基礎(chǔ)知識,正則表達(dá)式的檢索能力相當(dāng)強(qiáng)大,特別是對于類型為Text類型,并且存儲的是XML數(shù)據(jù)時,正則表達(dá)式的優(yōu)勢將更能發(fā)揮作用。
關(guān)于更詳細(xì)的信息可以參考文檔http://www.postgresql.org/docs/9.0/static/functions-matching.html
舉例:SELECT * FROM ind_dtofield where fieldnm !~* '[a-z]'
123、DTO的注解說明中,應(yīng)為界面上的名稱【參照界面填寫】
124、完善DTO中map的key值描述信息
125、完善DTO中字典屬性的注解說明
126、完善DTO中特殊信息的特殊標(biāo)記說明
127、 語種以前用的zh(中文)和en(英文),現(xiàn)在改為zh-CN(中文簡體:1220)和en-US(英語(美國:1056)),日語用ja-JP(日語(日本:1120))。前端傳的字符串應(yīng)該是小寫,數(shù)據(jù)庫是大小寫均有,寫SQL時注意轉(zhuǎn)換
128、共同參數(shù)、集團(tuán)酒店餐飲參數(shù)、系統(tǒng)參數(shù)處室話到緩存中的格式為:
共同參數(shù):類型語種.param_id.param_drpt
集團(tuán)酒店餐飲參數(shù):類型語種.attribute_vlu.attribute_drpt
系統(tǒng)參數(shù):類型_語種_master庫名.param_id.param_drpt【從庫查詢,得把從threadlocal中獲取的庫名轉(zhuǎn)化為主庫的】
上述紅字部分為key值,后面為map
設(shè)置方式:
jwCache.hmset(key, map)
jwCache.hset(key, field, value)
獲取方式
jwCache.hmget(key, fields...)
jwCache.hgetAll(key)
命令行示例:
hgetAll BLKLSTCXLRSN_1220hmget BLKLSTCXLRSN_1220 1hmget MARKET_1220_grp_dev 1000313echo -e -n "\xe5\x85\x8d\xe8\xb4\xb9\xe6\x88\xbf"
其中:(中文:1220)(英語:1056)(日語:1120)
129、除下圖之外,還會有一套模板庫,【模板庫僅供copy用,不程序不鏈接到其上】
PS:
dev庫不能被覆蓋,僅供開發(fā)用
已使用的庫,也不能被模板庫覆蓋
練習(xí)庫和測試庫,可以被本身的正式庫覆蓋
新建庫的發(fā)番配置數(shù)據(jù)要完整【韓松負(fù)責(zé)】
130、postgresql連接到schema【jdbc:postgresql://postgreSlave:5432/pcm_dev?characterEncoding=utf8¤tSchema=pcm_dev】
參見:https://jdbc.postgresql.org/documentation/head/connect.html
一個數(shù)據(jù)庫包含一個或多個命名的模式,模式又包含表。模式還包含其它命名的對象,包括數(shù)據(jù)類型、函數(shù),以及操作符。同一個對象名可以在不同的模式里使用而不會導(dǎo)致沖突; 比如,schema1和myschema都可以包含叫做mytable的表。和數(shù)據(jù)庫不同,模式不是嚴(yán)格分離的:一個用戶可以訪問他所連接的數(shù)據(jù)庫中的任意模式中的對象,只要他有權(quán)限。 我們需要模式有以下幾個主要原因: 1). 允許多個用戶使用一個數(shù)據(jù)庫而不會干擾其它用戶。 2). 把數(shù)據(jù)庫對象組織成邏輯組,讓它們更便于管理。 3). 第三方的應(yīng)用可以放在不同的模式中,這樣它們就不會和其它對象的名字沖突。
我們在使用一個數(shù)據(jù)庫對象時可以使用它的全稱來定位對象,然而這樣做往往也是非常繁瑣的,每次都不得不鍵入owner_name.object_name。PostgreSQL中提供了模式搜索路徑,這有些類似于Linux中的$PATH環(huán)境變量,當(dāng)我們執(zhí)行一個Shell命令時,只有該命令位于$PATH的目錄列表中,我們才可以通過命令名直接執(zhí)行,否則就需要輸入它的全路徑名。PostgreSQL同樣也通過查找一個搜索路徑來判斷一個表究竟是哪個表,這個路徑是一個需要查找的模式列表。在搜索路徑里找到的第一個表將被當(dāng)作選定的表。如果在搜索路徑中 沒有匹配表,那么就報告一個錯誤,即使匹配表的名字在數(shù)據(jù)庫其它的模式中存在也如此。 在搜索路徑中的第一個模式叫做當(dāng)前模式。除了是搜索的第一個模式之外,它還是在CREATE TABLE沒有聲明模式名的時候,新建表所屬于的模式。要顯示當(dāng)前搜索路徑,使用下面的命令:
131、不允許出現(xiàn)DTO嵌套自己的情況出現(xiàn)【除特殊需要外】
132、熱部署JRebel的spring監(jiān)控插件,對于spring-data的監(jiān)控有問題,請大家在開發(fā)環(huán)境啟動參數(shù)配置中關(guān)掉,如下圖所示:
133、防止NPE【NullPointerException】,是調(diào)用者的責(zé)任,注意NPE產(chǎn)生的場景:
1)返回類型為基本數(shù)據(jù)類型,return包裝數(shù)據(jù)類型的對象時,自動拆箱有可能產(chǎn)生NPE。
反例:public int f() { return Integer對象}, 如果為null,自動解箱拋NPE。
2) 數(shù)據(jù)庫的查詢結(jié)果可能為null。
3) 集合里的元素即使isNotEmpty,取出的數(shù)據(jù)元素也可能為null。
4) 遠(yuǎn)程調(diào)用返回對象時,一律要求進(jìn)行空指針判斷,防止NPE。
5) 對于Session中獲取的數(shù)據(jù),建議NPE檢查,避免空指針。
6) 級聯(lián)調(diào)用obj.getA().getB().getC();一連串調(diào)用,易產(chǎn)生NPE。
正例:使用JDK8的Optional類來防止NPE問題。
【Optional的代碼相對更加簡潔,當(dāng)代碼量較大時,我們很容易忘記進(jìn)行null判定,但是使用Optional類則會避免這類問題?!?br> 例子:return str.length(); 改為: return Optional.ofNullable(str).map(String::length).orElse(0);
例子:Optional<String> optStr = Optional.ofNullable(str); // 如果str是null,則創(chuàng)建一個空對象
例子:【手機(jī)和郵箱不是一個人的必須有的,所以我們利用Optional定義。】
public class User {/** 用戶編號 */private long id;private String name;private int age;// private Optional<Long> phone;// 不能序列化// private Optional<String> email;// 不能序列化
public User(String name, int age) {this.name = name;this.age = age;}
// 省略setter和getter
// 注意事項:Optional是一個final類,未實現(xiàn)任何接口,所以當(dāng)我們在利用該類包裝定義類的屬性的時候,如果我們定義的類有序列化的需求,那么因為Optional沒有實現(xiàn)Serializable接口,這個時候執(zhí)行序列化操作就會有問題
private long phone;
public Optional<Long> getPhone() {return Optional.ofNullable(this.phone);}
}
例子:【映射:map與flatMap】【映射是將輸入轉(zhuǎn)換成另外一種形式的輸出的操作】
String name = Optional.ofNullable(user).map(User::getName).orElse("no name");因為map之后返回的是Optional,我們把這種稱為Optional嵌套,我們必須在map一次才能拿到我們想要的結(jié)果:long phone = optUser.map(User::getPhone).map(Optional::get).orElse(-1L);其實這個時候,更好的方式是利用flatMap,一步拿到我們想要的結(jié)果:long phone = optUser.flatMap(User::getPhone).orElse(-1L);
例子:【過濾:fliter 】【可以將過濾操作做為參數(shù)傳遞給該方法,從而實現(xiàn)過濾目的】
optUser.filter(u -> u.getAge() >= 18).ifPresent(u -> System.out.println("Adult:" + u));
134、避免出現(xiàn)重復(fù)的代碼(Don’t Repeat Yourself),即DRY原則。
隨意復(fù)制和粘貼代碼,必然會導(dǎo)致代碼的重復(fù),在以后需要修改時,需要修改所有的副本,容易遺漏。必要時抽取共性方法,或者抽象公共類,甚至是組件化。 正例:一個類中有多個public方法,都需要進(jìn)行數(shù)行相同的參數(shù)校驗操作,這個時候請抽取:private boolean checkParam(DTO dto) {...}
135、編寫單元測試代碼遵守BCDE原則,以保證被測試模塊的交付質(zhì)量。
B:Border,邊界值測試,包括循環(huán)、特殊取時間點(diǎn)數(shù)據(jù)順序等。
C:Correct,正確的輸入,并得到預(yù)期結(jié)果。
D:Design,與設(shè)計文檔相結(jié)合,來編寫單元測試。
E:Error,強(qiáng)制錯誤信息輸入(如:非法數(shù)據(jù)、異常流程業(yè)務(wù)允許等),并得到預(yù)期的結(jié)果。
136、為了更方便地進(jìn)行單元測試,業(yè)務(wù)代碼應(yīng)避免以下情況:
a) 構(gòu)造方法中做的事情過多。
b) 存在過多的全局 變量 和靜態(tài)方法。
c) 存在過多的外部依賴。
d) 存在過多的條件語句。
說明: 多層條件語句建議使用衛(wèi)、策略模式狀態(tài)等方重構(gòu)。