快捷记忆

JAVA

1.数字格式化

DecimalFormat format = new DecimalFormat("000");

2.Logger日志输出以这种方式

logger.info("=== 消息队列接收信息 : {}", msg);

3.Jackson 高性能的JSON处理 ObjectMapper

可以把json转换成各种对象,各种对象转json,可以用于消息队列的传递

前端传一个map,map的一个key对应的value是List对象,后台拿到map怎么转

1
2
3
private ObjectMapper mapper = new ObjectMapper();
JavaType cjavaType = mapper.getTypeFactory().constructParametricType(ArrayList.class, Classify.class);
classifies = mapper.readValue(mapper.writeValueAsString(classifies),cjavaType);

4.线程安全队列

一个线程安全的队列实现方式有两种:1是使用阻塞方法、2是使用非阻塞。阻塞就是加锁,非阻塞采用循环CAS的方式实现

1
ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。

5.SpringBoot定时任务

定时任务:@Scheduled \
fixedRate 任务两次执行时间间隔是任务的开始点 \
fixedDelay 的间隔是前次任务的结束与下次任务的开始 \
cron 表达式

6.RandomAccessFiles随机流

针对文件内容进行操作,完成随机读取功能,可以读取指定位置的内容

7.求日期相差天数

1
int day = (int) ((x.getDeadline().getTime() - new Date().getTime()) / (1000 * 3600 * 24));

8.排序

list.sort(Comparator.comparing(x->x.getId));

9.SpringBoot项目打成jar后获取classpath下文件

1
2
3
ClassPathResource resource = new ClassPathResource("application.yml");
InputStream inputStream = resource.getInputStream();
File file = new File(inputStream);

ModelMapper:对象与DTO转换

1
IssueDTO issueDTO = modelMapper.map(issue,IssueDTO.class);

ResponseEntity:封装返回的HttpStatus,可定义

bootstrap 和 application 的区别

和application 配置文件相比,bootstrap 配置文件具有以下几个特性:

  • bootstrap 由父ApplicationContext加载,比application优先加载;

  • bootstrap里面的属性不能被覆盖

开启线程

1)extends Thread重写run方法
2)implements Runabled实现run方法,.start()开启

悲观锁与乐观锁

  • 悲观锁

总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁

  • 乐观锁

总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号version机制或CAS操作实现。

Java transient关键字

java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。序列化指的是通过实现Serializable接口后的自动序列化

  • 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。

  • transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。

  • 被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化

String的几种类型

  • String 字符串常量,不可变

  • StringBuffer 字符串变量(线程安全),可变

  • StringBuilder 字符串变量(非线程安全),可变

Integer是不可变类,进入一个方法后,在里面的值的改变不会影响方法外的引用

基本类型的变体引用类型和String都不能直接当纯引用类型来用,比如Integer,Double等都是int与double的引用类型,但是你不能像普通引用类型那样直接对他的值做改变,因为在他们里面封装的原始int与double都用了final进行声明。所以你就算重新赋值了原始的int与double都不会改变

RxJava异步框架

相当于观察者模式,异步线程去执行代码,可以使用该异步框架实现:

1、失败重试机制(当短时间内,比如发消息的方式,发生大量的数据库操作,可能导致数据库被锁而执行失败)
2、异步执行
3、观察者模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Observable.just(stateMachineId)
.map(t -> {
stateMachineService.deploy(organizationId, stateMachine.getId(), false);
return t;
})
.retryWhen(x -> x.zipWith(Observable.range(1, 10),
(t, retryCount) -> {
if (retryCount >= 10) {
logger.warn("error.stateMachine.deploy.error,stateMachineId:{}", stateMachineId);
}
return retryCount;
}).flatMap(y -> Observable.timer(2, TimeUnit.SECONDS)))
.subscribeOn(Schedulers.io())
.subscribe((Long id) -> {
});

Spring工具类

  • AntPathMatcher:路径匹配工具

  • ApplicationContextHelper:获取注入的对象

1
ApplicationContextHelper.getSpringFactory().getBean(xx.class)

Java泛型

泛型:即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)

在泛型接口、泛型类和泛型方法的定义过程中,我们常见的如T、E、K、V等形式的参数常用于表示泛型形参,由于接收来自外部使用时候传入的类型实参。泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型(即class是同一个,泛型参数在编译期会被擦除)

  • 上限与下限
