Java程序猿搬砖笔记(七)

作为码农平时搜集一些小知识点个人认为是个不错的习惯,书上说

好记性不如烂笔头

我想即使是以前忽略或者新get的很简单的东西,自己动手记下来不管如何印象也会更深刻。

Java导入1W条数据耗时超过100秒

项目中导入一个含合并单元格的Excel,1W条数据耗时超过100秒。Excel模板如下:
在这里插入图片描述

数据库设计:
合并单元格部分(框红部分)存入主表,后面的部分存入详细表。
目前代码逻辑:

  • 循环插入主表
  • 然后把主表返回的主键封装到详细表的所需数据,批量插入详细表

问题原因:
导入1W条数据(如果全部未合并单元格)会循环插入主表数据库1W次,然后循环插入从表数据库1W次,效率及其低。
解决方法:

  • 把数据库主键自增改为UUID,通过Java代码生成
    UUID uuid = UUID.randomUUID();
  • 封装主表数据批量插入、封装详细表数据批量插入

修改完成后1.1W条数据第一次12秒,之后大概6秒左右就可以插入完。

Java正则表达式的几种实现方式

  • 方法一
1
Pattern.matches(regex, str);
  • 方法二
1
str.matches(regex);
  • 方法三
1
2
3
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
boolean flag = m.matches();

Matcher中的find()方法可以获取匹配的具体信息

1
2
3
4
5
while(m.find()) {
System.out.println(m.start());
System.out.println(m.end());
System.out.println(m.group());
}

Linux查询进程杀进程命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 查询进程
ps -ef|grep xxx.jar
UID PID PPID C STIME TTY TIME CMD

zzw 14124 13991 0 00:38 pts/0 00:00:00 grep --color=auto dae

# 杀进程命令
kill -9 进程号

# 启动jar包程序
nohup java -jar xxx.jar &
# 远程调试启动
nohup java -jar -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005 kjcgkyg-0.0.1-SNAPSHOT.jar &

# 实时查看输出日志
tail -f nohup.out
# 查看最后1000行日志
tail -n 1000 nohup.out

参考链接1 参考链接2

spring 注解@primary解析

@primary的作用是在多个类同时实现一个接口时,优先选择Bean注入。可以直接在类上使用:

1
2
3
4
5
6
7
8
@Component
@Primary
public class OperaSinger implements Singer {
@Override
public String sing(String lyrics) {
return "I am OperaSinger: "+lyrics;
}
}

可以在配置类的方法中使用,这种方法会被@Qualifier(value = “operaSinger”)注解覆盖:

1
2
3
4
5
6
7
8
9
10
@Configuration
public class CustomerConfiguration {

@Bean
@Primary
public Singer getSinger(){
System.out.println("我是CustomerConfiguration.getSinger!!!");
return new MetalSinger();
}
}

参考链接

@resource可以和@Qualifier有一样的作用

参考链接

MySQL根据多字段查询出重复记录

1
2
3
4
SELECT id FROM t_awards WHERE id IN (
SELECT id FROM t_awards GROUP BY id,award_year,award_product
HAVING COUNT(*) > 1
)

MySQL取整的函数

1
2
3
4
5
6
# 向上取整
SELECT CEIL(1/2); // 1
# 向下取整
SELECT FLOOR(0.6); // 0
# 四舍五入
SELECT ROUND(1.5); // 2

MySQL 8.0忘记密码怎么办

参考链接1参考链接2

MySQL 8.0官网解压版安装详细教程

参考链接

exists和in的区别

  • in引导的子查询只能返回一个字段,exists子查询可以有多个字段
  • exists使用循环的方式,由outer表的记录数决定循环的次数,对于exists的影响最大,所以,外表的记录越小,子查询结果集较大时适用于exists
  • in 先执行子查询,子查询的结果返回去重之后,再执行主查询,所以,子查询的返回结果越少,越适合使用in关键字。

MySQL用一个表更新另一个表

参考链接

git回滚commit的近几次版本

  • 方法一
    回退最近几次提交
    git reset --soft head~
  • 方法二
    git log、git reset --soft commitId
    注意这里是回退commitId之后的操作,commitId不回退
  • 方法三
    git log
    然后用IDEA操作
    注意这里是回退commitId之后的操作,commitId不回退

参考链接1参考链接2

MySQL根据年份和id排序后,取前后五条数据的SQL

在这里插入图片描述
思路:可以先通过连接工具观察,然后一步一步调试写出sql。例子中的2022和199作为参数传过来的前后五条sql如下所示:
前五条sql:

1
2
3
4
5
SELECT id,project_code,project_name
FROM t_project_base_info
WHERE delete_flag = 0 AND ((project_year = 2022 and id > 199) or project_year > 2022)
order by project_year asc,id asc
LIMIT 0,5

后五条sql:

1
2
3
4
5
SELECT id,project_code,project_name
FROM t_project_base_info
WHERE delete_flag = 0 AND ((project_year = 2022 and id < 199) or project_year < 2022)
order by project_year desc,id desc
LIMIT 0,5

MyBatis映射集合,参数传入多个

xml映射文件代码:

1
2
3
4
<collection property="nextList" ofType="com.aspirecn.kjcgkmh.rms.vo.RecommendedReadingVo"
select="selectNextList"
column="{id=id,projectYear=project_year,cgkCompanyCode=cgk_company_code,roleKey=roleKey}">
</collection>

其中,id、project_year、cgk_company_code是当前表的字段,id、projectYear、cgkCompanyCode作为SQL查询参数传入id为selectNextList的SQL。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- 后五条 -->
<select id="selectNextList" parameterType="map" resultType="com.aspirecn.kjcgkmh.rms.vo.RecommendedReadingVo">
SELECT project_code,project_name
FROM t_project_base_info
WHERE delete_flag = 0 and cgk_company_code !=100
<if test="roleKey != null and roleKey != ''">
and cgk_company_code = #{cgkCompanyCode}
</if>
and ((project_year = #{projectYear} and id &lt; #{id}) or project_year &lt; #{projectYear})
order by project_year desc,id desc
LIMIT 0,5
</select>

<!-- 前五条 -->
<select id="selectPreviousList" parameterType="map" resultType="com.aspirecn.kjcgkmh.rms.vo.RecommendedReadingVo">
SELECT project_code,project_name
FROM t_project_base_info
WHERE delete_flag = 0 and cgk_company_code !=100
<if test="roleKey != null and roleKey != ''">
and cgk_company_code = #{cgkCompanyCode}
</if>
and ((project_year = #{projectYear} and id > #{id}) or project_year > #{projectYear})
order by project_year asc,id asc
LIMIT 0,5
</select>

Mapper.java代码:

1
List<RecommendedReadingVo> selectNextList(Map<String,Object> map);

注意,因为接收有多个参数,方法的参数应该为map类型(即使只有一个参数),否则会报错!

Springboot获取配置文件属性的几种方法

参考链接1参考链接2

BeanMap不能进行put操作

1
2
3
BeanMap map = BeanMap.create(req);
// 不能进行put操作,没有效果
map.put("cgkCompanyCode",companyCode);

SpringBoot配置集合(数组)

1
2
3
4
5
6
// yml配置文件中
rolekey:
interfaceAndRms: [13,18]
// Java代码中(:后面的表示若配置文件取不到用默认值)
@Value("${interfaceAndRms:13,18}")
private List<String> interfaceAndRms;

IDEA 搜索Jar包中的类和方法

Double Shift就行了

MySQL替换指定字段中的字符串

MySQL有replace函数
语法:

1
replace (`field_name`,'from_str','to_str')

示例:

1
2
3
4
-- 把t_achievement表的company_name字段中的","的替换为"、"
UPDATE t_achievement
SET company_name = replace(company_name, ',', '、')
WHERE company_name LIKE '%,%'

参考链接

SpringBoot事务学习

1、确保在启动类中添加了@EnableTransactionManagement注解,在seivice中添加@Transactional 注解
Spring底层会设置数据库开启事务、不自动提交,所以加上上面两个注解就可以了。
2、事务回滚
Spring事务默认只在发生未被捕获的RuntimeExcetpion时才回滚。可以捕获到异常后抛出新的RuntimeExcetpion,推荐这种方法。

1
2
3
4
5
6
7
// 插入失败后抛出自定义异常(继承RuntimeExcetpion),事务才能回滚
try {
achievementTagMapper.insertSelective(achievementTag);
} catch (Exception e) {
log.error("AchievementServiceImpl.importAchievement批量插入achievementTag表失败:", e.getMessage());
throw new CommonsException(MessageCode.Insert_Error, e);
}

也可以捕获异常后添加
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();代码
手动回滚,这样上层就无需去处理异常,但是这样接口出错信息返回不到前端了。

service类中不加@Transactional注解,方法A调用方法B
1、注解加在方法A,方法B不加:
①方法A异常(抛出)
方法A中事务回滚,不走方法B
②方法B异常(抛出)
方法A,方法B事务都回滚

2、注解加在方法B,方法A不加:
①方法A异常(抛出)
方法A中事务不回滚,不走方法B
②方法B异常(抛出)
方法A事务不回滚,方法B回滚

service类中加@Transactional注解,相当于所有方法都加了注解

参考链接

查看MySQL事务开启

参考链接

Java替换最后一个字符

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 替换最后一个字符
*
* @param: text 要操作的字符串
* @param: currentStr 最后一个字符串
* @param: targetStr 想替换为的字符串
* @return java.lang.String
* @author liquanhong
* @date 2021/11/1
*/
public static String replaceLast(String text, String currentStr, String targetStr) {
return text.replaceFirst("(?s)" + currentStr + "(?!.*?" + currentStr + ")", targetStr);
}

例子:

1
2
3
 String str = "、1232、43424、、55、、";
// 输出:、1232、43424、、55、
System.out.println(replaceLast(str,"、",""));

Java替换第一个字符

用Java String自带的replaceFirst方法,第一个参数是正则表达式
例子:

1
2
3
String str = "、1232、43424、、55、、";
// 输出:1232、43424、、55、、
System.out.println(str.replaceFirst("、", "" ));

MySQL查询Text字段会非常慢(IO慢),高并发下会特别明显。

列表查询尽量不要查这个字段。可以改为blob数据类型,得代码转换。

bootstrap.yml配置

  • SpringBoot 项目中如果没有依赖spring-cloud-context的话不会读取到bootstrap.properties(yml)文件,bootstrap.yml配置是SpringCloud项目才会用到的。
1
2
3
4
5
6
<!-- spring-cloud-context 加这个才能激活bootstrap.yml配置 注意springBoot的版本不能太高 否则项目起不来-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-context</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
  • bootstrap中的相同配置会被覆盖,bootstrap中的相同配置会被覆盖(亲自测试),所以application中不要配置bootstrap中已有的配置。
  • applicaiton中的配置按优先级,以第一次读取的为准。即优先级从高到低,高优先级的配置覆盖低优先级的配置,所有配置会形成互补配置。
  • 一般服务名和配置中心信息在bootstrp文件中指定。

BeanMap存在很多问题,最好用反射转换对象为Java原生Map

MySQL操作binlog相关命令

  • 查询所有相关日志
    SHOW MASTER logs;
  • 最新使用的日志
    SHOW MASTER status;
  • 查询指定日志文件修改
    SHOW BINLOG EVENTS IN ‘binlog.000014’;
  • 把指定时间开始的binlog导出为sql文件
    mysqlbinlog --no-defaults --start-datetime=“2021-12-08 16:00:00” “D:\Program Files\mysql-8.0.27-winx64\Data\binlog.000014” > D:\export\test1.sql

IDEA有时候修改父项目pom.xml,子项目没有引用到父项目的jar包

解决方法:先看父项目中是否引用到,如果父项目有子项目没有,清理IDEA缓存重启一下(已经这样解决两次了)。

Linux查看端口占用情况

命令:lsof -i:端口号
例子:
查看服务器 8000 端口的占用情况:

1
lsof -i:8000

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nodejs 26993 root 10u IPv4 37999514 0t0 TCP *:8000 (LISTEN)
可以看到 8000 端口已经被轻 nodejs 服务占用。

Java8中list转map如果Key重复的解决方法

若不处理会抛异常
java.lang.IllegalStateException: Duplicate key 11
解决方法:用第二个key覆盖第一个key。
示例代码如下:

1
2
Map<String,String> roleKeyMap = allRoleList.stream()
.collect(Collectors.toMap(Role::getName, Role::getRoleKey, (key1, key2) -> key2));

参考链接

HttpClient的一些方法

1
2
3
4
5
6
7
8
// 发送请求
httpResponse = httpClient.execute(httpPost);
// 获取状态码
int statusCode=httpResponse.getStatusLine().getStatusCode();
// 从响应对象中获取响应内容并转为String
String result = EntityUtils.toString(httpResponse.getEntity());
// 把result转为HashMap
HashMap resultMap = new Gson().fromJson(result, HashMap.class);

MySQL模糊匹配表名拼接删表语句

1
2
3
4
# drop不支持删表
SELECT CONCAT( 'drop table ', TABLE_NAME, ';')
FROM information_schema.tables
WHERE table_name LIKE 'wb_role_%';