项目基础
1.mysql
1.1基础语句
1 | # 查询所有数据库 |
1.2表的增删改查
1 | # 查询语句 |
1.3排序与分页
1 | # order by 在select语句的最后 |
1.4运算符IN和BETWEEN
1 | # IN表示检查指定的值 |
1.5运算符UNION和like
UNION操作符用于将两个或多个SELECT查询的结果合并到一个结果集中。1
2
3
4# 如果没有加ALL则表示去除重复行
SELECT column_list FROM table1_name
UNION ALL
SELECT column_list FROM table2_name;
LIKE用于模糊查询
声明 | 含义 |
---|---|
WHERE name LIKE ‘Da%’ | 查找以Da开头的名字 |
WHERE name LIKE ‘%th’ | 查找以th结尾的名字 |
WHERE name LIKE ‘%on%’ | 查找包含on的名字 |
WHERE name LIKE ‘Sa_’ | 查找以Sa开头且最多后跟一个字符的名字 |
WHERE name LIKE ‘_oy’ | 查找以oy结尾且最多包含一个字符的名字 |
WHERE name LIKE ‘an‘ | 查找包含an的名字,并以一个字符开头和结尾 |
1.6表的连接
- 内连接
- 两个表中交集的数据
1
2
3SELECT t1.emp_id, t1.emp_name, t1.hire_date, t2.dept_name
FROM employees AS t1 INNER JOIN departments AS t2
ON t1.dept_id = t2.dept_id ORDER BY emp_id;
- 左连接
- 左表和两表交集的数据
1
2
3SELECT t1.emp_id, t1.emp_name, t1.hire_date, t2.dept_name
FROM employees AS t1 LEFT JOIN departments AS t2
ON t1.dept_id = t2.dept_id ORDER BY emp_id;
- 右连接
- 右表和两表交集的数据
1
2
3SELECT t1.emp_id, t1.emp_name, t1.hire_date, t2.dept_name
FROM employees AS t1 RIGHT JOIN departments AS t2
ON t1.dept_id = t2.dept_id ORDER BY dept_name;
1.7函数
1.7.1关于日期时间函数
获取日期时间函数
- 获取当前日期时间
1
SELECT NOW(); # 2022-01-06 22:37:45
- 获取当前日期
1
SELECT CURRENT_DATE(); # 2022-01-06
- 获取当前时间
1
SELECT CURRENT_TIME(); # 22:39:04
日期格式化
- 日期转指定格式字符串示例代码:
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
27SELECT DATE_FORMAT('2022-01-08 22:23:01', '%Y%m%d%H%i%s');# 20220108222301
说明:
%W 星期名字(Sunday……Saturday)
%D 有英语前缀的月份的日期(1st, 2nd, 3rd, 等等。)
%Y 年, 数字, 4 位 ★★★
%y 年, 数字, 2 位
%a 缩写的星期名字(Sun……Sat)
%d 月份中的天数, 数字(00……31) ★★★
%e 月份中的天数, 数字(0……31)
%m 月, 数字(01……12) ★★★ month
%c 月, 数字(1……12)
%b 缩写的月份名字(Jan……Dec)
%j 一年中的天数(001……366)
%H 小时(00……23)★★★
%k 小时(0……23)
%h 小时(01……12)
%I 小时(01……12)
%l 小时(1……12)
%i 分钟, 数字(00……59) ★★★ minite
%r 时间,12 小时(hh:mm:ss [AP]M)
%T 时间,24 小时(hh:mm:ss)
%S 秒(00……59)
%s 秒(00……59) ★★★
%p AM或PM
%w 一个星期中的天数(0=Sunday ……6=Saturday )
%U 星期(0……52), 这里星期天是星期的第一天,查询指定日期属于当前年份的第几个周 ★★★★
%u 星期(0……52), 这里星期一是星期的第一天1
2
3
4
5
6
7# 日期格式化
select date_format(now(),'%Y%m%d%H%i%s');
# 获取当前是星期几
select date_format(now(),'%Y%m%W');
# 查看当前属于一年中的第几个周 以周末作为一个循环
select date_format(now(),'%Y%U');
select date_format('20220108090109','%Y%U'); - 字符串转日期
1
2
3# 日期格式与表达式格式一致即可
SELECT STR_TO_DATE('06/01/2022', '%m/%d/%Y'); # 2022-06-01
SELECT STR_TO_DATE('20220108090109', '%Y%m%d%H%i%s'); # 2022-01-08 09:01:09
日期间隔
- 增加日期间隔 ★★★
1
2
3
4# 间隔单位可以是DAY MONTH MINUTE WEEK YEAR SECOND HOUR
SELECT DATE_ADD(NOW(),INTERVAL 2 DAY); # 2022-01-07 22:46:39
SELECT DATE_ADD(NOW(),INTERVAL 2 MONTH); # 2022-02-06 22:47:17
SELECT DATE_ADD('2022-02-06 22:47:17',INTERVAL 2 MONTH); # 2022-04-06 22:47:17 - 减去一个时间间隔★★★
1
2SELECT DATE_SUB(NOW(),INTERVAL 3 DAY); # 2022-01-03 22:49:24
SELECT DATE_SUB('2022-02-06 22:47:17',INTERVAL 2 MONTH); # 2021-12-06 22:47:17 - 日期相差天数(天)
1
select datediff('2022-01-06','2021-12-28'); -- 9
- 相差时间(小时)
1
select timediff('2022-01-06 08:08:08', '2021-12-28 09:00:00'); -- 08:08:08
星期操作
返回日期date的星期索引1
2
3
4# 返回日期date的星期索引(1=星期天,2=星期一, ……7=星期六)。这些索引值对应于ODBC标准。
SELECT DAYOFWEEK(NOW())-1;
# 返回date的星期索引(0=星期一,1=星期二, ……6= 星期天)
SELECT WEEKDAY(NOW())+1
其它操作
1 | # 获取日 |
2.mybatis
快速入门
Controller1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DeptController {
private DeptService deptService;
//部门列表查询
public Result depList(){
List<Dept> depts = new ArrayList<>();
depts = deptService.selectList();
return Result.success(depts);
}
}
Service1
2
3public interface DeptService {
public List<Dept> selectList();
}
ServiceImpl1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DeptServiceImpl implements DeptService {
private DeptMapper deptMapper;
public List<Dept> selectList() {
List<Dept> depts = deptMapper.selectList();
for (Dept dept : depts) {
System.out.println(dept);// 调用对象的toString();
}
return depts;
}
}
Mapper1
2
3
4
public interface DeptMapper {
List<Dept> selectList();
}
Mapper.xml1
2
3
4
5
6
7
8
<mapper namespace="com.itheima.mapper.DeptMapper">
<select id="selectList" resultType="com.itheima.pojo.Dept">
SELECT * FROM dept
</select>
</mapper>
2.1mybatis动态语句
- if和where标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<!-- List<Employee> selectEmployeeByCondition(Employee employee); -->
<select id="selectEmployeeByCondition" resultType="employee">
select emp_id,emp_name,emp_salary from t_emp
<!-- where标签会自动去掉“标签体内前面多余的and/or” -->
<where>
<!-- 使用if标签,让我们可以有选择的加入SQL语句的片段。这个SQL语句片段是否要加入整个SQL语句,就看if标签判断的结果是否为true -->
<!-- 在if标签的test属性中,可以访问实体类的属性,不可以访问数据库表的字段 -->
<if test="empName != null">
<!-- 在if标签内部,需要访问接口的参数时还是正常写#{} -->
or emp_name=#{empName}
</if>
<if test="empSalary > 2000">
or emp_salary>#{empSalary}
</if>
<!--
第一种情况:所有条件都满足 WHERE emp_name=? or emp_salary>?
第二种情况:部分条件满足 WHERE emp_salary>?
第三种情况:所有条件都不满足 没有where子句
-->
</where>
</select> - set标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<!-- void updateEmployeeDynamic(Employee employee) -->
<update id="updateEmployeeDynamic">
update t_emp
<!-- set emp_name=#{empName},emp_salary=#{empSalary} -->
<!-- 使用set标签动态管理set子句,并且动态去掉两端多余的逗号 -->
<set>
<if test="empName != null">
emp_name=#{empName},
</if>
<if test="empSalary < 3000">
emp_salary=#{empSalary},
</if>
</set>
where emp_id=#{empId}
<!--
第一种情况:所有条件都满足 SET emp_name=?, emp_salary=?
第二种情况:部分条件满足 SET emp_salary=?
第三种情况:所有条件都不满足 update t_emp where emp_id=?
没有set子句的update语句会导致SQL语法错误
-->
</update> - choose/when/otherwise标签
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<!-- List<Employee> selectEmployeeByConditionByChoose(Employee employee) -->
<select id="selectEmployeeByConditionByChoose" resultType="com.atguigu.mybatis.entity.Employee">
select emp_id,emp_name,emp_salary from t_emp
where
<choose>
<when test="empName != null">emp_name=#{empName}</when>
<when test="empSalary < 3000">emp_salary < 3000</when>
<otherwise>1=1</otherwise>
</choose>
<!--
第一种情况:第一个when满足条件 where emp_name=?
第二种情况:第二个when满足条件 where emp_salary < 3000
第三种情况:两个when都不满足 where 1=1 执行了otherwise
-->
</select> - foreach
1
2
3
4
5
6
7
8
9
10
11
12
13
14<!--
collection属性:要遍历的集合
item属性:遍历集合的过程中能得到每一个具体对象,在item属性中设置一个名字,将来通过这个名字引用遍历出来的对象
separator属性:指定当foreach标签的标签体重复拼接字符串时,各个标签体字符串之间的分隔符
open属性:指定整个循环把字符串拼好后,字符串整体的前面要添加的字符串
close属性:指定整个循环把字符串拼好后,字符串整体的后面要添加的字符串
index属性:这里起一个名字,便于后面引用
遍历List集合,这里能够得到List集合的索引值
遍历Map集合,这里能够得到Map集合的key
-->
<foreach collection="empList" item="emp" separator="," open="values" index="myIndex">
<!-- 在foreach标签内部如果需要引用遍历得到的具体的一个对象,需要使用item属性声明的名称 -->
(#{emp.empName},#{myIndex},#{emp.empSalary},#{emp.empGender})
</foreach> - sql片段
- 抽取重复片段
1
2
3
4<!-- 使用sql标签抽取重复出现的SQL片段 -->
<sql id="mySelectSql">
select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp
</sql> - 引用
1
2<!-- 使用include标签引用声明的SQL片段 -->
<include refid="mySelectSql"/>
3.mybatis-plus
快速入门
- 依赖
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.5</version>
</parent>
<groupId>com.atguigu</groupId>
<artifactId>springboot-starter-mybatis-plus-06</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- 测试环境 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- 数据库相关配置启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- druid启动器的依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>1.2.18</version>
</dependency>
<!-- 驱动类-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
</dependencies>
<!-- SpringBoot应用打包插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project> - yaml
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# 连接池配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
url: jdbc:mysql:///day01
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus: # mybatis-plus的配置
# 默认位置 private String[] mapperLocations = new String[]{"classpath*:/mapper/**/*.xml"};
mapper-locations: classpath:/mapper/*.xml
configuration:
# 配置MyBatis日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
# 配置MyBatis-Plus操作表的默认前缀
table-prefix: t_
# 配置MyBatis-Plus的主键策略
id-type: auto
# 全局指定逻辑删除
logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) - 启动类
1
2
3
4
5
6
7
8
9
10
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class,args);
}
} - mapper接口
1
2public interface UserMapper extends BaseMapper<User> {
} - service接口
1
2public interface UserService extends IService<User> {
} - ServiceImpl
1
2
3
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService{
} - mapper.xml
1
2
3
4
5
6
7
8
9
10
11
<!-- namespace = 接口的全限定符 -->
<mapper namespace="com.atguigu.mapper.UserMapper">
<select id="queryAll" resultType="user" >
select * from user
</select>
</mapper>
3.1基于Mapper的CRUD
Insert方法1 | // 插入一条记录 |
1 | // 根据 ID 删除 |
1 | // 根据 whereWrapper 条件,更新记录 |
1 | // 根据 ID 查询 |
3.2基于Service的CRUD
1 | 保存: |
3.3条件构造器
Wrapper : 条件构造抽象类,最顶端父类
- AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
- QueryWrapper : 查询/删除条件封装
- UpdateWrapper : 修改条件封装
- AbstractLambdaWrapper : 使用Lambda 语法
- LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
- LambdaUpdateWrapper : Lambda 更新封装Wrapper
UpdateWrapper 可以把字段设置为null
组装查询条件
1
2
3
4
5
6
7
8
9
10
11
public void test01(){
//查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("username", "a")
.between("age", 20, 30)
.isNotNull("email");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println); 组装排序条件
1
2
3
4
5
6
7
8
9
10
11
public void test02(){
//按年龄降序查询用户,如果年龄相同则按id升序排列
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,id ASC
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.orderByDesc("age")
.orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
} 组装删除条件
1
2
3
4
5
6
7
8
9
10
public void test03(){
//删除email为空的用户
//DELETE FROM t_user WHERE (email IS NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
//条件构造器也可以构建删除语句的条件
int result = userMapper.delete(queryWrapper);
System.out.println("受影响的行数:" + result);
} and和or关键字使用(修改)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//将年龄大于20并且用户名中包含有a或邮箱为null的用户信息修改
//UPDATE t_user SET age=?, email=? WHERE username LIKE ? AND age > ? OR email IS NULL)
queryWrapper
.like("username", "a")
.gt("age", 20)
.or()
.isNull("email");
User user = new User();
user.setAge(18);
user.setEmail("user@atguigu.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影响的行数:" + result);
} 指定列映射查询
1
2
3
4
5
6
7
8
9
10
public void test05() {
//查询用户信息的username和age字段
//SELECT username,age FROM t_user
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("username", "age");
//selectMaps()返回Map集合列表,通常配合select()使用,避免User对象中没有被查询到的列值为null
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
} condition判断组织条件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void testQuick3(){
String name = "root";
int age = 18;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//判断条件拼接
//当name不为null拼接等于, age > 1 拼接等于判断
//方案1: 手动判断
if (!StringUtils.isEmpty(name)){
queryWrapper.eq("name",name);
}
if (age > 1){
queryWrapper.eq("age",age);
}
//方案2: 拼接condition判断
//每个条件拼接方法都condition参数,这是一个比较运算,为true追加当前条件!
//eq(condition,列名,值)
queryWrapper.eq(!StringUtils.isEmpty(name),"name",name)
.eq(age>1,"age",age);
} LambdaQueryWrapper示例代码
1
2
3
4
5
6
7LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(User::getName, "John")
.ge(User::getAge, 18)
.orderByDesc(User::getCreateTime)
.last("limit 10");
List<User> userList = userMapper.selectList(lambdaQueryWrapper);
3.4注解
- @TableName—-
表名注解
- @TableId—-
主键注解
- @TableField—-
字段注解
- @TableLogic—-
逻辑删除
全局指定逻辑删除1
2
3
4
5
6
7
8
9
10
11
12
13
public class User {
private Long id;
private String name;
private Integer age;
private String email;
//逻辑删除字段 int mybatis-plus下,默认 逻辑删除值为1 未逻辑删除 1
private Integer deleted;
}1
2
3
4
5
6mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)4.SpringMVC
4.1请求路径
@RequestMapping
@GetMapping
@PostMapping
@PutMapping 更新全部资源
@DeleteMapping
@PatchMapping 更新部分资源
4.2接收参数
param和JSON比较
- 参数编码:param是ASCII码,json是UTF-8
- 参数顺序:param没有限制
- 数据类型:param简单的数据类型
- 嵌套性:param不支持嵌套
- 可读性:param简单易读,json结构清晰
param参数接收
- 直接接收
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ParamController {
/**
* 前端请求: http://localhost:8080/param/value?name=xx&age=18
*
* 可以利用形参列表,直接接收前端传递的param参数!
* 要求: 参数名 = 形参名
* 类型相同
* 出现乱码正常,json接收具体解决!!
* @return 返回前端数据
*/
public String setupForm(String name,int age){
System.out.println("name = " + name + ", age = " + age);
return name + age;
}
} - @RequestParam注解
1
2
3
4
5
6
7
8
9
10
11
12
13/**
* 前端请求: http://localhost:8080/param/data?name=xx&stuAge=18
*
* 使用@RequestParam注解标记handler方法的形参
* 指定形参对应的请求参数@RequestParam(请求参数名称)
*/
public Object paramForm( String name,
int age){
System.out.println("name = " + name + ", age = " + age);
return name+age;
} - 特殊场景接值
- 一名多值(多选框)
1
2
3
4
5
6
7
8
9
10
11/**
* 前端请求: http://localhost:8080/param/mul?hbs=吃&hbs=喝
*
* 一名多值,可以使用集合接收即可!但是需要使用@RequestParam注解指定
*/
public Object mulForm({ List<String> hbs)
System.out.println("hbs = " + hbs);
return hbs;
} - 实体类接收
1
2
3
4
5
6
7
8
9
10
11
12
public class ParamController {
public String addUser(User user) {
// 在这里可以使用 user 对象的属性来接收请求参数
System.out.println("user = " + user);
return "success";
}
} - 路径接收
1
2
3
4
5
6
7
8
9
10
11
12
13
14/**
* 动态路径设计: /user/{动态部分}/{动态部分} 动态部分使用{}包含即可! {}内部动态标识!
* 形参列表取值: @PathVariable Long id 如果形参名 = {动态标识} 自动赋值!
* @PathVariable("动态标识") Long id 如果形参名 != {动态标识} 可以通过指定动态标识赋值!
*
* 访问测试: /param/user/1/root -> id = 1 uname = root
*/
public String getUser( Long id,
{ String uname)
System.out.println("id = " + id + ", uname = " + uname);
return "user_detail";
} - json参数接收
- 前端请求的json数据
1
2
3
4
5{
"name": "张三",
"age": 18,
"gender": "男"
} - 定义接收json的java类
1
2
3
4
5
6public class Person {
private String name;
private int age;
private String gender;
// getter 和 setter 略
} - Controller
1
2
3
4
5
6
public String addPerson( { Person person)
// 在这里可以使用 person 对象来操作 JSON 数据中包含的属性
return "success";
}
4.3接收cookie数据
考虑使用以下 cookie 的请求:1
JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84
下面的示例演示如何获取 cookie 值:1
2
3
4
public void handle( { String cookie)
//...
}
4.4接收请求头数据
请考虑以下带有标头的请求:1
2
3
4
5
6Host localhost:8080
Accept text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding gzip,deflate
Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 300
下面的示例获取 Accept-Encoding
和 Keep-Alive
标头的值:1
2
3
4
5
6
public void handle(
String encoding,
long keepAlive) {
//...
}
4.5校验注解
注解 | 规则 |
---|---|
@Null | 标注值必须为 null |
@NotNull | 标注值不可为 null |
@AssertTrue | 标注值必须为 true |
@AssertFalse | 标注值必须为 false |
@Min(value) | 标注值必须大于或等于 value |
@Max(value) | 标注值必须小于或等于 value |
@DecimalMin(value) | 标注值必须大于或等于 value |
@DecimalMax(value) | 标注值必须小于或等于 value |
@Size(max,min) | 标注值大小必须在 max 和 min 限定的范围内 |
@Digits(integer,fratction) | 标注值值必须是一个数字,且必须在可接受的范围内 |
@Past | 标注值只能用于日期型,且必须是过去的日期 |
@Future | 标注值只能用于日期型,且必须是将来的日期 |
@Pattern(value) | 标注值必须符合指定的正则表达式 |
标注值必须是格式正确的 Email 地址 | |
@Length | 标注值字符串大小必须在指定的范围内 |
@NotEmpty | 标注值字符串不能是空字符串 |
@Range | 标注值必须在指定的范围内 |
- 导入依赖(二选一)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21<!-- 校验注解 -->
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-web-api</artifactId>
<version>9.1.0</version>
<scope>provided</scope>
</dependency>
<!-- 校验注解实现-->
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.0.Final</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-annotation-processor</artifactId>
<version>8.0.0.Final</version>
</dependency>
1 | <dependency> |
- 注解应用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public class User {
//age 1 <= age < = 150
private int age;
//name 3 <= name.length <= 6
private String name;
//email 邮箱格式
private String email;
public int getAge() {
return age;
}
} - handler标记和绑定错误收集
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class UserController {
/**
* @Validated 代表应用校验注解! 必须添加!
*/
public Object save( User user,
//在实体类参数和 BindingResult 之间不能有任何其他参数, BindingResult可以接受错误信息,避免信息抛出!
BindingResult result){
//判断是否有信息绑定错误! 有可以自行处理!
if (result.hasErrors()){
System.out.println("错误");
String errorMsg = result.getFieldError().toString();
return errorMsg;
}
//没有,正常处理业务即可
System.out.println("正常");
return user;
}
}
5.接口文档的使用
查看请求路径和方式
请求路径:/depts/{id}
请求方式:DELETE
接口描述:该接口用于根据ID删除部门数据看请求参数
请求参数样例(路径传参)
—- @PathVariable1
/depts/1
请求参数样例(json传参)
—- @RequestBody 可以自己创建一个VO类传参1
2
3{
"name": "教研部"
}请求数据样例(Param传参)
—- @RequestParam1
2
3
4
5Integer page,
Integer pageSize,
String name, Short gender,
LocalDate begin,
LocalDate end1
/emps?name=张&gender=1&begin=2007-09-01&end=2022-09-01&page=1&pageSize=10
响应数据
响应数据样例 —- 可以自己创建一个DTO进行响应 —- 数据要一一对应1
2
3
4
5
6{
"id": 1,
"name": "学工部",
"createTime": "2022-09-01T23:06:29",
"updateTime": "2022-09-01T23:06:29"
}响应数据样例 —- 创建一个List集合接收并返回
1
2
3
4
5
6
7
8
9
10
11
12
13
14[
{
"id": 1,
"name": "学工部",
"createTime": "2022-09-01T23:06:29",
"updateTime": "2022-09-01T23:06:29"
},
{
"id": 2,
"name": "教研部",
"createTime": "2022-09-01T23:06:29",
"updateTime": "2022-09-01T23:06:29"
}
]
6.登录功能
mybatis+jwt+token+拦截器+明文密码
- 导入依赖
1
2
3
4
5
6
7
8
9
10
11<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency> - 编写配置类
1
2
3
4
5#jwt配置
jwt:
token:
tokenExpiration: 120 #有效时间,单位分钟
tokenSignKey: headline123456 #当前程序签名秘钥 自定义 - 导入工具类
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62package com.atguigu.utils;
import com.alibaba.druid.util.StringUtils;
import io.jsonwebtoken.*;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.util.Date;
public class JwtHelper {
private long tokenExpiration; //有效时间,单位毫秒 1000毫秒 == 1秒
private String tokenSignKey; //当前程序签名秘钥
//生成token字符串
public String createToken(Long userId) {
System.out.println("tokenExpiration = " + tokenExpiration);
System.out.println("tokenSignKey = " + tokenSignKey);
String token = Jwts.builder()
.setSubject("YYGH-USER")
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration*1000*60)) //单位分钟
.claim("userId", userId)
.signWith(SignatureAlgorithm.HS512, tokenSignKey)
.compressWith(CompressionCodecs.GZIP)
.compact();
return token;
}
//从token字符串获取userid
public Long getUserId(String token) {
if(StringUtils.isEmpty(token)) return null;
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
Integer userId = (Integer)claims.get("userId");
return userId.longValue();
}
//判断token是否有效
public boolean isExpiration(String token){
try {
boolean isExpire = Jwts.parser()
.setSigningKey(tokenSignKey)
.parseClaimsJws(token)
.getBody()
.getExpiration().before(new Date());
//没有过期,有效,返回false
return isExpire;
}catch(Exception e) {
//过期出现异常,返回true
return true;
}
}
} - Controller
1
2
3
4
5
6
7
8//登录功能
public Result login({ Emp emp)
Result result = empService.login(emp);
System.out.println(result);
return result;
} - Service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public Result login(Emp emp) {
//根据账号查询账号
Emp loginUser = empMapper.selectOne(emp);
//判断账号是否为空
if (loginUser == null){
return Result.error("账号不存在");
}
//判断密码是否为空,且密码是否正确
if (!StringUtils.isEmpty(loginUser.getPassword())
&& loginUser.getPassword().equals(emp.getPassword())){
//生成token
String token = jwtHelper.createToken(Long.valueOf(loginUser.getId()));
return Result.success(token);
}
return Result.error("密码错误");
} - 创建拦截器
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
40package com.itheima.interceptor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.until.JwtHelper;
import com.itheima.pojo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Process01Interceptor implements HandlerInterceptor {
private JwtHelper jwtHelper;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
//判断token是否为空或过期
if (StringUtils.isEmpty(token) || jwtHelper.isExpiration(token)){
//设置响应参数
Result result = Result.error("NOT_LOGIN");
//创建把参数转换为json的对象
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(result);
response.getWriter().print(json);
//拦截
return false;
}else{
//放行
return true;
}
}
} - 拦截器配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21package com.itheima.config;
import com.itheima.interceptor.Process01Interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
public class WebMvcConfig implements WebMvcConfigurer {
//拦截器配置
private Process01Interceptor process01Interceptor;
public void addInterceptors(InterceptorRegistry registry) {
//设置拦截除了/login的所有路径
registry.addInterceptor(process01Interceptor).excludePathPatterns("/login");
}
} - 统一返回值类型
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
28package com.itheima.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
public class Result {
private Integer code;//响应码,1 代表成功; 0 代表失败
private String msg; //响应信息 描述字符串
private Object data; //返回的数据
//增删改 成功响应
public static Result success(){
return new Result(1,"success",null);
}
//查询 成功响应
public static Result success(Object data){
return new Result(1,"success",data);
}
//失败响应
public static Result error(String msg){
return new Result(0,msg,null);
}
}
7.分页查询
mybatis+pagehelper插件
- 导入依赖
1
2
3
4
5
6<!--分页插件依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency> - 实现
1
2
3
4
5
6
7
8
9
10
11
12
public PageBean page(Integer page, Integer pageSize, String name, Short gender, LocalDate begin, LocalDate end) {
//设置分页参数
PageHelper.startPage(page,pageSize);
//进行模糊查询
List<Emp> emplist = empMapper.list(name, gender, begin,end);
//Page是插件提供的
Page<Emp> p=(Page<Emp>)emplist;
//封装返回值
PageBean pageBean=new PageBean(p.getTotal(),p.getResult());
return pageBean;
}
mybatis-plus+分页插件
- 分页插件的原理
首先分页参数放到 ThreadLocal 中,拦截执行的 sql,根据数据库类型添加对应的分页语句重写 sql,例如:(select from table where a) 转换为 (select count (※) from table where a) 和 (select from table where a limit ,)
计算出了 total 总条数、pageNum 当前第几页、pageSize 每页大小和当前页的数据,是否为首页,是否为尾页,总页数等。
- 配置分页插件的配置类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package com.xuecheng.content.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
public class MybatisPlusConfig {
/**
* 定义分页拦截器
*/
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
} - 测试类
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
public class CourseBaseInfoServiceImpl implements CourseBaseInfoService {
CourseBaseMapper courseBaseMapper;
//课程分页查询和条件查询
public PageResult<CourseBase> queryCourseBaseList(PageParams pageParams, QueryCourseParamsDto courseParamsDto) {
//拼装查询条件
LambdaQueryWrapper<CourseBase> queryWrapper = new LambdaQueryWrapper<>();
//根据名称模糊查询,在sql中拼接 course_base.name like '%值%'
queryWrapper.like(StringUtils.isNotEmpty(courseParamsDto.getCourseName())
,CourseBase::getName
,courseParamsDto.getCourseName());
//根据课程审核状态查询,在sql中拼接 course_base.audit_status = ? queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getAuditStatus())
,CourseBase::getAuditStatus
,courseParamsDto.getAuditStatus());
//根据课程状态查询
queryWrapper.eq(StringUtils.isNotEmpty(courseParamsDto.getPublishStatus())
,CourseBase::getStatus
,courseParamsDto.getPublishStatus());
//创建Page分页参数对象 ,参数:页码和每页记录数
Page<CourseBase> Page = new Page<>(pageParams.getPageNo(),pageParams.getPageSize());
//开始进行分页查询
Page<CourseBase> pageResult = courseBaseMapper.selectPage(Page, queryWrapper);
//数据列表
List<CourseBase> items = pageResult.getRecords();
//总记录数
long total = pageResult.getTotal();
//List<T> items , Long counts , Long page , Long ,pageSize
//封装返回参数
PageResult<CourseBase> courseBasePageResult =
new PageResult<CourseBase>(items,total,pageParams.getPageNo()
,pageParams.getPageSize());
System.out.println(courseBasePageResult);
return courseBasePageResult;
}
}
8.全局异常处理和自定义异常处理
- 设置通用信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public enum CommonError {
//定义枚举类
UNKOWN_ERROR("执行过程异常,请重试。"),
PARAMS_ERROR("非法参数"),
OBJECT_NULL("对象为空"),
QUERY_NULL("查询结果为空"),
REQUEST_NULL("请求参数为空");
private String errMessage;
public String getErrMessage() {
return errMessage;
}
//有参构造
private CommonError( String errMessage) {
this.errMessage = errMessage;
}
} - 自定义异常处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public class XueChengPlusException extends RuntimeException {
private String errMessage;
//无参构造
public XueChengPlusException() {
super();
}
//有参构造
public XueChengPlusException(String errMessage) {
super(errMessage);
this.errMessage = errMessage;
}
//get方法
public String getErrMessage() {
return errMessage;
}
//抛异常直接调用 XueChengPlusException(参数)
public static void cast(CommonError commonError){
throw new XueChengPlusException(commonError.getErrMessage());
}
public static void cast(String errMessage){
throw new XueChengPlusException(errMessage);
}
} - 响应用户的统一类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14import java.io.Serializable;
//和前端约定返回的异常信息模型
public class RestErrorResponse implements Serializable {
private String errMessage;
public RestErrorResponse(String errMessage){
this.errMessage= errMessage;
}
public String getErrMessage() {
return errMessage;
}
public void setErrMessage(String errMessage) {
this.errMessage = errMessage;
}
} - 全局异常处理
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
34import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
//@RestControllerAdvice
public class GlobalExceptionHandler {
//全局异常处理
public RestErrorResponse exception(Exception e){
//记录异常
log.error("系统异常{}",e.getMessage(),e);
//解析出异常信息
RestErrorResponse restErrorResponse = new RestErrorResponse(CommonError.UNKOWN_ERROR.getErrMessage());
return restErrorResponse;
}
//自定义异常处理
public RestErrorResponse customException(XueChengPlusException e){
//记录异常
log.error("系统异常{}",e.getErrMessage(),e);
//..
//解析出异常信息
String errMessage = e.getErrMessage();
RestErrorResponse restErrorResponse = new RestErrorResponse(errMessage);
return restErrorResponse;
}
}