1、数据量大时,分割查询列表,分批查询
List<List<String>> partition = Lists.partition(venderCodeList, 1_000);
for (List<String> perList : partition) {
//查询操作...
}
2、 FIND_IN_SET函数:单个字段中存储列表数据,以逗号分隔,需要匹配其中某些元素时
//示例:province_codes存储S002,S008,S009,S011
select * from commodity_pool_apply_order where FIND_IN_SET('S002',province_codes);
3、Mybatis批量插入
<insert id="batchInsert" parameterType="java.util.List">
insert into t_user(user_name, age, sex, telno, email, address)
values
<foreach collection="userList" item="item" separator=",">
(#{item.userName}, #{item.age}, #{item.sex}, #{item.telno}, #{item.email},
#{item.address})
</foreach>
</insert>
4、Mybatis查询条件是where (a='xxx' and b='yyy') or (a='xxx1' and b='yyy1')
<select id="selectByList" resultMap="BaseResultMap">
select <include refid="Base_Column_List" />
from t_user
where
<foreach collection="list" item="item" separator="or">
(product_code = #{item.productCode} and shop_code = #{item.shopCode})
</foreach>
</select>
5、Mybatis查询条件是 code in (...)
<if test="shipQueryVO.locationCodes != null and shipQueryVO.locationCodes.size>0">
and shop_code in
<foreach collection="shipQueryVO.locationCodes" separator="," open="(" close=")" item="item">
#{item}
</foreach>
</if>
6、Mybatis查询条件有特殊字符,把有特殊字符的语句放在 <![CDATA[ ]]>中,尽量缩小 <![CDATA[ ]]> 的范围
<if test="shipQueryVO.createdTimeStart != null">
<![CDATA[
and created_time >= #{shipQueryVO.createdTimeStart,jdbcType=TIMESTAMP}
]]>
</if>
7、Mybatis insert返回自增主键并设置到实体对象中
<insert id="insertSelective" parameterType="com.xxx.dao.entity.BaseBrandVO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
insert into base_brand values(.....)
</insert>
8、jsqlparser解析sql提取表名
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>4.2</version>
</dependency>
private static Set<String> getTableNames(String sql) {
Statement statement = null;
try {
statement = CCJSqlParserUtil.parse(sql);
} catch (JSQLParserException e) {
throw new ProductCenterQueryException("解析sql语句错误!sql:" + sql);
}
TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
List<String> tableList = tablesNamesFinder.getTableList(statement);
Set<String> tableNames = new HashSet<>();
for (String tableName : tableList) {
//获取去掉“`”的表名
if (tableName.startsWith("`") && tableName.endsWith("`")) {
tableNames.add(tableName.substring(1, tableName.length() - 1));
} else {
tableNames.add(tableName);
}
}
return tableNames;
}
9、MQ消息幂等处理(redis实现方式)
//MqMessageCacheDataUtil类中定义幂等判断逻辑
public static Boolean putMqMessage(String formInstanceId,String messageId){
if(StringUtils.isBlank(formInstanceId) || StringUtils.isBlank(messageId)){
return true;
}
String key = getKey(formInstanceId);
if(redisUtils.hashKey(key,messageId)){
return false;
}
redisUtils.hashPut(key,messageId,"");
redisUtils.expire(key,EXPIRETIME, TimeUnit.SECONDS);
return true;
}
//幂等校验
if(!MqMessageCacheDataUtil.putMqMessage(vo.getFormInstanceId(),vo.getUuId())){
log.info("重复消息,消息已消费:"+vo.getFormInstanceId()+":"+JSON.toJSONString(vo));
return;
}
//通过幂等校验,执行后续业务逻辑
.....
10、lombok使用
//开启链式编程
@Accessors(chain = true)
//创建一个无参构造函数
@NoArgsConstructor
//创建一个含有所有已声明字段属性参数的构造函数
@AllArgsConstructor
//生成toString()方法的实现,将callSuper设置为true,可以将toString的父类实现的输出包含到输出中
@ToString(callSuper = true)
//用于属性,结合类上标注@ToString使用
@ToString.Include(name =“some other name”)
@ToString.Exclude
//生成equals(Object other) 和 hashCode()方法
//默认不考虑父类,通过设置callSuper = true解决;
//默认使用类的所有非static、非transient字段,通过@EqualsAndHashCode.Exclude排除部分字段解决;
@EqualsAndHashCode(callSuper = true)
//用于属性,结合类上标注@EqualsAndHashCode使用
@EqualsAndHashCode.Exclude
@EqualsAndHashCode.Include
11、git使用进阶
# git无痕回退版本
# 1、未提交 已执行git add提交到暂存区,未执行git commit提交到本地仓库
git reset --hard #清空暂存区
# 2、已提交未推送 已执行git commit提交到本地仓库,但未执行git push推送到远程仓库
#git reset 参数[不带参数时保存回退文件内容修改,--hard 不保留回退文件内容修改] [回退commit_id或HEAD^或HEAD~N或远程分支origin/master]
git reset HEAD^ #上一个版本回退(保存回退文件内容修改)
git reset HEAD~N #N个版本回退(保存回退文件内容修改)
git reset <commit_id> #指定版本回退(保存回退文件内容修改)
git reset origin/master #回退到远程最新版本(保存回退文件内容修改)
# 3、已推送 已执行git commit提交到本地仓库,还执行了git push推送到远程仓库
git reset --hard HEAD~N #先本地回退到具体版本
git push -f #再执行强制推送到远程仓库
# git 配置相关
git config --list #查看git配置(常用于查看用户名/邮箱)
git config --global --list #查看git全局配置
git config --local --list #查看git当前仓库配置
git config --global user.name "your_name" #设置用户名(全局配置)
git config --global user.email "your_email" #设置邮箱(全局配置)
git config --local user.name "your_name" #设置用户名(当前仓库)
git config --local user.email "your_email" #设置邮箱(当前仓库)
# git cherry-pick相关使用
# 使用引用日志记录找回因git reset --head <commit_id> ,git push origin master -f,git branch -D <branch_name>丢失的修改内容
git reflog #查看引用日志记录
#根据reflog的列表找到对应的commit_id,再使用cherry-pick恢复对应的内容
git cherry-pick <commit_id>
# 场景:需要把某个分支的某次提交应用到其他分支上去,通过git log查到commitHash,再在需要应用的分支上执行cherry-pick
git cherry-pick <commitHash>
git cherry-pick commit1 commit2 #cherry-pick多次提交
git cherry-pick commit1^..commit2 #cherry-pick连续的几次提交
#cherry-pick遇到冲突时会中断,已cherry-pick的提交会应用到分支上,解决冲突后可继续cherry-pick
git cherry-pick --continue
#cherry-pick遇到冲突时,放弃cherry-pick
git cherry-pick --abort #放弃cherry-pick,退回到cherry-pick前的状态
git cherry-pick --quit #放弃cherry-pick,保留已经cherry-pick的改动
# 暂存当前分支改动的代码,防止切换分支时将当前分支的改动带到其他分支
git stash save "备注内容" #暂存当前分支代码修改内容但不提交
git stash list #查看暂存的列表
git stash pop #提取上一次暂存的代码(stash list也会减少)
git stash drop stash@{n} #删除指定暂存记录
git stash apply stash@{n} #根据暂存编号提取暂存的代码(stash list不会减少)
git stash clear #清除暂存列表
#git revert使用:回退部分历史提交,适用于需要被回退的提交后还有其他提交,此时不能用reset并push -f
#找到需要回退的提交的commitHash
git revert <commitHash> #回退指定历史提交,保留了第一次提交和此次revert的log记录
# git tag相关使用
git tag #查看本地tag列表
git tag V1.0.0 #基于当前代码打一个tag
git push origin V1.0.0 #将tag V1.0.0推送到远程仓库
#修复某tag上的bug并合并到master
git checkout -b bugfix-V1.0.0 V1.0.0 #修复V1.0.0tag上的bug,基于tag创建新分支进行修复
git tag V1.0.1 #修复完V1.0.0的bug后打新tag,一般是先基于修复bug的分支打tag,版本号升级;然后将本分支代码合并到master
git checkout master #切换到master分支
git merge bugfix-V1.0.0 #合并tagbug修复分支到master
git ls-remote --tags origin #列出远程tag列表
git tag -d <tag标签> #删除本地tag
git pull #拉取代码(包括远程tag)
git push origin --delete tag V1.0.0 #删除远程的tag
#查看代码库地址
git remote -v
#修改代码库地址
git remote set-url origin git@gitlab.tuhuyun.cn:shakaijian/pms-bi-server.git
#增加upstream远程库地址
git remote add upstream git@gitlab.tuhuyun.cn:TUHU.YEWU/PURCHASE/pms-bi-server.git
12、Mysql深分页问题
表结构
CREATE TABLE account ( id int(11) NOT NULL AUTO_INCREMENT COMMENT '主键Id', name varchar(255) DEFAULT NULL COMMENT '账户名', balance int(11) DEFAULT NULL COMMENT '余额', create_time datetime NOT NULL COMMENT '创建时间', update_time datetime NOT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (id), KEY idx_name (name), KEY idx_update_time (update_time) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 ROW_FORMAT=REDUNDANT COMMENT='账户表';
深分页现象: limit 100000,10时耗时明显增加
select id,name,balance from account where update_time> '2020-09-19' limit 0,10; -- 0.003s select id,name,balance from account where update_time> '2020-09-19' limit 100000,10; -- 1.969s
分析:
通过普通二级索引树idx_update_time,过滤update_time条件,找到满足条件的记录ID。 通过ID,回到主键索引树,找到满足记录的行,然后取出展示的列(回表) 扫描满足条件的100010行,然后扔掉前100000行,返回。
优化方式一:通过子查询优化(通过二级索引拿到分页的起始id,回表10次)
select id,name,balance FROM account where id >= (select a.id from account a where a.update_time >= '2020-09-19' limit 100000, 1) LIMIT 10; -- 0.038s
优化方式二:INNER JOIN 延迟关联
SELECT acct1.id,acct1.name,acct1.balance FROM account acct1 INNER JOIN (SELECT a.id FROM account a WHERE a.update_time >= '2020-09-19' ORDER BY a.update_time LIMIT 100000, 10) AS acct2 on acct1.id= acct2.id; -- 0.034s
优化方式三:标签记录法(类似于方法一,只不过此处的id是有查询发起者传入)
select id,name,balance FROM account where id > 100000 order by id limit 10;