1
2
<? extends T>表示该通配符所代表的类型是T类型的子类
<? super T>表示该通配符所代表的类型是T类型的父类

参考文献:Java总结篇系列:Java泛型

在方法上加final

该方法无法被重写或覆盖

好用的java类库

  • guava:谷歌开源的java库,提供用于集合,缓存,支持原语,并发性,常见注解,字符串处理,I/O和验证的实用方法

Guava教程

代码规范

Spring中用构造注入方式替代@Autowired

Java序列化的三种方式

  • 实现Serializable接口(隐式序列化):自动序列化非static和非transient的成员变量
  • 实现Externalizable接口(显式序列化):Externalizable接口继承自Serializable, 我们在实现该接口时,必须实现writeExternal()和readExternal()方法
  • 实现Serializable接口+添加writeObject()readObject()方法。(显+隐序列化):添加writeObject()和readObject()方法,方法必须要被private修饰,第一行调用默认的defaultRead/WriteObject();

java文本转义

URLDecoder.decode(text,"UTF-8")

java执行命令行语句

1
2
String [] cmd={"cmd","/C","copy exe1 exe2"}; 
Process proc =Runtime.getRuntime().exec(cmd);

git

1.想要查看最新的远程分支必须先pull新的master

2.查看远程仓库地址

git remote -v

3.git缓存区

  • git stash save xxx 保存stash有名字
  • git stash clear 清空缓存
  • git stash list 缓存列表
  • git stash apply stash@{0} 应用缓存

4.版本回退

1
2
3
4
git log
git reset --hard HEAD^ //删除工作空间改动代码,撤销commit,撤销git add
git reset --soft HEAD^ //撤回commit,代码仍然在add中
git reset --mixed HEAD^ //不删除工作空间改动代码,撤销commit,并且撤销git add

5.分支强行替换名字

1
2
3
git branch -m master old-master
git branch -m develop master
git push -f origin master

在新master直接push时会提示当前分支的引用并非与服务器上的master相同,执行它提示的命令即可更换新的引用

6.rebase 变基

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
git rebase
把一个分支中的修改整合到另一个分支的办法有两种: merge 和 rebase 。 rebase 可以把在一个分支里提交的改变移到另一个分支里重放一遍。
rebase 的操作不当,可能导致原分支被修改等后果。作为一个危险操作,需要慎重。
变基操作解决冲突后应执行 git add . 和 git rebase --continue ,而不是 git commit 命令
A分支切出来一个B分支,当前在B分支,已经做了两个提交,切回A分支也做了两个提交,再切回B分支
# B分支合并A分支的提交记录
git rebase A
# 处理完冲突继续合并
git add .
git rebase –continue
# 跳过
git rebase –skip
# 取消合并
git rebase –abort
# 最后再切回A分支,A merge B,这样A中的提交记录就是整洁的了(没有merge记录)

若出现重复的提交记录,可以在推到仓库的自己分支增加–force强推,覆盖自己之前push的记录

git忽略提交

  • 在项目根目录,创建.gitignore(该文件会传至仓库与大家共享),配置忽略的文件

需要让gitignore生效,需要git rm -r --cached . // 删除本地缓存

  • 在项目下,.git/info/exclude文件中添加本地提交要忽略的文件,不与仓库共享

根据tag切分支

1
git branch <new-branch-name> <tag-name>

删除tag

1
2
git tag -d [tag];
git push origin :[tag]

git初始化仓库

1
2
git remote add origin git@github.com:chenshinan/111.git
git push -u origin master

常用命令

1
2
3
4
git branch -a #查看本地和远程所有的分支
git branch -r #查看所有远程分支
git fetch #将本地分支与远程保持同步
git fetch -p 获取远程仓库的新分支以及删除远程仓库已删除的分支

sql

根据日期拉取报表

SELECT COUNT(*), DATE(CreateTime) FROM event_order WHERE YEAR(CreateTime) = '2016' GROUP BY DAY(CreateTime) \
详细可见:http://blog.csdn.net/jiang_2992/article/details/73801366

拼接、截取、分组拼接

拼接:CONCAT('yykj',code) \
截取:right(str,index)、substring(str,index) \
分组拼接:select name,group_concat(id) from tab group by name 结果:小明 | 1,3,5

