Java 问题
BeanUtils.copyProperties 的使用
BeanUtils.copyProperties
可以用于更新,将源数据更新到目标数据中
import org.springframework.beans.BeanUtils;
public Result updatePostgraduate(@RequestBody EasPostgraduate easPostgraduate) {
EasPostgraduate oldPostgraduate = easPostgraduateService.getById(easPostgraduate.getId());
BeanUtils.copyProperties(easPostgraduate, oldPostgraduate); easPostgraduateService.updateById(oldPostgraduate);
}
// 集合类需要遍历复制
List<Company> companies = companyMapper.selectList(new QueryWrapper<Company>().lambda().like(name != null
&& !"".equals(name), Company::getName, name) );
List<CompanyInfoVO> companyInfo = new ArrayList<>();
for (Company company : companies) {
CompanyInfoVO companyInfoVO = new CompanyInfoVO();
BeanUtils.copyProperties(company, companyInfoVO);
companyInfo.add(companyInfoVO);
}
判空 empty 和 blank 的区别
isEmpty
判断某字段字符串是否为空,为空的标准是 str == null
或者 str.length() == 0
isBlank
判断某字符串是否为空或长度为 0 或由空白符、制表符、换行符、换页符、回车符构成
@WebFilter 过滤路径太广
@WebFilter
不加任何属性时为过滤全部路径@WebFilter(urlPatterns = "")
加上这个属性之后只过滤指定路径,指定路径在其他地方写
JSONObject 的链式放元素
new JSONObject().fluentPut("code", 400);
且可以直接 new 对象放入在其他 JSONObject 里 jo.put("data", new JSONObject().fluentPut("status", "read"));
MySQL 连续打卡查询语句
SET @row_number = 1, @customer_no = NULL;
SELECT a.num FROM(SELECT @row_number := CASE WHEN (DATE_ADD( punch_date, INTERVAL - 1 DAY ) = @customer_no)
THEN @row_number + 1 ELSE 1 END num, @customer_no := punch_date, punch_date FROM punch
WHERE unionid = 'ogOZxv30psiQMEsM4nQ9LM2eFa5c' AND brush_type = 2
ORDER BY punch_date ASC ) a WHERE a.punch_date = '2022-07-25'
PageHelper 分页失败
查询出的 SQL 一定要写到 PageInfo 之前才行,否则会分页失败。
public Result find() {
PageHelper.startPage(PageNum, PageSize);
List<JSONObject> list = commonService.find();
PageInfo<JSONObject> pageInfo = new PageInfo(list);
JSONObject jo = new JSONObject();
jo.put("data", pageInfo.getList());
jo.put("size", pageInfo.getSize());
jo.put("total", pageInfo.getTotal());
return new SuccessResult(jo);
}
SQL 中双引号、单引号、反引号的作用和区别
" 双引号 在最外层包含这个 SQL 语句的。
' 单引号 是用来包含字符串的,当表中的字段为char
或者varchar
时,数据要用它包括起来。
` 反引号 在命名字段的时候,如果字段名与 SQL 关键字冲突了,这时候要用它包括关键字来规避关键字检测。
StringRedisTemplate 和 RedisTemplate 区别
- StringRedisTemplate: 只能存储 String 类型的值,因此不能存储如对象,序列化为 String,为原字符串。
- RedisTemplate: 可以存储任意类型,含对象。序列化采用 jdk 的,如:\xAC\xED\x00
RedisTemplate 建议强制加上泛型 @Autowired private RedisTemplate<String, Object> redisTemplate;
使用 JSONObject 和 VO 区别
如果查询的 SQL 返回 null,则 JSONObject 不返回此字段值,VO 则返回字段值 "field" : null
请求参数的限制
参数上写上@RequestParam
这个注解,代表参数需要存在默认是(required = true)
参数上什么都不写等于@RequestParam(required = false)
,就是这个参数不传值就为null
参数上写上@NonNull
,参数为不传则直接抛出 NPE 异常,是 Lombok 的注解
建议必传参数用@RequestParam
,不必传参数就不写注解
@WebFilter 注解方式不生效
注意filterName
不要显示的写类的驼峰命名法,直接去掉会默认以类名驼峰命名法来注入到 bean 中,在Application
中加上@ServletComponentScan
注解来启动
Mybatis 当 xml 里面传入的参数为 0,自动转化为空字符
<if test="gender != null and gender !=''">
加了and gender !=''
,当传入 0 时,会自动转化成空字符
SpringBoot 添加启动时 Banner 图输出
resources
目录下新建一个banner.txt
文件 https://www.bootschool.net/ascii 生成 ASCII 艺术字网站
MySQL 中的死锁释放
通过使用 KILL 语句来终止一个会话。如果该会话持有行锁,则在释放锁之前无法使用KILL语句结束该会话。
- 使用命令
SHOW ENGINE INNODB STATUS;
来查看当前会话的状态并找到占用锁的线程id。 - 使用命令
SELECT * FROM information_schema.INNODB_LOCKS WHERE locked_by_thread = [线程ID]
,来查看当前会话所持有的锁信息。 - 选择需要杀死的线程 id,并使用命令
KILL [线程ID];
来终止该会话。 - 当会话被结束时,对应的行锁也会被释放。
打印字体输出颜色
System.out.println
可以输出一段文本,并且默认的字体颜色是黑色。如果想要把部分文本以红色字体显示,需要使用控制台输出时支持 ANSI 转义序列的终端工具。
System.out.println("\033\[31m" + "哈哈" + "\033\[0m");
其中,\033\[31m
表示设置当前输出文本的颜色为红色, \033\[0m
表示重置输出文本的格式,确保之后的文本不会受到前面设置属性的影响。
ANSI 转义序列中的\033\[33m
表示将文本颜色设置为黄色,其中 33 代表颜色代码。具体而言,在 ANSI 颜色编码中,30-37 分别代表黑、红、绿、黄、蓝、紫、青和白。
Java项目跑起来后立即执行一次任务
@PostConstruct public void executeTask() { // your task code here }
这将使任务在 Spring 应用程序上下文加载后立即执行一次,并且不会重复执行。请注意,此方法不是传统的基于时间的 cron 表达式方式,而是基于事件的触发器方法,在Spring框架中比较常见。
MyBatis-Plus中的Service层的随机查询写法
实现类似于SELECT * FROM ai_info ORDER BY RAND() LIMIT 10
的随机查询操作:
List<AiPromptInfo> infos = aiInfoService.getBaseMapper().selectList( new QueryWrapper<AiPromptInfo>().orderByAsc("RAND()") .last("LIMIT " + number));
SpringBoot同一个项目用不同端口启动
在工具栏上选择 Edit Configurations -> 添加新的配置,
并选择 Spring Boot 添加新的配置,并选择 Spring Boot
然后在 Main class 中选择我们的启动类,在 VM options 填写
-Dserver.port=9001
Java 字段值为null不返回该字段
// 实体类上加 @JsonInclude(JsonInclude.Include.NON_NULL)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class SuccessResult extends Result { public Object data; ... }
Java 实体类日期类型格式化
在实体类上增加@JsonFormat注解
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
Spring Security 中的 antMatchers() 过滤级别
http.authorizeRequests().antMatchers("/static/**")
和.antMatchers("/static/*")
在匹配URL上有一些区别。
.antMatchers("/static/**")
:这个模式表示以/static/
开头的路径及其子路径,例如 "/static/css/style.css"、"/static/js/main.js"
等。
通配符**
匹配任意层级的子路径。 .antMatchers("/static/*")
:这个模式表示以/static/
开头的路径下的直接子路径,不包括子路径的子路径,例如 /static/css
、/static/js
等。 如果你希望匹配到/static/
下的所有子路径,使用.antMatchers("/static/**")
; 如果只需要匹配到/static/
下的直接子路径,而不包括子路径的子路径,则可以使用.antMatchers("/static/*")
。
SpringBoot 配置 Session 管理
设置Session超时时间(以秒为单位) server.servlet.session.timeout=1800
设置Session存储方式(默认为使用内存存储) server.servlet.session.store-type=redis
设置Cookie名称(用于存储Session ID) server.servlet.session.cookie.name=MYSESSIONID
设置Cookie路径 server.servlet.session.cookie.path=/
设置Cookie的安全标志(默认为启用) server.servlet.session.cookie.secure=true
设置Cookie的HTTPOnly属性(默认为启用) server.servlet.session.cookie.http-only=true
Spring Security 中的 hasRole 和 hasAuthority 的区别
http.authorizeRequests()
.antMatchers("/admin/").hasAuthority("admin")
.antMatchers("/user/").hasAuthority("user")
及
http.authorizeRequests()
.antMatchers("/admin/").hasRole("admin")
.antMatchers("/user/").hasRole("user")
hasRole
的处理逻辑和hasAuthority
似乎一模一样,不同的是,hasRole
这里会自动给传入的字符串加上ROLE_
前缀,所以在数据库中的权限字符串需要加上ROLE_
前缀。即数据库中存储的用户角色如果是ROLE_admin
,这里就是 admin。
我们在调用hasAuthority
方法时,如果数据是从数据库中查询出来的,这里的权限和数据库中保存一致即可,可以不加ROLE_
前缀。即数据库中存储的用户角色如果是 admin,这里就是 admin。
也就是说,使用hasAuthority
更具有一致性,你不用考虑要不要加ROLE_
前缀,数据库什么样这里就是什么样!而hasRole
则不同,代码里如果写的是 admin,框架会自动加上ROLE_
前缀,所以数据库就必须是 ROLE_admin
。
看起来hasAuthority
和hasRole
的区别似乎仅仅在于有没有ROLE_
前缀。
@Secured 和 @PreAuthorize 注解的区别
@Secured
和@PreAuthorize
是 Spring Security 框架中用于方法安全性检查的两个注解。它们的主要区别在于安全检查的方式和表达式的使用。
- 安全检查方式:
- @Secured:基于角色和权限进行安全性检查。该注解通过指定一个或多个角色或权限,限制方法的访问。例如,@Secured("ROLE_ADMIN")表示只有具有"ROLE_ADMIN"角色的用户才能访问该方法。
- @PreAuthorize:基于表达式进行安全性检查。该注解使用 Spring Expression Language(SpEL)表达式来定义访问规则。例如,@PreAuthorize("hasRole('ROLE_ADMIN')")表示只有具有"ROLE_ADMIN"角色的用户才能访问该方法。
- 表达式使用:
- @Secured:不支持使用表达式,只能直接指定角色或权限。例如,@Secured("ROLE_USER")表示只有具有"ROLE_USER"角色的用户才能访问该方法。
- @PreAuthorize:支持使用Spring Expression Language(SpEL)表达式来进行访问控制。SpEL 是一种强大的表达式语言,可以访问方法参数、方法返回值、作用域对象等。例如,@PreAuthorize("hasRole('ROLE_ADMIN')")表示只有具有"ROLE_ADMIN"角色的用户才能访问该方法。
在实际应用中,@PreAuthorize
注解通常更为灵活,因为它可以使用表达式来定义复杂的访问规则,如基于多个角色、权限以及其它条件的组合进行访问控制。而@Secured
注解则更适用于简单的角色或权限检查场景。
SpringBoot 整合 security 报错
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.reflection.ReflectionException: Illegal overloaded getter method with ambiguous type for property enabled in class class com.springboot.security.entity.User. This breaks the JavaBeans specification and can cause unpredictable results.
原因:在做用户登录的时候,通常我们会判断当前账号是否可用,将这个属性设置为Boolean
类型。而我们定义好的类需要实现UserDetails
接口来规范用户属性,然后重写里面的方法来判断当前的用户是否可用。这时候重写的isEnabled
方法就会跟 Mybatis 自动生成的getEnabled
方法同时存在。
解决方法:因为isEnable
相当于getEnable
,导致 JavaBean 里面有两个getEnabled
方法,违反了 JavaBean 的规范,只要将其中一个删掉就可以了。
@JsonIgnoreProperties(ignoreUnknown = true) 注解
是 Jackson 库中的一个注解,用于在反序列化 JSON 数据时忽略未知的属性。当 JSON 对象包含属性,而 Java 对象中没有相应的属性时,使用这个注解可以避免抛出异常。
具体来说,当使用 Jackson 库将 JSON 数据反序列化为 Java 对象时,如果 JSON 对象包含了一些 Java 对象中没有的属性,那么默认情况下 Jackson 会抛出 JsonMappingException
异常。而通过在 Java 对象上添加 @JsonIgnoreProperties(ignoreUnknown = true)
注解,可以告诉 Jackson 在遇到未知属性时忽略它们,而不是抛出异常。
这个注解可以避免因为 JSON 数据中存在未知属性而导致的错误,特别是当你的 Java 对象无法完全映射 JSON 数据时,这个注解非常有用。它可以让你在反序列化过程中忽略未知属性,而不会导致程序崩溃。
Redis中序列化与注解的问题
com.fasterxml.jackson.annotation
com.alibaba.fastjson2.annotation
需要区别 Redis 的序列号器配置和实体类中的不序列化注解是不是同一个包下的,否则不能生效。
忽略实体类值为null的字段
List<User> records = pageInfo.getRecords();
List<JSONObject> list = JSONObject.parseArray(JSON.toJSONString(records), JSONObject.class);
// 泛型可以变成自己设置的VO类来接收
List<MemberInfoVO> list = JSONObject.parseArray(JSON.toJSONString(records), MemberInfoVO.class);
列表 lambda 多条插入数据库方式
List<String> authoritiesList = wrapper.getJSONArray("authorities").toJavaList(String.class)
List<UserAuthority> list = new ArrayList<>();
authoritiesList.forEach(authority -> list.add(new UserAuthority().setUid(uid).setAuthority(authority));
Token 前缀去除
// 去除token前面的"Bearer "
token = token.replaceFirst("^\\\S+\\\s", "");
PageHelper 分页 3 个核心数据
PageHelper.startPage(pageNum, pageSize);
List<PassMemberVO> list = userService.listPassMember(name, companyName);
PageInfo<PassMemberVO> pageInfo = new PageInfo<>(list);
JSONObject result = new JSONObject();
result.put("pages", pageInfo.getPages());
result.put("total", pageInfo.getTotal());
result.put("list", pageInfo.getList());
接收日期字符串解析成LocalDateTime
// 格式必须为 2023-01-01T00:00:00 才行 String startDateTimeStr = wrapper.getString("startDateTime");
LocalDateTime startDateTime = LocalDateTime.parse(startDateTimeStr);
// 2023-01-01 00:00:00 就不行会报错 要用以下办法
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime startDateTime = LocalDateTime.parse(startDateTimeStr, formatter);
创建 qrtz_locks 表
qrtz_locks 表是 Quartz 框架中用于实现分布式锁的表,用于控制多个 Quartz 实例之间的并发执行。如果该表不存在,可以使用 Quartz 提供的table_mysql_innodb.sql
脚本来创建该表。
以下是创建 qrtz_locks 表的步骤:
找到 Quartz 的 JAR 文件,通常位于
~/.m2/repository/org/quartz-scheduler/quartz/2.3.2/quartz-2.3.2.jar
解压Quartz的JAR文件,可以使用以下命令:
jar -xvf quartz-2.3.2.jar
进入解压后的目录
org/quartz/impl/jdbcjobstore
,可以看到tables_mysql_innodb.sql
文件。执行以下命令创建
mysql -u -p <tables_mysql_innodb.sql
如果使用的是其他数据库,可以使用相应的脚本文件,例如tables_postgres.sql
。
quartz 初始化数据库表结构
spring:
quartz:
auto-startup: true
job-store-type: jdbc
jdbc:
initialize-schema: never
数据库架构初始化模式: never: 从不进行初始化 always: 每次都清空数据库进行初始化 embedded (默认): 只初始化内存数据库 # 先选 always 待表生成之后再改为 never。只有使用druid数据库连接池才会自动生成表
Maven 更换阿里镜像源
方式一:修改 maven 根目录下的 conf 文件夹中的 setting.xml 文件中的 mirrors 下添加 mirror 标签
<mirrors>
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>阿里云公共仓库</name>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
</mirrors>
方式二:在项目中添加发下配置
<repositories>
<repository>
<id>nexus-aliyun</id>
<name>nexus-aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
阿里 FastJSON 问题
com.alibaba.fastjson2.JSONException: class java.lang.String cannot be converted to class
该代码原来写无问题,在新系统执行时报错,原因为 fastjson 漏洞升级了版本的问题,新系统使用的版本为 2.0.11,或者换最新版的 com.alibaba:fastjson:2.0.35
JSONArray authoritiesArray = wrapper.getJSONArray("authorities");
List<String> authoritiesList = authoritiesArray.toJavaList(String.class);
// 换以下方式
JSONArray authoritiesArray = wrapper.getJSONArray("authorities");
List<String> authoritiesList = JSONObject.parseArray(authoritiesArray.toString(), String.class);
Jar 包增量更新
- 将解压后的文件放在一个没有任何其他内容的文件夹中。
- 删除原 jar 包,确保当前文件夹中只包含你想要压缩的文件。
- 在该文件夹中打开命令行窗口(在文件路径中输入 cmd 即可进入)。
- 在命令行中输入
jar cvf0M name.jar ./
,其中 name 代表你想要的 jar 包的名字,./
表示当前文件夹中的所有文件。 - 按下回车键,命令行会开始压缩文件,并生成一个新的 jar 包。