CloudJavaBackendSummaries

CloudJavaBackendSummaries
1、开发环境eclipse工程,引入jw仓库的jw-base,3-base,3-common工程,引入cw仓库的自己的工程,以及cw仓dto库中31开头工程,方Concurrent便调试(因为现在机器配置较低,建议删除McAfee,停掉其他杀毒软件,和防火墙)
启动jvm,增加参数,-Dlog4j.home=D:/sqlCmd,其中"log4j.home"配置自己机器路径

2、每个功能操作必须对应一个API,不能复用(比如,删除单条,批量删除),A层每个功能一定要拆开。
ps:增加了Controller方法操作所对应系统功能code注解@OperFunCode(funCode="xxx-xxx-001")

3、除了包名、工程名、A层controller类的RequestMapping之外,不要用工程名命名任何东西。
ps:A层controller类的URL命名,参照如下/bs/3510010/GrpCrmProfile
删除无用A层方法,及其后续P层、I层、D层代码生成的方法!!!

4、xdo.cfg文件在启动时候生成,会根据当前工程位置生成,因此不要上传

4、清除暂时不用的代码,只删除代码生成的,如果有在代码生成后,修改有用信息的,均不删除ps:要记得修改相应的P层和A层的配置文件

5、entity实体生成后,根据业务需要修改其继承父实现类、以及实现的接口类。目前提供的接口类汇总如下:
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,测试一下并发情况【尤其是自己写的核心业务】

7、公共静态枚举,统一使用com.jw.base.framework.core.Constant,目前包含如下:
状态枚举 0:无效 1:有效(默认)休眠标识 0:正常(默认) 1:休眠保密标识 0:不保密(默认) 1:保密性别 0:男(默认) 1:女 2:未知默认标志 0:非默认 1:默认常用标志 0:非常用(默认) 1:常用联系方式类型 0:电话,1:QQ,2:EMAIL,3:微信建立方式 1:酒店 2:网站 3:线下会员系统 4:CRS
null标识:1:null 0:not null
写枚举时,枚举类型,及其包含属性必须编写相应注释;根据需要设置default值(若有必要)或抛出异常;要有isEquals方法、以及getEnumConsNm【switch case用】
静态变量定义要功能闭包。

8、小数数字entity统一用java.math.BigDecimal,整数统一用Integer,字符统一用String,日期统一用java.util.Date
ps:不要用com.ibm.icu.math.BigDecimal,要用java.math.BigDecimal

9、所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。说明: 对于Integer var = ?在-128至127之间的赋值,Integer对象是在 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行 判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑, 推荐使用 equals 方法进行判断。

10、entity的@Column中nullable = false或true,要和数据库保持一致(数据库变更要通知开发人员)

11、注意变量名的拼写(部分单词中某些字符前后颠倒)

12、新增方法,I层需统一设置默认值,不能依赖前端传值(如是否休眠、是否有效、是否黑名单)

13、注意不要忽略A层validator的校验,要写全面完整
ps:ValidConstant,命名统一按照: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";/* 开始日期不可大于结束日期 /public static final String VALID_GRPCRMCORPCONTRACT_004 = "VALID_GRPCRMCORPCONTRACT_004";/* 合同类型不可为空 */public static final String VALID_GRPCRMCORPCONTRACT_005 = "VALID_GRPCRMCORPCONTRACT_005";

14、3105001工程中ValidConstant、LogConstant按照业务分包放

15、对象复制不使用BeanUtil.copyPropertiesExceptNull,统一使用entity中copyentity方法

16、dto中不再用现成的entity做定义,用新定义的dto(结合页面元素),新增模块package
dto的子包,包名要全部小写,不要已大写字母开头命名

17、所有sql语句select列表,默认order by 自增主键

18、带条件和排序查询,统一使用增强的enhanceQueryListByProerties方法,暂不允许使用批量更新方法,批量更新业务实现,需先enhanceGet,再逐条保存

19、日期查询sql统一使用如下方式:(禁止在where条件属性中使用函数)
select cast(字段 as timestamp) from where cast('2015-10-12 12:02:45' as timestamp) <= 字段;统一使用DateUtil.DATE_TIME_FORMAT(yyyy-MM-dd HH:mm:ss,大写HH代表24小时制)


20、悲观锁统一使用lock类锁表,各个业务实现自己的锁表类
Map<String,String> paramMap = new HashMap<String, String>();paramMap.put(BsSysUser.class.getName(), bsSysUserRequestFormDto.getSubmitData().getId().toString());lockProvider.lock(paramMap); 锁表用法

21、改子表,保存后,更新主表version表

22、DTO中每条业务记录都需带着“操作标示”(统一放在entity的subdto中)

23、要优先使用目前提供的工具类
StringUtil:isnull、isnotnull、spilt
集合工具统一用import org.apache.commons.collections4.CollectionUtils;

24、新增修改需为两个方法,不能复用一个

25、统一用CodeBean.java返回给前端key + code + name

26、checkbox类似数组存储,不用逗号隔开,用统一的子表(多项表)

27、ResponseFormDto返回结果数据中统一叫resultData

28、要使spring中校验生效,需增加调用doValidator


29、A层@OperSysType注解,暂不用打,不用注解该方法调用哪个数据源;【考虑到以后校验,可能以后会打,统一再重构】
S层增加@OperSysType注解,说明本方法提供对应数据源类型;S层@OperSysType注解,增加属性isSlave,默认false,切换为主库,如果为true,切换从库PCM标示切换到PCM库,HPT标示根据操作酒店对应数据库切换、GRP标示根据当前集团对应数据库切换ps:目前dto中专了sourceNm,以sourceNm切,优先级最高;第二优先级为S层上的注解,第三优先级为A层注解(A层注解咱不用打)

30、A层@DrptAPI注解中增加属性listParam,dtoResponse,如下:
@DrptAPI(infnm="列表获取数据",infdrpt="列表获取数据",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="根据ID获取详细信息",infdrpt="根据ID获取详细信息",dtoParam="IdRequestDto",dtoResponse="com.jw.hms.example.m31xx.f001.dto.BsSysUserResponseFormDto")

