目录
ArrayList<Object> objects = Lists.newArrayList();
HashMap<Object, Object> objectObjectHashMap = Maps.newHashMap();
HashSet<Object> objects1 = Sets.newHashSet();
Map<String, Object> map = new HashMap<>(16);
HashMap<Object, Object> objectObjectHashMap = Maps.newHashMapWithExpectedSize(16);
StringUtils org.apache.commons.lang3
CollectionUtils org.apache.commons.collections4.CollectionUtils
MapUtils org.apache.commons.collections4.CollectionUtils
Collections.emptyList();
Collections.emptySet();
Collections.singletonList();
安全容器
List<SupplierTodoDTO> result = Collections.synchronizedList(new LinkedList<>());
CopyOnWriteArrayList<Object> objects = new CopyOnWriteArrayList<>();
@NotNull(message = "操作人misId不合法") 用于判断基础数据类 对象等
@NotBlank(message = "配置名称不能为空") 判断String
@NotEmpty(message = "配置列表不能为空") 集合类
@Length(min = 1, max = 100, message = "标题不合法") String类型长度
@Range(min = 0, max = 1, message = "force字段不合法") int等的范围
@Valid
@NotNull(message = "分页信息不能为空")
private Page paging;
List<AnnouncementFirstListResponseVO> sortedUnRead = unReadList.stream().map(item -> Pair.of(item.getAnnouncementVO().getId(), item))
.sorted(Comparator.comparing(Pair::getLeft))
.map(Pair::getRight)
.collect(Collectors.toList());
sortedSupplierTodoDTOList = sortedSupplierTodoDTOList.stream()
.filter(Objects::nonNull)
.sorted(Comparator.comparing(SupplierTodoDTO::getOrderId, Comparator.reverseOrder()).thenComparingInt(SupplierTodoDTO::getTaskId))
.collect(Collectors.toList());
1.遇到排查没有结论的问题,首先怀疑主从延迟
2.任何书,要介绍某东西,都是先说认知,背景知识,在说怎么做。
3.在和他人沟通时,要达成一致,统一认知
4.要从全局去思考,比如这次对于成功率的定义,就是成功的/总量,拆的太散显然是不合理的。稍微有变动就会导致成功率不准确。
@Retryable(maxAttempts = 15, backoff = @Backoff(delay = 100L)) @Transactional(rollbackFor = Exception.class)
@Cacheable
https://km.sankuai.com/page/1285988895
并发问题注意:代码逻辑和并发要分开(不要因为并发修改代码逻辑,在逻辑之外加代码去解决技术实现问题)
1.事务注解必须要跨类才能生效,原理是通过aop。@EnableAspectJAutoProxy本类也可以生效,注意必须是public方法,private代理失败
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
((MessageSendServiceImpl)AopContext.currentProxy()).saveTaskAndDetail(batchTask, batchMessageDetails);
@Transactional(rollbackFor = Exception.class)
public void saveTaskAndDetail(BatchTask batchTask, List<BatchMessageDetail> batchMessageDetails) {
batchTaskService.createBatchTask(batchTask);
batchMessageDetailRepository.add(batchMessageDetails);
}
2.注意慢sql,要走索引
3.并发问题:多个线程操作同一份资源就会产生。不要去管这块会不会产生并发问题。要抓住本质就是可能会有多个线程操作同一份资源。杜绝隐藏问题的发生
4.代码逻辑和并发要分开(不要因为并发修改代码逻辑,在逻辑之外加代码去解决技术实现问题)
5.命名问题,代码逻辑性问题,站在业务角度去理解优化代码。
6.要有目标和问题,去思考和检验自己。项目周期各个节点
7.thrift接口记得加@ThriftField注解
8、上线时间变动在群里通知,手头事项安排,不能按照预期完成及时给TL通报
9、重试注解,事务注解启动类
@EnableTransactionManagement(proxyTargetClass = true)
@EnableRetry
类上面的注解
@Retryable(backoff = @Backoff(delay = 100))
https://km.sankuai.com/page/1273326292
事务注解必须要跨类才能生效,原理是通过aop。@EnableAspectJAutoProxy本类也可以生效,注意必须是public方法,private代理失败。
事务本类生效三个要点:1.使用注解@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)开启代理
2.((ResultNotifyServiceImpl) AopContext.currentProxy()) 手动获取当前类的代理
3.必须是public方法,private代理失败。
1.出入参加日志
2.重点:数据库:性能和sql的正确性
3.profile:线上线下哪里应该一致,哪里应该不同
4.新增文件看性能。修改原来的文件看影响 try catch
5.每一个文件,每一行代码都要过到
6.数据库修改 加代码,,出入对称(新增字段)
7.早判断,早结束。避免多层if else循环嵌套。一个if不通过,直接return,避免里面继续嵌套if else
8.if判断常量放前面
9.需要有默认值,默认值以default开头,StringUtils.defaultString(业务逻辑值可能为空,默认值)
10.但凡是状态,必须用枚举
(不要老想着当前场景这样做没有问题。要从整个事的影响来考虑,会不会影响到这个地方。单从目前情况来看,可能没有问题,但是一旦发生问题确实会有影响。比如本次监控大盘更新消息发送结果的时候,之前代码逻辑是new了MessageRecordPO然后去更新,但是更改了逻辑,用了select先从库里查出来。但从功能上来看没有什么问题,但是确实造成了影响,后续迭代发展这里可能埋下隐患)
类名,方法名,常量,异常处理
参数校验尽量提取能抽到一个方法抽取到一个方法中
方法和类上一定要有注释
清楚职能
a调b,b调c,c调d,d调f 特别糟糕
应该是有一个主方法:
a 调 b,c,d,f
提供代码质量:一开始进行类设计的时候没有梳理好,没有想清楚每个类和方法的职能,
就是一条线顺着写下去,a方法调b,b调c,c调d,光想着去实现功能。如何解决:想清楚类的职能,
先用一句话去给同事讲明白这个类要做什么,以及方法是干什么,然后再用三个英文单词做总结提炼。
方法的命名过长可以根据返回值和入参这些信息对命名做精简。想清楚这些,代码总体结构就会好很多。
细节点优化:
1.代码中避免出现魔法字符串,用常量代替
2.代码中的状态码使用枚举类
3.参数校验尽量提到前面,校验不通过早结束
4.使用lambda表达式可以使代码更精简
5.使用Assert做校验
6.bean注入属性值的时候,可以用构造器,避免多个set
7.三元运算符可以使代码更精简
8.注意命名,思考命名的本质,命名是有确切的业务含义的(比如监控大盘打点的目的是为了上报数据,方法名叫上报就比叫打点好很多)
9.避免多个if else嵌套太深。 可以使用三元运算符减少if else。if判断可以一个一个进行判断,一个不通过直接return,避免嵌套太深。
(这里和批量个性化触达需求场景不一样,业务需求导致判断完一个接着判断下面的,判断完所有才return。正常都是校验不通过直接return,不会接着往下判断,不要被此影响)
10.避免重复代码
11.if判断常量放前面,避免可能为空的情况
12.set,map,list等使用工具类去创建,haspmap需要指定初始化大小。
13.避免在定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值。(反例:POJO 类的 createTime 默认值为 new Date(),但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。)
14.包装类和基本数据类型的使用。包装类:所有的 POJO 类属性必须使用包装数据类型。RPC 方法的返回值和参数必须使用包装数据类型。基本数据类型:所有的局部变量使用基本数据类型。详情见3.3
ArrayList<Object> objects = Lists.newArrayList();
HashMap<Object, Object> objectObjectHashMap = Maps.newHashMap();
HashSet<Object> objects1 = Sets.newHashSet();
Map<String, Object> map = new HashMap<>(16);
HashMap<Object, Object> objectObjectHashMap = Maps.newHashMapWithExpectedSize(16);
15.ctime,utime在仓库层赋值(只做sql的插入时间,更新时间,如果业务上确实有用到时间字段,推荐新建一列begin_time等去做业务逻辑)。数据库时间列使用ddatetime类型,程序中使用 java.util.Date 。
1、上线时间变动在群里通知
2、手头事项安排,不能按照预期完成及时给龙哥通报
https://km.sankuai.com/page/1295532911
https://km.sankuai.com/page/306914235
1.https://km.sankuai.com/page/1293004195
2.管控梳理 todo
3.触达任务拆分管控 todo
触达的本质是在什么样的场景下,通过什么通道发送什么内容给什么对象。最后达到什么样的目标
1.从全局整体进行思考,比如消息结果异步通知,crane扫描到达终止状态的数据,发送通知后更新标识为发送过,从技术实现上当然可以,但是不合理。为什么会把已经终态的数据从新扫描出来。
2.现阶段其实学习太多没有呢么重要,重要的是多思考,把事情想明白和透彻。总结和输出文档的时候,把事情想明白说明白的时候都是思考的过程。设计文档的目标,解决什么问题。
3.在消息未进入触达系统之前,由mafka死信保证,消息一定会进入触达系统。在落库到触达系统后,由crane保证消息的状态一直向前推进。调用message成功通知由message触发,调用失败会重试,明确返回失败或者名字管控并且拒绝下发由processor保证。
4.做事一定要有逻辑,1,2,3步,要带着目标和问题
5.spring加载配置类ConfigurationClassParser
6.阿里规约,设计模式,mysql优化,锁,jvm,springcloud,谷粒商城,spring源码
设计模式:https://km.sankuai.com/page/1288809237 mysql:https://km.sankuai.com/page/1214623209
区分度高的放前面。即使是唯一索引,不用根据业务去层级递进,直接区分度高的放前就行
对于有关时间字段的复合索引的设计。比如status ctime 如果status的区分度足够高,是不需要给ctime加索引的。比如status = x唯一个异常状态,一千条数据只会出现一两次,区分度很高就不需要加索引
如果status 的区分度不高,呢么就可以给ctime加索引。比如触达发消息,发送成功的消息状态为2 如果有业务需求需要查询状态为2的数据,就需要加索引
如果选择insert 只设置了部分值,比如id,name,age 只设置name
insert into tb_user (id,name,age) value (null,"张三",null);
选择insertSelective
insert into tb_user (id,name) value (null,"张三"); 主键通常自增
如果age有默认值,设置为默认值,没有默认值允许为null,则为null,不允许为null,还没有设置报错
updateSelective和update同理
update user
<set>
<if test="name != null">
name = #{name,jdbcType=VARCHAR},
</if>
<if test="pwd != null">
pwd = #{pwd,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
update user
set name = #{name,jdbcType=VARCHAR},
pwd = #{pwd,jdbcType=VARCHAR}
where id = #{id,jdbcType=INTEGER}
updateByPrimaryKeySelective会对字段进行判断再更新(动态sql)(如果为Null就忽略更新)
updateByPrimaryKey对你注入的字段全部更新,如果为字段不更新,数据库的值就为默认值。如果更新还设置null 就会报错,即使数据库默认值为null,因为sql检查过不去
insertSelective同理 如果数据库设置非空并设置了默认值,用了insert 插入会报错,但是用insertSelective 插入会是默认值
selective可以实现动态的查询
<select id="countByParam" parameterType="com.meituan.groceryscm.sqm.notification.os.service.adapter.dal4vss.param.QueryAnnouncementListParam"
resultType="java.lang.Integer">
select count(*) from announcement_info a
<where>
<if test="creator !=null and creator !=''">
AND a.creator =#{creator}
</if>
<if test="title !=null and title !='' ">
AND a.title like concat(#{title},'%')
</if>
<if test="receivers !=null and receivers.size() > 0">
AND a.receiver IN
<foreach collection="receivers" item="receiver" open="(" separator="," close=")">
#{receiver}
</foreach>
</if>
<if test="beginTime != null and beginTime > 0 and endTime != null and endTime>0">
and a.begin_time >= #{beginTime,jdbcType=BIGINT}
and #{endTime,jdbcType=BIGINT} >= a.begin_time
</if>
<if test="announcementType != null and announcementType > 0">
and a.announcement_type = #{announcementType,jdbcType=BIGINT}
</if>
<if test="tag != null and tag > 0">
AND a.tag =#{tag}
</if>
AND valid = 1
</where>
</select>
比如根据id查询 name 和age 可以不传为null
可以根据 id 和 name 查询 age为null,任意的组合
目前我们的系统 processor使用mybatis 且是select和update的方式没有使用动态sql。os系统使用mybatis且动态sql的方式
message系统使用 mybatisplus 的方式本质也是动态sql
https://www.cnblogs.com/ityouknow/p/10856177.html
关于基本数据类型与包装数据类型的使用标准如下:
避免在定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值。(反例:POJO 类的 createTime 默认值为 new Date(),但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。)
包装类和基本数据类型的使用。包装类:所有的 POJO 类属性必须使用包装数据类型。RPC 方法的返回值和参数必须使用包装数据类型。基本数据类型:所有的局部变量使用基本数据类型。
说明:POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何 NPE 问题,或者入库检查,都由使用者来保证。
正例:数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。
反例:某业务的交易报表上显示成交总额涨跌情况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调用不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线-。所以包装数据
类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。
https://zhuanlan.zhihu.com/p/366972218
https://km.sankuai.com/page/1071229389
// 获取品退商家
List<Long> refundSupplierByCalculateDateList;
try {
// 强制走主库
ZebraForceMasterHelper.forceMasterInLocalContext();
refundSupplierByCalculateDateList = refundSupplierRepository.searchRefundSupplierByCalculateDate(calculateDate, taskId);
log.info("获取到品退商家:refundSupplierByCalculateDateList:{} size:{}", JsonTool.writeToString(refundSupplierByCalculateDateList), refundSupplierByCalculateDateList.size());
} finally {
ZebraForceMasterHelper.clearLocalContext();
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。