连接查询

  • left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
  • right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
  • inner join(等值连接) 只返回两个表中联结字段相等的行

给用户添加数据库权限

grant all privileges on state_machine_service.* to 'choerodon'@'%';

删除表、清空表

存在则删除:drop table if exists 表名 \
删除表:delete from 表名 \
清空表:truncate table 表名

mysql workbench 的安全模式

SET SQL_SAFE_UPDATES = 0;

SQL最后一行求总和

1
2
3
4
5
SELECT
IFNULL(u.status, '合计') 状态, COUNT(*)
FROM
user u
GROUP BY u.status WITH ROLLUP

ALTER语句

添加列:
ALTER TABLE test ADD (c1 char(1),c2 char(1));
ALTER TABLE test ADD COLUMN (c1 char(1),c2 char(1));
ALTER TABLE test ADD c1 char(1),ADD c2 char(1);
修改列:
ALTER TABLE test CHANGE c1 c3 char(1),CHANGE c2 c4 char(1);
ALTER TABLE test CHANGE COLUMN c1 c3 char(1),CHANGE COLUMN c2 c4 char(1);
删除列:
ALTER TABLE test DROP c1,drop c2;
ALTER TABLE test DROP COLUMN c1,DROP COLUMN c2;

对于获取组织和组织的用户人数,推荐的优化方式是先通过groupby用户,再join组织表

select for update

概念:该语句用来锁定特定的行(如果有where子句,就是满足where条件的那些行)。当这些行被锁定后,其他会话可以选择这些行,但不能更改或删除这些行,直到该语句的事务被commit语句或rollback语句结束为止,防止select之后,update之前,数据已经发送改变导致再update出现错误数据

在select…for update之后,可以使用of子句选择对select的特定数据表进行加锁操作。默认情况下,不使用of子句表示在select所有的数据表中加锁。

更新判断语句

1
2
3
4
5
6
7
update agile_issue
set apply_type = (
case when type_code IN
('task','bug','sub_task','story','issue_epic')
then 'agile'
else 'test' end
)

insert into select(查询到的数据插入)

1
2
Insert into Table2(field1,field2,...) select value1,value2,... from Table1
Insert into Table2 select * from Table1

insert ignore into(存在则忽略)

order by case 的用法

当进行order by排序时,需要安装特定的顺序排序,则有case ... when ... then ... end的用法

1
2
3
4
5
6
7
8
select id,name from priority
order by
CASE name
when '高' then 1
when '中' then 2
when '低' then 3
else 0
END

update + select

1
2
3
update kb_page_content,kb_page
set kb_page_content.title= kb_page.title
where kb_page.id = kb_page_content.PAGE_ID
1
2
3
update kb_page_content pc set pc.title= (
select p.title from kb_page p where p.id = pc.PAGE_ID
)

mysql用命令运行大sql文件

1
2
3
4
5
6
# 进入mysql
mysql -u ${数据库用户名} -p
# 切换数据库
use ${数据库名}
# 运行sql文件
source ${文件位置/文件名}

sql正则表达式

context可以选多值,用“,”分隔,sql可以用REGEXP匹配,注意使用的是${xx}

1
2
select * from field
where context REGEXP '^${context},|,${context},|^${context}$|,${context}$'

数据库连接池大小的设置

连接数 = ((核心数 * 2) + 有效磁盘数)

如果说你的服务器CPU是4核i7的,连接池大小应该为((4*2)+1)=9,即使有 10000 个并发访问,同样只需要10个线程这样数据库的吞吐量最高。参考文献

mybatis

循环数组判断为空

1
2
3
4
5
6
<if test="userIds != null and userIds.size()>0">
AND mm.user_id IN
<foreach item="item" index="index" collection="userIds" open="(" separator="," close=")">
#{item}
</foreach>
</if>

if other

1
2
3
4
5
6
7
8
<choose>
<when test="searchDTO.otherArgs.resolution == 'true'">
and ais.is_completed = 1
</when>
<otherwise>
and ais.is_completed = 0
</otherwise>
</choose>