31、@DrptAPI注解必须书写全参数@DrptAPI(infnm="列表获取数据",infdrpt="列表获取数据",dtoParam="GrpRolePermissionQueryDto",dtoResponse="com.jw.common.framework.m0002.f001.dto.DataTableDto",listParam="...")

32、共同参数统一调用CmmParameterProvider中的如下方法
获取数据字典List(过滤后的字典集合)public DictionaryResponseDto querylistDictionaryList(DictionaryQueryDto dictQueryDto) throws Exception获取数据字典key对应的描述(单条描述)public DictionaryResponseDto querylistDictDescription(DictionaryQueryDto dictQueryDto) throws Exception
P层远程调用P层,所有调用查参数统一用:字典表获取 PlfCmmParameterServiceImpl.queryListToDictionary
ps:PSM库没有参数表

33、列表查询方法(P层)统一使用queryList开头、查复杂记录方法(P层)统一使用queryIds开头、查报表方法(P层)统一使用queryReport开头。

34、查询sql,当前操作酒店条件,统一使用ShareSession.getSessionUnitUid_EXCU。

35、sql的like查询,要根据业务需要,增加前后百分号;in用List,order by多个用List;

36、PMS和餐饮的entity需implements OucdEntity,字段可以任意起名,但需在成员变量上打上如下注解@FieldUnitcd(systyp=Constant.SYS_TYP_PMS),并实现getUnitUidOu(),和setUnitUidOu方法。A层给RequestCommonDto中的UnitUid赋值,P层新增要增加@CheckUnitcd(systyp=Constant.SYS_TYP_PMS)。

