Java程序猿搬砖笔记(六)

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

好记性不如烂笔头

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

@RequestBody注解支持空参数

@RequestBody(required=false),get/post且实体不加无参构造方法都行

windows查看占用端口并杀死对应进程

1
2
3
4
5
6
7
8
9
10
11
E:\Documents\MyIdeaProjects\act\target>netstat -ano |findstr "8888"
TCP 0.0.0.0:8888 0.0.0.0:0 LISTENING 14176
TCP [::]:8888 [::]:0 LISTENING 14176

E:\Documents\MyIdeaProjects\act\target>tasklist |findstr "14176"
javaw.exe 14176 Console 2 254,736 K

E:\Documents\MyIdeaProjects\act\target>taskkill /f /t /im "javaw.exe"
成功: 已终止 PID 14176 (属于 PID 13304 子进程)的进程。

E:\Documents\MyIdeaProjects\act\target>netstat -ano |findstr "8888"

MySQL和MyBatis对JSON类型字段的处理

  • MySQL有JSON字段,MyBatis不支持JSON类型字段的处理,需要自己写Handler

  • MySQL存有转义字符的JSON会报错,比如"apiParam":"{orderId:"$orderId$"}"

    参考链接
    参考链接

Java连接8.0版本的MySQL

Java连接的8.0版MySQL时需要把mysql-connector-java包换成8.X版本的,把链接换成com.mysql.cj.jdbc.Driver。com.mysql.cj.jdbc.Driver是6.x版本的新功能,需要指定时区serverTimezone和useSSL报com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server错误同样是因为数据库版本太高了而对应的驱动器太低了。相关代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<!-- dbcp链接池 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql:///activiti_demo?serverTimezone=UTC&amp;useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="maxActive" value="3" />
<property name="maxIdle" value="1" />
</bean>

参考链接

Spring整合Activiti测试时报错

错误如下:

1
2
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'processEngine': FactoryBean threw exception on object creation; nested exception is org.apache.ibatis.exceptions.PersistenceException: 
Error querying database. Cause: java.sql.SQLSyntaxErrorException: Table 'activiti_spring.act_ge_property' doesn't exist

原因:因为mysql使用schema标识库名而不是catalog,因此mysql会扫描所有的库来找表,如果其他库中有相同名称的表,activiti就以为找到了,本质上这个表在当前数据库中并不存在。设置nullCatalogMeansCurrent=true,表示mysql默认当前数据库操作,在mysql-connector-java 5.xxx该参数默认为true,在6.xxx以上默认为false,因此需要设置nullCatalogMeansCurrent=true。
解决方法:配置mysql连接时加上:nullCatalogMeansCurrent=true。
代码如下:

1
<property name="url" value="jdbc:mysql://localhost:3306/activiti_spring?nullCatalogMeansCurrent=true"/>

参考链接

Apache CXF客服端调用失败

错误信息如下:

1
Caused by: org.apache.cxf.binding.soap.SoapFault: Unexpected wrapper element

