
WeRun23级纳新收获大杂烩
本文档整理来源是2024.1~2024.2期间:
后端第二、三次作业纳新23级同学的作业,一些新点子与注意点
安卓端纳新需要做后端接口,需补充的技术栈
Java 简化字符串 转换为状态码
以下问题为ChatGPT回答内容:
您可以使用Java中的枚举(Enum)来实现简化提示字符串到状态码的转换。以下是一个简单的示例代码:
public class StatusCodeConverter {
public enum StatusCode {
SUCCESS("操作成功", 200),
NOT_FOUND("未找到资源", 404),
SERVER_ERROR("服务器内部错误", 500);
private final String message;
private final int code;
StatusCode(String message, int code) {
this.message = message;
this.code = code;
}
public String getMessage() {
return message;
}
public int getCode() {
return code;
}
}
public static void main(String[] args) {
// 示例用法
System.out.println(StatusCode.SUCCESS.getMessage() + ",状态码:" + StatusCode.SUCCESS.getCode());
System.out.println(StatusCode.NOT_FOUND.getMessage() + ",状态码:" + StatusCode.NOT_FOUND.getCode());
System.out.println(StatusCode.SERVER_ERROR.getMessage() + ",状态码:" + StatusCode.SERVER_ERROR.getCode());
}
}
在这个例子中,我们定义了一个名为 StatusCode
的枚举,其中包含了一些常见的状态码,以及相应的提示信息。每个枚举常量都有一个与之关联的提示信息和状态码。您可以根据自己的需求添加更多的枚举常量。
给学弟的回复:
彩蛋(关于学长的探索):学长发现了你的代码中返回了各种各样的提示字符串,如下面的代码,但是这种实现方式耦合度高,一段代码要重复写好多次,还容易出现运行逻辑错误(编译器不会报错,但是实际上执行了错误的逻辑):
// 修改前 如果equals这一串文字复制错了,IDEA不会检查报红,容易检查不出bug
String checkState = userService.characterLegalityCheck(user.getUsername());
if(!checkState.equals("Character Legality Check Passed"))
return "username:" + checkState;
// 比如复制粘贴的时候把Passed后面多一个空格就寄了
if(!checkState.equals("Character Legality Check Passed "))
学长建议这种state、status字段以后都改成较为简单的值,把字符串单独再返回,于是学长展开了一系列探索之路:
修改方式1:state 传 String 状态码,状态码和变量名可以自己规定
// 访问权限可根据需要而定 但 static 和 final 是尽量必须的
private static final String CHECK_PASSED = "Character Legality Check Passed";
public static final String OTHERS_NOT_USED = "Characters other than numbers, underscores, and uppercase and lowercase letters cannot be used";
String stateCode = userService.characterLegalityCheck(user.getUsername());
if(stateCode.equals("100"))
return "username:" + CHECK_PASSED;
else if(stateCode.equals("101"))
return "username:" + OTHERS_NOT_USED;
修改方式2:想到了C语言的枚举,遂将 state 用enum表示,语句存入String[]数组,但是实操发现代码有一些复杂:
public class CheckState {
public enum StateCode {
CHECK_PASSED,
OTHERS_NOT_USED,
PASSWORD_STRENGTH_WEAK
}
// 按顺序存入数组中,然后访问下标获取内容
public static String[] States = {
"Character Legality Check Passed",
"Characters other than numbers, underscores, and uppercase and lowercase letters cannot be used",
"Please use at least two types of numbers, underscores, and uppercase and lowercase letters"
};
public static void main(String[] args) {
System.out.println("States[StateCode.CHECK_PASSED.ordinal()] = " + States[StateCode.CHECK_PASSED.ordinal()]);
}
}
后来查阅了一下Java中enum的用法,发现它并不像C语言那么简单,它其实本质上还是一个类,存在着成员变量和构造方法:链接:Java中枚举类型(Enum)使用进阶
根据以上特性,再加上GPT的帮助,形成了以下代码:
// StatusCode.java
public enum StatusCode {
// 状态码及其内容
CHARACTER_CHECK_PASSED(100,"Character Legality Check Passed"),
OTHERS_NOT_USED(201,"Characters other than numbers, underscores, and uppercase and lowercase letters cannot be used"),
PASSWORD_STRENGTH_WEAK(301,"Please use at least two types of numbers, underscores, and uppercase and lowercase letters");
// 成员变量与构造方法
private final int code;
private final String message;
StatusCode(int code, String message) {
this.code = code;
this.message = message;
}
// getter
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
// main方法 用于测试
public static void main(String[] args) {
System.out.println("CHARACTER_CHECK_PASSED.getMessage() = " + CHARACTER_CHECK_PASSED.getMessage());
}
}
这种实现将**耦合度低 + 集成度高 + 方便调用**优点聚在一起,具体的内容只集中在前几行,后面的成员变量和构造方法都不用改,并且不易出错——一旦打错 IDE 就一定会在编译阶段就会爆红,而不会在运行时发生问题再排错(如像原来写的用equal方法比对字符串一致性)。
另外,变量名、状态码都可以由你来自定义哦!(比如1xx成功 2xx注册出现问题 3xx登录出现问题)
如果感兴趣,建议了解一下**HTTP状态码**,相信一定会对你有帮助!
关于 Spring IOC 与 DI 的理解
2023210872-李承胤
//将当前这个实现类交给IOC容器管理,成为IOC容器中的Bean对象 //即将IOC对象的创建控制权由程序自身转移到外部容器 public class UserServiceImpl implements UserService { //运行时,@Autowired注解使IOC容器提供该类型的Bean对象,并赋值给该变量
2023210824-李洋
在学习到IOC和DI时,我忍不住直呼“妙啊“,这是继面向对象思想后,又一让我觉得被点醒的内容,通过Component注解将方法交给IOC容器管理,再通过Autowired注解将bean拿出,这种解耦思路让我眼前一亮,当有多个实现同一接口的方法时,只需要选择让谁成为bean便可直接调用,使代码更加便于维护。
紫丁香-计算机-张奕诺:
关于 `@Resource` 和
@Autowired
注解的区别,我找到了一篇文章链接作为参考;现阶段完成大作业可以认为二者没有区别,但更推荐使用后者
@Autowired 和 @Resource 都是用来实现依赖注入的注解(在 Spring/Spring Boot 项目中),但二者却有着 5 点不同:
来源不同:@Autowired 来自 Spring 框架,而 @Resource 来自于(Java)JSR-250;
(最重要)依赖查找的顺序不同:@Autowired 先根据类型再根据名称查询,而 @Resource 先根据名称再根据类型查询;
即:@Autowired是按类型注入的
支持的参数不同:@Autowired 只支持设置 1 个参数,而 @Resource 支持设置 7 个参数;
依赖注入的用法支持不同:@Autowired 既支持构造方法注入,又支持属性注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入;
编译器 IDEA 的提示不同:当注入 Mapper 对象时,使用 @Autowired 注解编译器会提示错误,而使用 @Resource 注解则不会提示错误。
友链:21-魏鹏宇学长 关于这方面源码理解的blog 链接
数据库 password 字段设计
简而言之,如果要加密,varchar长度要在60以上。
MySQL 数据库 utf8mb4 字符集问题
目前版本的MySQL如果字符集选择utf8,它的意思其实是utf8mb3:
utf8 is an alias for the utf8mb3 character set.
The utf8mb3 character set will be replaced by utf8mb4 in some future MySQL version. Although utf8 is currently an alias for utf8mb3, at that point utf8 will become a reference to utf8mb4. To avoid ambiguity about the meaning of utf8, consider specifying utf8mb4 explicitly for character set references instead of utf8.
可以看出,MySQL中的utf8实质上不是标准的UTF8。MySQL中,utf8对每个字符最多使用三个字节来表示,所以一些emoji甚至是一些生僻汉字就存不下来了,比如“𡋾”。
问题复现与报错如下:
曾几何时,每次建库都选utf8,觉得自己比那些用乱七八糟编码的人不知道酷到哪里去了。直到好多年前的某次课程设计做项目的时候,愉快的建了个用户表:
CREATE TABLE `test_user` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
然后愉快的新增用户:
INSERT INTO test_user(name) VALUES("我是😁")
,接着愉快的反思人生:Incorrect string value: '\xF0\x9F\x98\x81' for column 'name' at row 1
原文blog:链接
Mybatis 源码:SqlSession
这里有一篇6年以前的blog:链接
简而言之,SqlSession 是 MyBatis 的关键对象,通过这个接口可以操作命令,管理事务等,是JDBC connection的增强版。
需要注意的是:虽然 SqlSession 提供select/insert/update/delete方法,在旧版本中使用使用 SqlSession 接口的这些方法,但是新版的 Mybatis 中就会建议使用Mapper 接口的方法。
Java Springboot 推荐的各种规范
转载与原文链接:
https://zhuanlan.zhihu.com/p/413278453
https://www.cnblogs.com/lovemelucky/p/15235062.html
这里有好多部分都需要进行学习,不在赘述了
项目打包时出现的问题
maven package 打好jar包之后,执行jar包发现总是找不到主类,后来发现是pom.xml出现了问题:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<configuration>
<mainClass>com.werun.FebAndroidApplication</mainClass>
<!-- <skip>true</skip>-->
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
这里要注意两点,一是要把mainClass属性配置对,二是要把skip属性注掉。
application.yml 中 classpath 的位置
先上结论,Springboot 在开发过程中 classpath 位置在:
src/main/java路径
src/main/resouces路径
第三方jar包的根路径
在编译后,打开jar包后的位置在:
BOOT-INF/classes
这里包含上述resources路径的文件。
BOOT-INF/lib
参考博客:
https://blog.csdn.net/y_chengbo/article/details/110118195
https://www.cnblogs.com/klhans/p/14900059.html
Mybatis xml配置
项目需求
之前接触一对多设计时使用吗mybatis-plus的QueryWrapper,避免了写SQL语句。
但是这次项目数据库使用多对多设计,而mp的QueryWrapper配置如下:
public User getUserWithRoles(Long userId) {
// 查询用户信息,并将其存储到缓存中
User user = userMapper.selectById(userId);
// 查询用户对应的角色信息,并将其存储到缓存中
List<Role> roles = roleMapper.selectList(
Wrappers.<Role>lambdaQuery()
.inSql(Role::getId,
"SELECT role_id FROM user_role WHERE user_id = " + userId));
// 将角色信息设置到用户对象中,并返回用户对象
user.setRoles(roles);
return user;
}
好吧。既然QW又整了一堆活,还用了Lambda,又多了一堆学习成本,那还不如乖乖写 xml + SQL 语句吧。
mybatis 配置
首先在application.yml里配置Mybatis,各个属性应该无需解释:
# MyBatis配置
mybatis:
typeAliasesPackage: com.werun.entity
mapperLocations: classpath*:/mapper/*.xml
# configLocation: classpath:mybatis/mybatis-config.xml
configuration:
map-underscore-to-camel-case: true
最后那个map-underscore-to-camel-case,其实是开启数据库与实体类的驼峰映射,详见:Mybatis之map-underscore-to-camel-case开启驼峰功能
classpath 后面加上* 代表可以匹配多个文件
由于classpath相关属性,我们就在resources/mapper里创建一个xxxMapper.xml
xml 基本格式
前两行固定格式如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
namespace:对应的Java Mapper接口 包含包名
<mapper namespace="com.werun.mapper.MovieMapper">
resultMap:描述如何从结果集中加载对象。可以与实体类对应。常见属性有id、type,常见元素id、result
<resultMap id="BaseResultMap" type="com.samton.erp.api.orders.bean.entity.TErpOrders" >
<id column="orders_id" property="ordersId" jdbcType="BIGINT" />
<result column="order_type" property="orderType" jdbcType="SMALLINT" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="orders_date" property="ordersDate" jdbcType="TIMESTAMP" />
</resultMap>
这里
column 指数据库字段名
property 指实体类属性
jdbcType 指数据表支持的类型列表。这个属性只在insert、update、或delete的时候针对允许为空的列有用。
javaType 一个完整的类名,或者是要给类型别名。如果你匹配的是一个JavaBean,那么MyBatis通常会自行检测到。然后,如果你是要映射到一个HashMap,那么需要指定javaType要达到的目的。
基础 CRUD 的返回值
<select id="findMoviesByActorId" resultType="com.werun.response.vo.MovieVO">
SELECT m.id, m.name ,m.grade, m.pic_url
FROM movie m
INNER JOIN movie_actor ma ON m.id = ma.movie_id
INNER JOIN actor a ON ma.actor_id = a.id
WHERE a.id = #{id}
</select>
<!-- 添加演员到电影 -->
<insert id="addActorToMovie">
INSERT INTO movie_actor (actor_id, movie_id)
VALUES (#{actorId}, #{movieId})
</insert>
<delete id="removeMovie">
DELETE FROM movie_actor
WHERE movie_id = #{movieId}
</delete>
resultMap:根据上述resultMap的id返回其定义的pojo
resultType:可以直接按照实体类属性名称返回实体类,无需配置resultMap。
如果数据库中的字段在resultType指定的实体类中没有对应的字段名称,那这个值将丢失。如果实例类中的字段名称在数据库中不存在或者不一致,那么最终实体类的这个字段为null(int类型是0,boolean类型为false)
如果在MyBatis中不配置resultType,则MyBatis会根据查询结果的类型来自动映射结果。这意味着如果查询返回的列名与目标对象的属性名匹配,MyBatis会尝试自动映射它们。如果没有配置resultType,则MyBatis会默认使用HashMap来存储结果,其中键是列名,值是列值。但是,最好的实践是显式地配置resultType或resultMap来确保准确的结果映射和类型安全。
update、insert、delete 的返回值都是 int 指受影响的行数。一般 成功为1 失败为0 错误则报异常