37、CRM和GROUP中,前台需要选择酒店,录入新增记录场景,其对应entity需implements PoscdEntity,字段可以任意起名,但需在成员变量上打上如下注解@FieldUnitcd(systyp=Constant.SYS_TYP_GRP),并实现getUnitUidPos(),和setUnitUidPos方法
系统会只在保存时,给其赋值,赋值来源ShareSession.getSessionUnitUid_EXCU(),该值由A层初始化赋值中获取(RequestCommonDto中的UnitUid)--dto.getRequestCommonDto().setUnitUid("xxxxxxxxx");P层之前的AuthorityFilter,会将RequestCommonDto中的UnitUid,set到threadlocal中。统一的CheckUnitcdAdvice拦截进行同登录人能操作酒店进行校验P层仅仅新增要增加@CheckUnitcd(systyp=Constant.SYS_TYP_GRP

38、查询列表页面,传入查询条件的dto,不建议是个通用的map,并且要做必要的spring @Valid

39、用@Deprecated注释的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择。在使用不被赞成的程序元素或在不被赞成的代码中执行重写时,编译器会发出警告。
java.lang.Deprectated是J2SE 5.0中标准的Annotation型态之一,它对编译器说明某个方法已经不建议使用,如果有人试图使用或重新定义该方法,必须提出警示讯息。

40、检查建的表,应该没有默认值,并且可以为空字段应为DEFAULT NULL

41、前端联调机器192.168.18.35(Administrator\123456),把在本机开发环境maven install的R和RA工程的target下的war包放到相应webapp下重启服务
ps:35部署记得清缓存,D:\java\apache-tomcat-8.5.4\work
ps:R和RA工程必须,clean + install,这样其.war包才是最新的

42、代码注释原则:凡同示例代码不同逻辑,均需编写必要代码注释做相应说明(方法说明,应写明业务,不能仅仅些表名英文)

43、无用的老代码,不要保留,建议全部删掉(git上会有版本,可以找回),尤其注意功能调整变化造成的已无用的代码

44、@DrptController、@DrptAPI、@DrptField等接口自定义系统注解,要增加必要业务说明
ps:dto注解DrptField增加属性nullable、length、precision,并且其中fieldnm放字段英文名

45、A层@Log注解中的logCD,应用对应静态类静态变量进行赋值

46、A层Controller中try\catch捕获异常处理类中增加请求dto参数,供log4j日志记录用(bug调试用)
ExceptionTools.handleException(exception, dto, this.getRequest(), this.getResponse());

47、>微服务层必须注解事务@Transactional,在get,is等读操作微服务中必须@Transactional(readOnly = true),即从这一点设置的时间点开始(时间点a)到这个事务结束的过程中,其他事务所提交的数据,该事务将看不见(查询中不会出现别人在时间点a之后提交的数据)(如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性)

如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持。>@Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能。>在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。>用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期异常(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的异常时回滚;而遇到需要捕获的异常(throw new Exception("注释");)不会回滚,即遇到受检查的异常(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常})

48、变量命名,不要以_开头

49、尽量不要在字段中存入null值,null值可用-1替代,在oracle中,null跟''是一样的,oracle都会理解为是null值;在mysql、PostgreSQL中,null与''是不相同的,''会插入一个空字符串

50、@SuppressWarnings。该批注的作用是给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默。如:@SuppressWarnings("deprecation")表示不显示使用了不赞成使用的类或方法时的警告。

51、注意各个业务RequestFormDto中getLogDiffer,要和界面保持一致,以便后面页面还原使用(dto中getLogDiffer方法不能都给注释掉,要按照页面元素进行相应编码)

52、dto中getLogDiffer方法,不要用@JsonIgnore注解,或@JsonIgnoreProperties,这两个是jackson来处理json串转换的问题的(有些时候我们经常有忽略某些属性的情况),不是FastJson的,改用@JSONField(serialize=false),或用FastJson提供的各种filter机制来实现

53、不要用JsonUtil,改用FastJsonUtil,否则转换整形会有小数点(Gson默认将int和long型数据转换为double)
ps:fastjson解析json时报错default constructor not found. class.
类需要有一个空的构造函数public JwMessage() {super();}

54、getLogDiffer中setPfieldvalue值,如果是Integet不要加.toString(),统一使用StringUtil.nullToEmpty方法处理,如下:改为:ib.setPfieldvalue(StringUtil.nullToEmpty(getOriginData().getSeq()));大家要根据代码生成,根据不同类型,做相应处理

55、代码注释建议使用javadoc的/** xxxxxxxxx /,不用/ xxxxxxxxx / a、单选注释:符号是:// b、块注释: 符号是: / / 可以跨多行 c、javadoc注释: 符号是: / */ 可以跨多行, 生成javadoc时,这样的注释会被生成标准的javaapi注释。

56、redis调用(批量、同时操作多个key值的)方法如mset、msetnx、mget、rename、renamenx、smove、sinter、sdiff、sunion、zunionstore、时,key值前面拼接上Constant.KEY_SLOTkeySlot算法中,如果key包含{},就会使用第一个{}内部的字符串作为hash key,这样就可以保证拥有同样{}内部字符串的key就会拥有相同slot。

57、G是带泛型的,非G是非泛型的产品baseDAO实现:BaseProductJdbcDaoImpl.java(BaseProductJdbcDaoImpl 增加三个with语句拼接方法)BaseProductProvider原子服务接口:BaseProductGProvider.javaBaseProductProvider原子服务接口:BaseProductProvider.javaBaseProductService微服务接口:BaseProductGService.javaBaseProductService微服务接口:BaseProductService.javaBaseProductProviderImpl原子服务实现:BaseProductGProviderImpl.javaBaseProductProviderImpl原子服务实现:BaseProductProviderImpl.javaBaseProductServiceImpl微服务实现:BaseProductGServiceImpl.javaBaseProductServiceImpl微服务实现:BaseProductServiceImpl.java

58、D层DaoQueryMysqlImpl、DaoQueryOracleImpl、DaoQuerySqlServerImpl先删掉,生成的不放到工程代码中

59、修改为统一只用日志框架SLF4J中的API【门面模式的日志框架】,有利于维护各个类的日志处理方式
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按照日志级别输出(分包记录,后续完善)${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、金额、房间数、人数等放置默认值"0",字符串的根据业务需要放置默认值,不建议有值为null

63、接口自定义系统,包名注意,按照变化及时修改(dto)

64、dto和entity中,成员变量,均要运用驼峰命名法(处敏感信息外)
entity中Integer类型字段打上@Size注解 保存时会报 HV000030: No validator could be found for type: java.lang.Integer.这个错误,应该打@Max注解ps:一定注意String改为Integer时,要进行相应修改

65、所有GRP的表,应该createtype字段,统一在A层放值ps:检查所有表,createtype字段是否建全

66、修改字段名、字段类型、字段长度等,建议全部工程Search一下,该字段名 + 字段名的驼峰命名,确认修改完整ps:如果涉及框架,要和刘博文沟通

67、单表新增不用锁记录,修改锁当前记录;主子表情况,子表新增或修改锁主表,并且修改时锁自身;

68、在做功能前查看一下,是否已有人对应相应业务表做了代码生成(Ctrl+Shift+R)

69、发番统一获取,在P层注入I层发番类@Resourceprivate CmmComIdProvider cmmComIdProvider;cmmComIdProvider.getNumberId(GrpCrmCorpConst.CONTRACT_NO_KZNO)其中GrpCrmCorpConst.CONTRACT_NO_KZNO,需在各自库中cmm_number表中,配置相应发番规则
按照该《系统配置.xlsx》中发番配置,统一从com.jw.base.framework.core.Constant获取发番类型

70、注入类,用@Resource,默认安照名称进行装配,名称可以通过name属性进行指定,变量名要与类名一致@Resourceprivate HptRsvRateCalendarService hptRsvRateCalendarService;

71、测试要记得清脏数据,如grp库中createtyp中值为null,以前代码问题造成的脏数据等
ps:货币统一用NUMERIC(16,4)

72、p层,RPC远程调用其他P层(统一在R层配置,方便管理)


P层调用时要把commondto中属性值赋值上,如sessionkey、token等
ps:
P层配置服务提供方接口



A层配置服务消费方接口【需要把RA工程的Eclipse中Jetty配置勾去“Compile Scope”,不影响开发的热部署】

若RA工程的Eclipse中Jetty配置勾上“Compile Scope”,把A层工程target勾掉,并且把“spring-web”的jar包勾掉勾上的JRebel将监听【monitored】classes文件的changes


73、对于大表,不要用hibernate查询,用sql。Hibernate: select ... from XXX order by id desc limit ? offset ? 手拼SQL不用参数化: select ... from XXX order by id desc limit 30 offset 0 select里如果用参数,将极慢,如果直接拼到sql里,就快很多,差好几个数量级

74、jdbc查询sql,只能用于列表、报表,不允许使用在事务中(jdbc同hibernate不是一个链接)新提供了下面几个org.hibernate.Session的方法public List<Map<?, ?>> findBySqlToMap(final String sql);【现有query方法做复杂查询不方便时使用】public List<Map<?,?>> findBySqlToMap(final String sql, final Object[] params);【现有query方法做复杂查询不方便时使用,也适用于有返回结果集的存储过程或查询语句】public Integer executeSql(final String sql);【只在select insert等特殊情况下用,此方法不能用于执行存储过程】
拼写select insert语句执行,也要参数化!!!!

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、一个事务中,如业务需要一次性创建多对象,建议executeSql执行select insert语句,提高速度

77、使用各自账户进行测试
用户名/密码: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、字典表如下
共同参数:plf_cmm_parameter, plf_cmm_param_multilang(PCM、GRP、PMS)集团参数:cmm_base_param, cmm_base_param_multilang(PCM、GRP、PMS)酒店参数:cmm_unit_param, cmm_unit_param_multilang(PCM、GRP、PMS)pos参数: cmm_pos_param, cmm_pos_param_multilang(GRP、PMS)系统参数:plf_cmm_config, plf_cmm_config_multilang(PCM、GRP、PMS)
配合朱倩梳理现有参数表数据,梳理后会删掉pcm库和pms库中10张参数表,后续需要增加参数数据,统一找朱倩,需要增加类型(修改EXCEL),统一找韩松
按照《业务-参数定义.xlsx》梳理共同参数、酒店集团参数等,调用参数类型统一修改为com.jw.base.framework.core.Constant中静态变量ps:有之前部分共同参数修改为了参数、酒店集团参数

79、CountDownLatch和CyclicBarrier线程同步的辅助工具,通过它可以做到使一条线程一直阻塞等待,直到其他线程完成其所处理的任务。CountDownLatch:用给定的计数初始化CountDownLath。调用countDown()方法计数减 1,在计数被减到 0之前,调用await方法会一直阻塞。减为 0之后,则会迅速释放所有阻塞等待的线程,并且调用await操作会立即返回。CyclicBarrier:用计数 N 初始化CyclicBarrier, 每调用一次await,线程阻塞,并且计数+1(计数起始是0),当计数增长到指定计数N时,所有阻塞线程会被唤醒。继续调用await也将迅速返回。
CountDownLatch用法之一:初始化CountDownLatch startSwitch = new CountDownLatch(1),以及CountDownLatch stopSwitch = new CountDownLatch(groupNum),先在线程池中初始化要并行的线程,每个线程中startSwitch开关先阻塞,在主线程中startSwitch.countDown()(计数-1),开始所有线程池中线程(解锁获得资源),同时主线程stopSwitch.await()阻塞,每个线程池中线程完成各自工作后stopSwitch.countDown()计数器减一,最后一个子线程完成工作后,唤醒主线程继续。CyclicBarrier用法之一:初始化CyclicBarrier startSwitch = new CyclicBarrier(groupNum + 1),以及CyclicBarrier stopSwitch = new CyclicBarrier(groupNum),先在线程池中初始化要并行的线程,每个线程中startSwitch开关先阻塞(计数+1),在主线程中startSwitch.await()(计数+1),开始所有线程池中线程(当计数增长到指定计数N时,所有阻塞线程会被唤醒),同时主线程service.shutdown(),在等待所有线程池中线程运行完成,每个线程池中线程完成各自工作后stopSwitch.await()阻塞(计数+1),最后一个子线程完成工作后,主线程service.shutdown()完成并继续。

80、线程池创建至少是通过静态成员变量方式,不能是局部成员变量,后续修改为通过ThreadPoolExecutor方式

81、框架提供了拦截器进行统一的限流 + 系统参数cfg.properties定时重新加载【访问路径计数是否大于限流值】

82、线程不安全的(和公共资源相关的),getUUID,获取账号,主键,获得密钥key,减库存,抢优惠券(用到某些数据结构,队列或集合,参照如下结构使用)(方法内部定义的变量,不涉及线程安全,通常设计线程安全的变量一般都是成员变量)
HashMap非线程安全,改用java.util.concurrent.concurrentHashMap,或HashTable(HashTable不允许插入空值,HashMap允许),或者使用Collection工具类将集合包装成线程安全的集合(pubilc static Map m = Collections.synchronizedMap(new HashMap()),此方法在并发级别不高是也够用)
ArrayList非线程安全,虽然Vector是线程安全的,但Vector效率低,因此不建议使用(ArrayList和Vector都是使用“数组”作为其内部实现)可以考虑使用java.util.concurrent.CopyOnWriteArrayList(在读多写少的场景,次List性能非常好,对于它来说,读取是完全不用加锁的,写入也不会阻塞读取操作,只有写入和写入之间需要进行同步等待),所谓CopyOnWrite就是在写入操作时,进行一次自我复制,即,当List需要修改时,并不修改原有内容(这对于保证当前在读线程的数据一致性非常重要),而是对原有数据进行一次复制,将修改内容写入副本中,写完后,再将修改完的副本替换原有数据,这样就保证写操作不会影响读了。
LinkedList非线程安全,使用链表的数据结构实现了List,可以使用Collections.synchronizedList()方法来包装任意List(pubilc static List<String> l = Collections.synchronizedList(new LinkedList<String>())
StringBuffer是线程安全,StringBuilder是线程不安全的(这句话是不对的),StringBuffer只是说他的方法是排他的,也就是说,StringBuffer每一次append是线程安全的,但众多次append的组合并不是线程安全的(在方法上加synchronized关键字),如果换成StringBuilder,甚至有可能append一半,就让位其他线程

83、JDK并发包中,有一个atomic包,里面实现了一些直接使用CAS操作的线程安全的类型无锁(原子)线程安全整数:AtomicInteger,对于整数的封装无锁(原子)数组:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray无锁(原子)的对象引用:AtomicReference,对于普通对象的引用带有时间戳的对象引用:AtomicStampReference让普通变量也能享受原子操作:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater

84、之后我们前后端接口的敲定遵循契约精神,按如下流程进行:1、数据库设计完成后韩松、赵翔、博文、后端开发一起商量所需接口个数和每个接口需要实现的功能。2、后端项目经理按照接口数和工作量分配负责开发每个接口的后端人员。3、后端相关人员进行所负责接口的定义,包括参数说明及返回值说明。4、定义好的接口给博文审核,确保技术实现上无问题且实现方式最优。5、博文审核过的接口给赵翔审核,确保可以完全实现前端业务需求。6、审核通过的接口进入接口定义系统,包括输入输出结构定义,此时形成契约。7、后端人员进行接口开发及单元测试,注意接口健壮性、容错(如数据库字段变更不应影响契约)和方便性(如允许传入最少的调入参数)。8、开发完成的接口提交测试环境,崔俊依据接口系统生成自动化测试脚本,并进行返回值验证,判断返回键值是否符合契约。9、测试通过的接口通知前端进行联调,否则退回后端进行修改,使之遵守契约。

85、"push git 411"问题,如下:error: RPC failed; result=22, HTTP code = 411fatal: The remote end hung up unexpectedlyps:以上发生在push命令中,有可能是push的文件过大导致解决方法:修改仓库下.git/config 文件>windows:在 .git/config 文件中加入[http]postBuffer = 524288000>Linux:git config http.postBuffer 524288000

86、配置统一放置,便于自动化部署,所有配置信息均汇总于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【各个工程名】不用在工程启动参数中增加-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配置文件放到相应base工程中

87、写枚举时,枚举类型,及其包含属性必须编写相应注释;根据需要设置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尽量设置大些// 防止创建超过maxActive数量的连接if (activeCount + poolingCount >= maxActive) {empty.await();continue;}连接太多了的时候,在empty条件上等待,就是等空了再运行

90、在工程中增加禁止爬虫爬取的robots.txt配置文件

91、上线一定要根据需要设置druid的maxActive,最大并发数
注意调整好服务器时间~~

92、dubbo异步调用定义如下<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层充当消费者调用DataTableDto data2 = bsSysUserService.queryListAsync(bsSysUserQueryDto, page);System.err.println("立即返回为null:"+data2); //拿到调用的Future引用,当结果返回后,会被通知和设置到此Future。 Future<DataTableDto> pFuture = RpcContext.getContext().getFuture(); //如果data已返回,直接拿到返回值,否则线程wait,等待data返回后,线程会被notify唤醒。 data2 = pFuture.get();System.out.println("返回异步值"+data2);
PS:注意dubbo异步调用传递性问题
ServiceA异步调ServiceB,ServiceB再同步调ServiceC,此时ServiceC会当异步调用。但是,如果后续还有同步调用,则因B调C为同步,则就会正常同步调用了。

93、多线程并发,建议使用实现Callable接口,与Future + 线程池结合使用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检索查询,有两个,一个是带分页的,一个是不带分页的,带分页的会查询两次【其中有一次是统计总条数】,建议根据需要用对应方法,非必要的,不要用分页的方法

95、ArrayList底层是动态数组,默认初始化大小为10,自动扩充容量是原有容量的1.5倍+1,通过底层的复制方法将原有数据复制,因此 如果数据量很大,那么造成数组重新分配的次数会增加,但对于一般的数据量下,1千需要分配 11次,1万一级需要分配17次,10万 需要分配23次,100万需要分配28次。所以应根据实际情况,大致分配一个初始化的容量还是有必要的。但是如果你初始容量太大,而数据增长很慢,那么就在浪费内存了。如何取舍,还是看具体的应用场景。
96、HashMap底层是数组+链表,默认初始化大小为16,加载因子为0.75,即容量超过16*0.75=12时自动扩容,HashMap在扩容时,新数组的容量将是原来的2倍,由于容量发生变化,原有的每个元素需要重新计算bucketIndex,再存放到新数组中去,也就是所谓的rehash。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地降低 rehash 操作次数。

97、创建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进行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);根据不同当前人不同酒店\集团,切换对应dblink标示

98、增加异步消费者接口定义<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、绘制报表,中文注意字体,建议先设置好所需样式,单元格设置其样式b、不用国际化,不同文字绘制不同报表c、图片使用fastDFS的URLd、手工设置每个单元格高度、宽度,使上下单元格对其,可避免导出excel出现单元格挤在一起的情况【按照每个单元格对其方式画,空白地方用空白label填充,方便对齐】以@开头和@结尾,如:"@chainUid@、@unitUid@,系统域宏替换【中间只可以有字符\数字\下划线】以#开头和#结尾,如:"#resv_no#",做主子SQL替换,并自动填充值【中间只可以有字符\数字\下划线】以&开头和&结尾,如:&account.floor_id|param_id|FLOOR|param_drpt|account.floor_name&,做参数替换,并自动填充值【中间除了可以有字符\数字\下划线,还可以有.和|】【其中参数类型,如FLOOR,参见《SVN:\新产品研发\platform\03.设计开发\02.数据库设计\业务-参数定义.xlsx》】以$开头和$结尾,$ and account.arr_dt >= cast(:arr_dt_begin as timestamp) |arr_dt_begin|EQUALS$,做SQL查询条件的拼接和替换e、html报表多页,每页都是带表头和页数信息的f、穿透可以使用"JavaScript:alert("+$F{id}+")"方式调用js脚本【a标签】j、图片URL,动态填充【图片一定要勾选上“Is lazy”这个选项】h、子报表URL,动态填充【相对路径】i、字典填充【以&开头和&结尾,如:&account.floor_id|param_id|FLOOR|param_drpt|account.floor_name&】j、非字典查询条件where拼接【以$开头和$结尾,$ and account.arr_dt >= cast(:arr_dt_begin as timestamp) |arr_dt_begin|EQUALS$】查询条件关键字:EQUALS:等值IN-LIST:IN集合PRE-LIKE/SUF-LIKE/ALL-LIKE:前%/后%/前后%模糊查询k、字典查询条件where拼接【$account.floor_name|floor_name|FILTER.ALL-LIKE$】查询条件关键字:FILTER.EQUALS:等值FILTER.IN-LIST:IN集合FILTER.PRE-LIKE/FILTER.SUF-LIKE/FILTER.ALL-LIKE:前%/后%/前后%模糊查询ps:SQL数据源不拼接order by语句,排序在ireport中设置l、条件值,写死拼接,放在xxx域中,用于展示【字典】m、查询条件接口【填充字典内容】n、parameters框架封装共享session参数o、目前框架封装如下16种中文字体SIMFANG【仿宋】、simhei【黑体】、simkai【楷体】、SIMLI【隶书】、simsun【宋体】、SIMYOU【幼圆】、STCAIYUN【华文彩云】、STFANGSO【华文仿宋】、STHUPO【华文琥珀】、STKAITI【华文楷体】、STLITI【华文隶书】、STSONG【华文宋体】、STXIHEI【华文细黑】、STXINGKA【华文行楷】、STXINWEI【华文新魏】、STZHONGS【华文中宋】p、非参数类型房含 【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()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。threadlocal里面使用了一个存在弱引用的map,当释放掉threadlocal的强引用以后,map里面的value却没有被回收.而这块value永远不会被访问到了. 所以存在着内存泄露. 最好的做法是将调用threadlocal的remove方法。dubbo服务提供方,线程池,线程结束并不被销毁,需要必须显式的去回收,人工调用remove(),否则内存泄漏

101、entity上增加注解,@UniqueKeyEntity,说明本实体的唯一标示是哪个字段,所有模型类都需要增加注解


102、所有需要记日志的dto,都增加实现LogOrigin接口相关代码


103、 获取客户端访问IP,用这个从头获取X-Real-IP
做强壮处理如果X-Real-IP为null,则用request.getRemoteAddr


104、手工插入数据库,番号增加,则需把redis做相应增加
手工造数据到数据库,则主键ID序列需要做相应增加

105、grp库、pms库、pcm库中cmm_number表手工增加数据,需要加大该表序列

106、日志库所有表都按照日志时间进行了分区,查询必须带着相应分区字段

107、@Override是伪代码,表示重写,但如果方法名称相同而参数列表不同(返回类型可以相同也可以不同),那么只是方法的重载,而非重写。



不要按照上面的这种写法写了哦

108、根据业务定义初始化数据库连接池,目前约定如下,
每50个酒店左右一个库,在增加到接近酒店数是,新建一套grp_dev、pms_dev、log_dev、stat_dev
所有酒店共享一个pcm_dev、cms_dev、jw_reportools
只有一套练习库,包括:grp_dev、pms_dev、log_dev、stat_dev
只有一套测试库,包括:grp_dev、pms_dev、log_dev、stat_dev
R层工程启动,建立默认数据库连接,并读取现有数据库连接配置,建立所有数据库连接
默认数据库连接:
maxActive=20【池最大20个链接】initialSize=1 【初始一个链接】minIdle=1 【池最小1个链接】
每个plf_ops_dbinfo中配置的业务库连接,配置如下
开发环境 生产环境
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执行前,怕数据库上面查出的数据已经被更改,query.list()进行了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因此,大家在写代码时,请注意,查询出对象能复用就复用,尽量不要多次查询,会有一致性问题

110、检查一下自己发番的KEY用的对不对哦,
别复制过来忘了改,用的其他的流水号!

111、对List、Set、Map在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常,原因调用list.remove()方法导致modCount和expectedModCount的值不一致。注意,像使用for-each进行迭代实际上也会出现这种问题。不要使用Itr类中的remove()方法,虽然在单线程下不会报错,但在多线程下仍会抛错,因此建议用并发容器CopyOnWriteArrayList和ConcurrentHashMap代替ArrayList和Vector和HashMap。
CopyOnWriteArrayList注意事项
(1) CopyOnWriteArrayList不能使用Iterator.remove()进行删除。(2) CopyOnWriteArrayList使用Iterator且使用List.remove(Object);会出现如下异常: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(),用于上线后,复杂业务方便跟踪,快速有效解决问题。

113、根据下述索引建议,优化SQL语句
(1)负向条件查询不能使用索引select * from order where status!=0 and stauts!=1not in/not exists都不是好习惯可以优化为in查询:select * from order where status in(2,3)
(2)前导模糊查询不能使用索引select * from order where desc like '%XX'而非前导模糊查询则可以:select * from order where desc like 'XX%'
(3)数据区分度不大的字段不宜使用索引select * from user where sex=1原因:性别只有男,女,每次过滤掉的数据很少,不宜使用索引。
PS:特别说明一下区分度1: 重复度越高,区分度越小,索引效果越不好2: 重复度越低,区分度越高,索引效果越好,但带来的影响也越大--增删改变慢,并间接影响查询速度.惯用手法: 截取不同长度,并测试其区分度select count(distinct left(word,6))/count(*) from dict;截取word字段长度,从1开始截取,计算字符前缀没有重复的字符占全部数据的比例对于一般的系统应用: 区别度能达到0.1-0.2,索引的性能就可以接受
(4)在属性上进行计算不能命中索引select * from order where YEAR(date) < = '2017'即使date上建立了索引,也会全表扫描,可优化为值计算:select * from order where date < = CURDATE()或者:select * from order where date < = '2017-01-01'
(5)如果业务大部分是单条查询,使用Hash索引性能更好,例如用户中心select * from user where uid=?select * from user where login_name=?原因:B-Tree索引的时间复杂度是O(log(n))Hash索引的时间复杂度是O(1)
(6)允许为null的列,查询有潜在大坑单列索引不存null值,复合索引不存全为null的值,如果列允许为null,可能会得到“不符合预期”的结果集select * from user where name != 'shenjian'如果name允许为null,索引不存储null值,结果集中不会包含这些记录。
(7)复合索引最左前缀,并不是值SQL语句的where顺序要和复合索引一致用户中心建立了(login_name, passwd)的复合索引select * from user where login_name=? and passwd=?select * from user where passwd=? and login_name=?都能够命中索引select * from user where login_name=?也能命中索引,满足复合索引最左前缀select * from user where passwd=?不能命中索引,不满足复合索引最左前缀
PS:多说一下联合索引联合索引能够满足最左侧查询需求,例如(a, b, c)三列的联合索引,能够加速a | (a, b) | (a, b, c) 三组查询需求。这也就是为何不建立(passwd, login_name)这样联合索引的原因,业务上几乎没有passwd的单条件查询需求,而有很多login_name的单条件查询需求。最左前缀:顾名思义,就是最左优先,上例中我们创建了a_b_c多列索引,相当于创建了(a)单列索引,(a,b)组合索引以及(a,b,c)组合索引。 a ab abc标准写法 ba bac bca cab cba acb这五种引擎会自动优化成ab和abc的查询顺序查索引。
(8)使用ENUM而不是字符串ENUM保存的是TINYINT,别在枚举中搞一些“中国”“北京”“技术部”这样的字符串,字符串空间又大,效率又低。
(9)如果明确知道只有一条结果返回,limit 1能够提高效率select * from user where login_name=?可以优化为:select * from user where login_name=? limit 1原因:你知道只有一条结果,但数据库并不知道,明确告诉它,让它主动停止游标移动
(10)把计算放到业务层而不是数据库层,除了节省数据的CPU,还有意想不到的查询缓存优化效果select * from order where date < = CURDATE()这不是一个好的SQL实践,应该优化为:$curDate = date('Y-m-d');$res = mysql_query('select * from order where date < = $curDate');原因:释放了数据库的CPU多次调用,传入的SQL相同,才可以利用查询缓存
(11)强制类型转换会全表扫描select * from user where phone=13800001234不会命中索引

114、业务表为Date的字段,在做equals比较时要尤其注意
entity.getField().equals(java.util.Date),是无法比较的
原因:从数据库里查询的这个时间字段的类型,已经被Hibernate默认转化为:java.sql.Timestamp类型,同java.util.Date类型比较会由于类型不一致,而直接返回false



建议:
1、都转成String 再比较
2、Timestamp转成Date
3、Date转成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
以减少类反射的次数,大家用到相应类反射地方,可以参考

116、每次上传后要统计代码数量,供品质量化使用
一段时间内的差分代码行数统计【统计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 }'



一段时间内的新归代码行数统计自己根据近期新增文件统计
一段时间内的差分代码母代码行数统计【修改代码总行数】先打印某人某段时间提交文件查看行数命令git log --author="bwliu" --since=1.day.ago --pretty=tformat: --name-status | gawk '{print "wc -l """"D:/_git/jw-source/"""$2}'



然后执行打印出来的查看条数的脚本

117、通过ID查名字的,都统一用如下接口


118、正式环境无感知迭代发布流程梳理如下:
1、导流把需要发布的集中中一个点的流量导到其他节点中去2、暂停一分钟后,停前后端服务3、更新此次迭代的最新数据库结构,缓存结构及相应数据4、发布后端程序5、发布前端程序6、启动前后端服务7、测试后,流量导回到新发布服务上8、其他服务重复上述过程
灰度发布流程梳理如下:1、导流把需要发布的需调整灰度策略(目前可以根据请求头上集团UID和酒店UID进行切换)节点的流量导到其他节点中去2、暂停一分钟后,停前后端服务3、更新此次迭代的最新数据库结构,缓存结构及相应数据4、发布后端程序5、发布前端程序6、启动前后端服务7、测试后,流量导回到新发布服务上
119、正式环境GIT开发、测试、发布流程


120、query.list()的FlushMode默认为AUTO,当session设置为FlushMode.AUTO时,hibernate在进行查询的时候会判断缓存中的数据是否为脏数据,是则刷数据库,不是则不刷
Hibernate session FlushMode有五种属性:1、NEVEL:已经废弃了,被MANUAL取代了2、MANUAL:如果FlushMode是MANUAL或NEVEL,在操作过程中hibernate会将事务设置为readonly,所以在增加、删除或修改操作过程中会出现如下错误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;解决办法:配置事务,spring会读取事务中的各种配置来覆盖hibernate的session中的FlushMode3、AUTO设置成auto之后,当程序进行查询、提交事务或者调用session.flush()的时候,都会使缓存和数据库进行同步,也就是刷新数据库4、COMMIT提交事务或者session.flush()时,刷新数据库;查询不刷新5、ALWAYS:每次进行查询、提交事务、session.flush()的时候都会刷数据库
ALWAYS和AUTO的区别:当hibernate缓存中的对象被改动之后,会被标记为脏数据(即与数据库不同步了)。当 session设置为FlushMode.AUTO时,hibernate在进行查询的时候会判断缓存中的数据是否为脏数据,是则刷数据库,不是则不刷,而always是直接刷新,不进行任何判断。很显然auto比always要高效得多。

121、所有dto和entity的描述和name要标示正确
fieldnm是字段名,后面是中文描述这边入ES转化,要用这个的要不就转化乱了

122、postgresql正则表达式使用:

操作符

描述

例子

~
匹配正则表达式,大小写相关
'thomas' ~ '.thomas.'

~*
匹配正则表达式,大小写无关
'thomas' ~* '.Thomas.'

!~
不匹配正则表达式,大小写相关
'thomas' !~ '.Thomas.'

!~*
不匹配正则表达式,大小写无关
'thomas' !~* '.vadim.'

正则表达式原子

原子

描述

(re)
(这里的 re 是任何正则表达式) 匹配一个对 re 的匹配,有可报告的匹配信息

(?:re)
同上,但是匹配不会被报告 (一个"不捕获"圆括弧) (只在 ARE 中有)

.
匹配任意单个字符

[chars]
一个 方括弧表达式, 匹配任意的字符(参阅 Section 9.7.3.2 获取更多细节)

*k*
(这里的 k 是非字母数字字符) 匹配一个当作普通字符看待的特定字符, 比如,\ 匹配一个反斜杠

*c*
这里的 c 是一个字母数字 (可能跟着其它字符),它是一个逃逸, 参阅 Section 9.7.3.3(仅存在于 ARE; 在 ERE 和 BRE 中,它匹配 c

{
如果后面跟着一个字符,而不是数字, 那么就匹配左花括弧{;如果跟着一个数字, 那么它是范围的开始(见下面)

x
这里的 x 是一个没有其它特征的单个字符, 则匹配该字符

正则表达式量词

量词

匹配

一个匹配 0 或者更多个原子的序列

一个匹配 1 或者更多个原子的序列

?
一个匹配 0 个或者 1 个原子的序列

{m}
一个正好匹配 m 个原子的序列

{m,}
一个匹配m 个或者更多原子的序列

{m,n}
一个匹配 mn 个(包含两端) 原子的序列;m 不能比 n

*?

  • 的非贪婪模式

+?

  • 的非贪婪模式

??
? 的非贪婪模式

{m}?
{m} 的非贪婪模式

{m,}?
{m,} 的非贪婪模式

{m,n}?
{m,n} 的非贪婪模式

正则表达式约束

约束

描述

^
匹配字串的开头

$
匹配字串的结尾

(?=re)
正前瞻 匹配任何匹配 re 的 子字串起始点(只在 ARE 中有)

(?!re)
负前瞻 匹配任何不匹配 re 的子字串的起始点。(只在 ARE 中有)

上面只列出正则表达式的一些基础知识,正则表达式的检索能力相当强大,特别是对于类型为Text类型,并且存储的是XML数据时,正则表达式的优势将更能发挥作用。
关于更详细的信息可以参考文档http://www.postgresql.org/docs/9.0/static/functions-matching.html
举例:SELECT * FROM ind_dtofield where fieldnm !~* '[a-z]'

123、DTO的注解说明中,应为界面上的名称【参照界面填写】


124、完善DTO中map的key值描述信息



125、完善DTO中字典属性的注解说明


126、完善DTO中特殊信息的特殊标记说明


127、 语种以前用的zh(中文)和en(英文),现在改为zh-CN(中文简体:1220)和en-US(英语(美国:1056)),日语用ja-JP(日语(日本:1120))。前端传的字符串应该是小写,数据库是大小写均有,写SQL时注意转换

128、共同参数、集团酒店餐饮参数、系统参数处室话到缓存中的格式为:
共同参数:类型语种.param_id.param_drpt
集团酒店餐饮参数:类型
语种.attribute_vlu.attribute_drpt
系统参数:类型_语种_master库名.param_id.param_drpt【从库查询,得把从threadlocal中获取的库名转化为主库的】
上述红字部分为key值,后面为map
设置方式:
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库不能被覆盖,仅供开发用
已使用的库,也不能被模板库覆盖
练习库和测试库,可以被本身的正式库覆盖
新建库的发番配置数据要完整【韩松负责】

130、postgresql连接到schema【jdbc:postgresql://postgreSlave:5432/pcm_dev?characterEncoding=utf8&currentSchema=pcm_dev
参见:https://jdbc.postgresql.org/documentation/head/connect.html

一个数据库包含一个或多个命名的模式,模式又包含表。模式还包含其它命名的对象,包括数据类型、函数,以及操作符。同一个对象名可以在不同的模式里使用而不会导致冲突; 比如,schema1和myschema都可以包含叫做mytable的表。和数据库不同,模式不是严格分离的:一个用户可以访问他所连接的数据库中的任意模式中的对象,只要他有权限。    我们需要模式有以下几个主要原因:    1). 允许多个用户使用一个数据库而不会干扰其它用户。    2). 把数据库对象组织成逻辑组,让它们更便于管理。    3). 第三方的应用可以放在不同的模式中,这样它们就不会和其它对象的名字冲突。
我们在使用一个数据库对象时可以使用它的全称来定位对象,然而这样做往往也是非常繁琐的,每次都不得不键入owner_name.object_name。PostgreSQL中提供了模式搜索路径,这有些类似于Linux中的$PATH环境变量,当我们执行一个Shell命令时,只有该命令位于$PATH的目录列表中,我们才可以通过命令名直接执行,否则就需要输入它的全路径名。PostgreSQL同样也通过查找一个搜索路径来判断一个表究竟是哪个表,这个路径是一个需要查找的模式列表。在搜索路径里找到的第一个表将被当作选定的表。如果在搜索路径中 没有匹配表,那么就报告一个错误,即使匹配表的名字在数据库其它的模式中存在也如此。    在搜索路径中的第一个模式叫做当前模式。除了是搜索的第一个模式之外,它还是在CREATE TABLE没有声明模式名的时候,新建表所属于的模式。要显示当前搜索路径,使用下面的命令:


131、不允许出现DTO嵌套自己的情况出现【除特殊需要外】



132、热部署JRebel的spring监控插件,对于spring-data的监控有问题,请大家在开发环境启动参数配置中关掉,如下图所示:


133、防止NPE【NullPointerException】,是调用者的责任,注意NPE产生的场景:
1)返回类型为基本数据类型,return包装数据类型的对象时,自动拆箱有可能产生NPE。
反例:public int f() { return Integer对象}, 如果为null,自动解箱抛NPE。
2) 数据库的查询结果可能为null。
3) 集合里的元素即使isNotEmpty,取出的数据元素也可能为null。
4) 远程调用返回对象时,一律要求进行空指针判断,防止NPE。
5) 对于Session中获取的数据,建议NPE检查,避免空指针。
6) 级联调用obj.getA().getB().getC();一连串调用,易产生NPE。
正例:使用JDK8的Optional类来防止NPE问题。
【Optional的代码相对更加简洁,当代码量较大时,我们很容易忘记进行null判定,但是使用Optional类则会避免这类问题。】
例子:return str.length(); 改为: return Optional.ofNullable(str).map(String::length).orElse(0);
例子:Optional<String> optStr = Optional.ofNullable(str); // 如果str是null,则创建一个空对象
例子:【手机和邮箱不是一个人的必须有的,所以我们利用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类,未实现任何接口,所以当我们在利用该类包装定义类的属性的时候,如果我们定义的类有序列化的需求,那么因为Optional没有实现Serializable接口,这个时候执行序列化操作就会有问题
private long phone;
public Optional<Long> getPhone() {return Optional.ofNullable(this.phone);}
}
例子:【映射:map与flatMap】【映射是将输入转换成另外一种形式的输出的操作】
String name = Optional.ofNullable(user).map(User::getName).orElse("no name");因为map之后返回的是Optional,我们把这种称为Optional嵌套,我们必须在map一次才能拿到我们想要的结果:long phone = optUser.map(User::getPhone).map(Optional::get).orElse(-1L);其实这个时候,更好的方式是利用flatMap,一步拿到我们想要的结果:long phone = optUser.flatMap(User::getPhone).orElse(-1L);
例子:【过滤:fliter 】【可以将过滤操作做为参数传递给该方法,从而实现过滤目的】
optUser.filter(u -> u.getAge() >= 18).ifPresent(u -> System.out.println("Adult:" + u));

134、避免出现重复的代码(Don’t Repeat Yourself),即DRY原则。
随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是组件化。 正例:一个类中有多个public方法,都需要进行数行相同的参数校验操作,这个时候请抽取:private boolean checkParam(DTO dto) {...}
135、编写单元测试代码遵守BCDE原则,以保证被测试模块的交付质量。
B:Border,边界值测试,包括循环、特殊取时间点数据顺序等。
C:Correct,正确的输入,并得到预期结果。
D:Design,与设计文档相结合,来编写单元测试。
E:Error,强制错误信息输入(如:非法数据、异常流程业务允许等),并得到预期的结果。

136、为了更方便地进行单元测试,业务代码应避免以下情况:
a) 构造方法中做的事情过多。
b) 存在过多的全局 变量 和静态方法。
c) 存在过多的外部依赖。
d) 存在过多的条件语句。
说明: 多层条件语句建议使用卫、策略模式状态等方重构。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容