resultMap的用法

  • association:一对一/多对一

  • collection:一对多/多对多

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<resultMap id="boardDependInfo" type="io.choerodon.agile.api.dto.BoardDependInfoDTO">
<id property="id" column="id"/>
<id property="boardFeatureId" column="board_feature_id"/>
<association property="boardFeature" columnPrefix="origin_" javaType="io.choerodon.agile.api.dto.BoardFeatureInfoDTO">
<id property="id" column="id"/>
<result property="featureId" column="feature_id"/>
<result property="sprintId" column="sprint_id"/>
<result property="piId" column="pi_id"/>
<result property="teamProjectId" column="team_project_id"/>
<result property="rank" column="rank"/>
<result property="programId" column="program_id"/>
</association>
<collection property="subBugDOList" column="issue_id"
ofType="io.choerodon.agile.infra.dataobject.IssueDetailDO"
select="querySubBugByIssueId"/>
</resultMap>

mybatis的缓存

一级缓存:是SQlSession级别的缓存。在操作数据库时需要构造 SqlSession 对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的 SqlSession 之间的缓存数据区域(HashMap)是互相不影响的

二级缓存:是mapper级别的缓存,多个 SqlSession 去操作同一个mapper的sql语句,多个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear

工作机制:

  • 一个会话, 查询一条数据, 这个数据就会被保存在当前会话的一次缓存(SqlSession)中

  • 如果关闭会话, 那么一次缓存中的数据就会被保存到二级缓存(namespace)中,新的会话查询的内容, 就可以参照二级缓存

  • 一个 xxxMapper 对应一个 namespace, 不同的 namespace 查出的数据会保存在自己对应的二级缓存中

  • 查出的数据会先被保存在一级缓存中, 只有会话关闭或者提交之后, 以及缓存中的数据才会被转移到二级缓存

Mybatis中$与#的区别

#是将传入的值当做字符串的形式,$是将传入的数据直接显示生成sql语句

docker

删除未启动的容器:docker rm $(docker ps -a -q)

运行docker-compose:$ docker-compose up -d(在docker-compose.yml所在目录,-d是指后台运行)

复制容器中的文件到宿主机

1
docker cp 71e53ddad89f:/usr/local/tomcat/bin/catalina.sh tomcat-conf/

查看容器中运行的进程信息

1
docker top mymysql

maven

查看依赖树

1
mvn dependency:tree >> /tree.txt;

包依赖分析插件:Maven Helper

在IDEA中安装插件:Maven Helper,安装后即可在pom.xml中查看包依赖

image

dockerfile

安装vim

1
RUN apt-get update && apt-get install vim -y

mysqldump的使用

  • 备份命令:mysqldump -h主机名 -P端口 -u用户名 -p密码 –database 数据库名 > 文件名.sql
  • 备份压缩:mysqldump -h主机名 -P端口 -u用户名 -p密码 –database 数据库名 | gzip > 文件名.sql.gz
  • 同时备份多个库:mysqldump -h主机名 -P端口 -u用户名 -p密码 –databases 数据库名1 数据库名2 数据库名3 > 文件名.sql
  • 备份实例上所有的数据库:mysqldump -h主机名 -P端口 -u用户名 -p密码 –all-databases > 文件名.sql
  • 备份数据库结构,不备份数据:mysqldump -h主机名 -P端口 -u用户名 -p密码 –no-data 数据库名1 数据库名2 数据库名3 > 文件名.sql

Linux

1
2
3
4
5
6
lsof -i tcp:6479  查看端口号对应的进程
kill -9 pid 杀死pid的进程
ps -A | grep redis 搜索进程名
ps -ef | grep redis 搜索进程名,并显示所有信息,-e显示所有进程,-f全格式
tail -n 1000 error.log | grep -10 hello 搜索带有hello关键词并显示关键字所在行及上下各3行的内容
curl cip.cc 在命令行中查询当前网络的公网出口IP

其他

IDEA快捷键

  • Alt+ left/right 切换代码视图
  • Alt+ Up/Down 在方法间快速移动定位
  • Ctrl+Shift+Up/Down 整行代码向上/下移动。
  • Ctrl+D 复制行
  • Ctrl+X 删除行
  • 快速展示类的元素: Ctrl + F12
  • 实现接口或抽象类的方法: Ctrl + I
  • 向类中插入getter, setter方法: Alt + Insert
  • 打开接口的实现类: Ctrl + H
  • 导包: alt+enter

工具

  • 热部署工具:JRebel、Spring Loaded、spring-boot-devtools

  • 代码构建工具:maven、apache ant

  • Arthas 是Alibaba开源的Java诊断工具:

1
trace -j io.choerodon.issue.api.service.impl.ProjectConfigServiceImpl queryTransformsByProjectId

excel导入

导入内容包含回车换行问题

10:Line Feed (馈行)、11:Vertical Tab (垂直制表)、12:Form Feed (馈页)、13:Carriage Return (回车),此处换行字符需要

1
2
3
for(int i=10;i<14;i++){
str = str.replaceAll(String.valueOf((char)i), "\\\\n");
}

获取时间为数值问题

方法1:获取后处理

1
2
3
Calendar c = new GregorianCalendar(1900,0,-1);
Date d = c.getTime();
Date date = DateUtils.addDays(d, 42605); //42605是距离1900年1月1日的天数

方法2:获取时处理

1
2
3
4
5
6
7
8
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
Date theDate = cell.getDateCellValue();
ret = simpleDateFormat.format(theDate);
} else {
ret = NumberToTextConverter.toText(cell.getNumericCellValue());
}
break;

获取百分比

1
2
3
4
5
6
7
8
9
10
11
cell.setCellType(Cell.CELL_TYPE_NUMERIC);
String v="";
if(cell.getNumericCellValue()==0){
v="0";
}else{
v=String.valueOf(cell.getNumericCellValue()*100);
if(v.endsWith(".0")){
v=v.substring(0, v.length()-2);
}
v=v+'%';
}

导出excel

文本换行

1
2
3
4
5
6
7
8
1.设置自动换行样式
HSSFCellStyle bodyStyle = workbook.createCellStyle();
bodyStyle.setWrapText(true);
2.文本值换行
String strText = "你是乱世的烟火\r\n你是寒夜的湖泊";
3.设置列的值和样式,使用HSSFRichTextString()处理文本值
cellNum.setCellStyle(bodyStyle);
cellNum.setCellValue(new HSSFRichTextString(strText));

Spring security

Spring Security是基于spring的应用程序提供声明式安全保护的安全性框架,它提供了完整的安全性解决方案,能够在web请求级别和方法调用级别 处理身份证验证和授权.它充分使用了依赖注入和面向切面的技术.

Spring security主要是从两个方面解决安全性问题:

  • web请求级别:使用servlet过滤器保护web请求并限制URL级别的访问

  • 方法调用级别:使用Spring AOP保护方法调用,确保具有适当权限的用户采用访问安全保护的方法.

在Spring Security中,认证过程称之为Authentication(验证),指的是建立系统使用者信息( principal )的过程。使用者可以是一个用户、设备、或者其他可以在我们的应用中执行某种操作的其他系统。
Authorization(授权)指的是判断某个 principal 在我们的应用是否允许执行某个操作。在 进行授权判断之前,要求其所要使用到的规则必须在验证过程中已经建立好了。

最基础的对象是 SecurityContextHolder,这是我们在应用中存储当前安全细节的地方,包含了当前在应用中使用到的principal细节。
默认情况下, SecurityContextHolder 使用一个 ThreadLocal 对象来存储这些细节,这表示对于在同一个线程中执行的方法,安全上下文(security context)都是可用的,即使安全上下文没有显式的当做方法的参数进行传递。
通过这种方式使用 ThreadLocal 是很安全的,如果当前使用到的规则需要在请求处理完之后被清空。
当然,Spring Security帮你自动的处理了这些,你不需要考虑。

Spock集成单元测试

  • 重名校验记得取的id即为当前名字的id

  • 调用controller方法失败(格式错误等),entity为空,actRequest为false

  • 调用成功,执行controller方法时抛出异常,未处理的异常,entity返回值非200,actRequest为false

  • 调用成功,执行controller方法时抛出异常,被捕获,entity返回值为200,actRequest为true

  • 在path和param中的参数均有null校验,若传null,调用成功,被spring抛出异常,被捕获,entity返回值为200,actRequest为true

spring基础

bean的作用域

  • singleton:只有一个实例,也即是单例模式
  • prototype:访问一次创建一个实例,相当于new

bootstrap.yml与application.yml

bootstrap.yml先加载application.yml后加载,bootstrap.yml用于应用程序上下文的引导阶段

一旦bootStrap.yml 被加载,则内容不会被覆盖,即便后期加载的application.yml的内容标签与bootstrap的标签一致,application 也不会覆盖bootstrap,而application.yml 里面的内容可以动态替换。