解决方法:

  • 客户端接口头部的@WebService加上服务端的命名空间(可以通过wsdl文档查看)
    比如@WebService(targetNamespace=“http://service.ws.myapp.com/”)
  • 客户端和服务端包名改成一致的(不推荐)

IDEA几个常用快捷键

ctrl+home 跳到文件头
ctrl+end 跳到文件尾
ctrl+f1+1 定位到文件位置
ctrl+shift+u 大小写转换

jps -l命令

jps -l命令可以查看本地启动的java进程,这是Jdk提供的命令

IDEA配置svn

  • 需要按照SVN的时候选择命令行,否则在svn客户端安装路径bin目录下找不到svn.exe。
  • 重新安装SVN后,右键->settings(设置)->icon overlays(图标覆盖)->选择shell(windows外壳),如果无效需要修改注册表后重启。
    参考链接
    参考链接

IDEA更新svn项目提示authentication required解决方法

  • File->Settings->Version Control->Subversion->Clear Auth Cache 这个时候IDEA拉取代码会报No appropriate protocol (protocol is disabled or cipher suites are inappropriate)错,需要进行第二步
  • 本地拉取代码,提示框选择永久接受证书;再回到IDEA 成功拉取代码。

引入Swagger后前端访问路径默认加了/v2/api-docs

方法一、把属性文件的server.servlet.context-path=/portal去掉可以正常访问

方法二

  • 在资源目录新建一个属性文件,内容如下:
1
pringfox.documentation.swagger.v2.path=/
  • 在SwaggerConfig配置类添加注解
1
@PropertySource("classpath:swagger.properties")

参考链接

Java实体转Map

  • Web项目推荐使用
1
BeanMap map = BeanMap.create(noticePageReq);
  • Java项目推荐使用反射

参考链接

mybatis做动态排序的时候,字段打印出字符串解决方法

1
2
3
4
5
6
7
8
9
10
11
<select id="queryNoticePageList" resultMap="BaseResultMap" parameterType="Map" >
select
<include refid="Base_Column_List" />
from t_notice
<if test="prop != null" >
order by #{prop}
<if test="order != null" >
#{order}
</if>
</if>
</select>

order by中打印出了字符串,在sql中会语法错误 :

1
2
select id, notice_type_id, title, source, description, status, create_time, update_time, create_user, last_update_user, is_show, release_time
FROM t_notice order by 'title' 'asc';

解决方法:

1
把上面的#{prop}改为${prop},#{order}改为${order}

SpringBoot接收对象属性的属性时为空

Java实体对象如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Data
public class PageRequest<T>{
private Integer page; //当前页
private Integer rows; //每页显示数
private String prop; //排序字段名
private String order; //排序方式:asc或desc
private T data;
}

public class NoticePageReq {
@ApiModelProperty(value="公告标题")
private String title;
}

请求接口后:
在这里插入图片描述

解决方法1:在类NoticePageReq上添加@Data注解,这个注解会给属性加Set和get方法,需要添加lombok依赖。
解决方法2:在属性title添加@JsonProperty注解。这个注解的主要作用是把传过来的JSON值转换名称,比如下划线转驼峰,然后注入值。

Spring中的@Transactional(rollbackFor = Exception.class)属性详解

参考链接1参考链接2参考链接3

用了lombok的@Data注解就不要继承关系

如何理解 public static T

这是泛型方法。在方法中出现了泛型的结构,泛型参数与类的泛型参数无关。泛型方法所属的类是不是泛型类都可以。泛型方法可以声明为static。参考代码如下:

1
2
3
4
5
6
7
8
9
public class ValidatorUtil {
// 获取validator对象
private static Validator validator = Validation.byProvider(HibernateValidator.class).configure()
.failFast(false).buildValidatorFactory().getValidator();

public static <T> Set<ConstraintViolation<T>> validateBean(T bean){
Set<ConstraintViolation<T>> validateResult = validator.validate(bean);
return validateResult;
}

参考链接

Java BigDecimal累加

1
2
3
4
BigDecimal awardMoney = new BigDecimal(0);
for(ImportDoubleAwardsField field: entrySet.getValue()){
awardMoney = awardMoney.add(field.getMoney());
}

注意BigDecimal的add()方法返回的是相加后的数据

利用BeanMap进行对象与Map的相互转换

不过项目中使用BeanMap.create()方法个别地方会报异常,可能是用了热部署工具dev-tools,但是我去掉这个也报错,索性改为了反射方法
参考链接

使用reflect反射对象与Map的相互转换

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package edu.hrbeu.platform.modeling.common.util;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
/**
* 使用reflect进行转换
*/
public class MapTransformUtils1 {

public static <T> T mapToObject(Map<String, Object> map, Class<T> beanClass) throws Exception {
if (map == null)
return null;

T obj = beanClass.newInstance();

Field[] fields = obj.getClass().getDeclaredFields();
for (Field field : fields) {
int mod = field.getModifiers();
if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
continue;
}

field.setAccessible(true);
field.set(obj, map.get(field.getName()));
}

return obj;
}

public static Map<String, Object> objectToMap(Object obj) throws Exception {
if (obj == null) {
return null;
}

Map<String, Object> map = new HashMap<String, Object>();

Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
map.put(field.getName(), field.get(obj));
}

return map;
}
}

参考链接

Linux 脚本 sh 和 ./ 的区别

参考链接

git分支相关操作命令

查看当前分支

1
git branch

查看所有分支(本地和远程)

1
git branch -a

查看所有分支(远程)

1
git branch -r

切换分支

1
git checkout 分支名

创建分支

1
git branch 分支名

删除分支

1
2
删除前需要先切换到其他分支,然后执行下面的命令
git branch -D 分支名

合并master分支代码到dev

1
先切换到dev分支然后执行git merge master

SpringBoot 定义全局日期响应格式

方法一(消息转换器):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* 设置全局响应日期格式
*
* @author liquanhong
* @createTime 2021/09/07
*/

@Configuration
public class Json2MessageConventerConfig {
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
objectMapper.setDateFormat(sdf);
converter.setObjectMapper(objectMapper);
return converter;
}
}

