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() == 0isBlank判断某字符串是否为空或长度为 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.annotationcom.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 包。