这种方法如果前端传递接口未定义的参数会报JSON参数转换错误:

1
JSON parse error: Unrecognized field \"a\" (class com.aspirecn.rewardportal.common.entity.PageRequest), not marked as ignorable; nested exception is com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field \"a\" (class com.aspirecn.rewardportal.common.entity.PageRequest), not marked as ignorable (5 known properties: \"rows\", \"data\", \"prop\", \"order\", \"page\"])\n at [Source: (PushbackInputStream); line: 4, column: 9] (through reference chain: com.aspirecn.rewardportal.common.entity.PageRequest[\"a\"])

方法二(配置文件 推荐):

1
2
3
4
5
6
7
8
9
#在application.properties文件中配置
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

#在application.yml文件中配置
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8

方法三(在指定的Bean属性中添加注解):

1
2
3
4
5
6
7
8
9
@JsonFormat(timezone = "GMT+8", pattern = "yyyyMMddHHmmss")
private Date createTime;

需要有下面的依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>

注意如果有用到Fastjson ,注解可能会失效可以用@JSONField注解解决。

1
2
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date updatedDate;

方法四(用于解决配置文件不生效的问题)

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
27
28
29
30
31
/**
* WebMvc配置
*
* @author liquanhong
* @createTime 2021/10/25
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 使用此方法, 以下 spring-boot: jackson时间格式化 配置 将会失效
* spring.jackson.time-zone=GMT+8
* spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
* 原因: 会覆盖 @EnableAutoConfiguration 关于 WebMvcAutoConfiguration 的配置
* */
@Override
public void extendMessageConverters(List converters) {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = converter.getObjectMapper();
// 生成JSON时,将所有Long转换成String
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
objectMapper.registerModule(simpleModule);
// 时间格式化
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// 设置格式化内容
converter.setObjectMapper(objectMapper);
converters.add(0, converter);
}
}

SpringBoot对多模块包扫描问题

方法一:

1
@SpringBootApplication(scanBasePackages = {"com.xx.xx.xx.A", "com.xx.xx.B"})

方法二:

1
https://blog.csdn.net/lintiyan/article/details/94362640

方法三(SpringBoot默认会扫描本模块下面的包):

1
2
@SpringBootApplication
@ComponentScan("com.xx.xx.B")

SpringBoot包扫描排除指定类

1
2
@ComponentScan(basePackages={"com.aspirecn.core.common","com.aspirecn.kjcgkyg"},
excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,classes = DruidConfig.class))

参考链接

Spring的@RequestBody注解接收参数时如果实体类属性以if和k开头,那么传参会接收不到

前端入参:

1
2
3
4
5
{
"kBB": "kBB123",
"isBB": "isBB123",
"ifBB": "ifBB123"
}

后端接收不到,如下图所示:
在这里插入图片描述

isBB可以接收到。 接收ifBB、kBB需要在实体类的属性中加上注解。,如下面的代码所示:

1
2
3
4
@JsonProperty(value = "kBB")
private String kBB;
@JsonProperty(value = "ifBB")
private String ifBB;

mysql bigint类型和datetime类型的转换

1、bigint类型转换为datetime类型

1
select from_unixtime(1164691264437/1000);

2、datetime类型转换为bigint类型

1
select UNIX_TIMESTAMP('2021-09-13 11:24:59');

使用EasyExcel导入问题

  • 如果在注解中添加index属性,value的名称可以和Excel的列名不一样;
  • 如果没有添加index属性,value的名称必须和Excel的列名一样
1
2
@ExcelProperty(value = "研发能力类型名称",index = 6)
private String capabilityTypeName;

校验导入的Excel数据是否重复

比如要求同一类型成果同一年份同一成果名称不能相同,如下图所示:
在这里插入图片描述

1、先去数据库查询类型、年份和成果名拼接字符串列表得到List recordList

1
2
3
SELECT concat(type,'@',which_year,'@',name)
FROM t_achievement
WHERE type not in(1,2)

2、循环读到的Excel行,每读一条拼取一个key去recordList中匹配,然后把该条数据也放到recordList

1
2
3
4
5
String productKey = achievementDto.getType()+AchievementConstant.SEPARATOR_A+achievementDto.getWhichYear()+AchievementConstant.SEPARATOR_A+achievementDto.getName();
if(recordList.contains(productKey)){
errorBuilder.append(AchievementConstant.PRODUCT_NAME_REPEAT);
}
recordList.add(productKey);

这样把填写和数据库查询的数据都放到了一起,好处是不用再去单独校验用户填写的数据相互之间是否有问题。