此处你们写代码的时候,可以直接在数据表中用一个字段来描述状态。这个是最简单的方式。
如果有能力的同学,可以上Active()
就是做工作流的。
### 数据库
常见的SQL的问题:
你们设计数据表的时候不要给我用外键。
初级程序员:SQL的查询。推荐你们好好去看一下SQL优化。
钱,不要用float,double。包含Java。原因是以二进制存储的,有一定的精度丢失。
```mssql
#货币一定要用decimal
#创建数据库的表
create table a(
id int auto_increment primary key,
a float, #那么此处改成double,仍然会存在精度丢失
b decimal(9,2) #长度为9,保留两位小数 货币类型
)engine innodb;
#插入一些数据
insert into a values (null,1234567.23,1234567.23);
#查询SQL语句
select * from a;
```
开发中如果你用了主键,主键采用了雪花算法等,或者Long等,长度比较大的值的时候,你一定要用String
```
安全范围外会精度丢失:JavaScript 的 Number 遵循 IEEE 754 双精度浮点标准,仅能精确表示 ±(2⁵³ − 1) 即 ±9,007,199,254,740,991(约 16 位十进制整数);
Java long 最大值为 2⁶³−1 ≈ 9.22×10¹⁸(可达 19 位),超过安全整数的值(如雪花 ID、大金额)在 JSON 解析时会被静默截断/四舍五入,导致数据错误。
```
把数据类型改成String就彻底没有这个问题了。
设计数据库表的时候,再加一个范式,主键无关性。
性别应该用 char(2) varchar(2) bit [男,女] int
下面这个图,是我用PowerDesinger画的物理数据模型。能够直接生成SQL语句,有兴趣自己建立一下pdm。当然你们给我提交的时候,可以用sql语句给我,用word文档给我,用pdm给我都可以。

### 作业
1:给我把该装的软件先装上,
2:需要你们把需求分析文档完成。相关UML图像完成。
3:完成数据库表的构建。需要你们自己把这个表建完。
## 数据库
数据库的索引。index。B+树。索引为什么这么快?
海量数据如何分页?
```
mysql的默认分页方式
select * from tblteacher
#mysql 使用limit关键字进行分页
select * from tblteacher limit 8,3
```
需要扫描并丢弃第一个参数offset的数据。深分页的时候表开销巨大。数据量特别大的时候,查询前100页是没有问题的,但是你查询最后几页的时候,速度特别慢。
先模拟网数据库里插入大量数据。
```
#建表的语句
create table testindex(
uid int auto_increment primary key, #主键
username varchar(20), #用户名
age int #年龄
)ENGINE=innodb charset=UTF8;
#模拟插入一些数据
#随机生名字
select SUBSTRING('赵钱孙李周吴郑王',3,1) #孙
select SUBSTRING('赵钱孙李周吴郑王',4,1) #李
#把上面的第二个参数改为随机数
#100以内的随机数
select FLOOR(RAND()*100)
select FLOOR(1+RAND()*8)
##随机的姓
select SUBSTRING('赵钱孙李周吴郑王',FLOOR(1+RAND()*8),1) #随机的姓
#随机的名
select SUBSTRING('玲凤菜富贵钱国军才华',FLOOR(1+RAND()*10),1)
#随机姓名 mysql字符串链接要用concat函数
select concat('a','b')
#完全随机的名字
select concat(SUBSTRING('赵钱孙李周吴郑王',FLOOR(1+RAND()*8),1),SUBSTRING('玲凤菜富贵钱国军才华',FLOOR(1+RAND()*10),1) ,SUBSTRING('玲凤菜富贵钱国军才华',FLOOR(1+RAND()*10),1) )
#随机的年龄
select FLOOR(1+RAND()*100)
#插入数据
insert into testindex values (null,concat(SUBSTRING('赵钱孙李周吴郑王',FLOOR(1+RAND()*8),1),SUBSTRING('玲凤菜富贵钱国军才华',FLOOR(1+RAND()*10),1) ,SUBSTRING('玲凤菜富贵钱国军才华',FLOOR(1+RAND()*10),1) ),FLOOR(1+RAND()*100))
select * from testindex;
#直接用表复制,来提供速度
select null,username,age from testindex;
#然后你就使劲的运行下面的这个语句,至到你觉得够了为止
insert into testindex select null,username,FLOOR(1+RAND()*100) from testindex
```
```
select * from testindex;
select count(*) from testindex;
#数据量特别大的时候分页
select * from testindex limit 20,10
select * from testindex limit 9000000,10 #查询效率比前几页低了10几倍
#而且如何我加了where条件之后,这个查询结果就很恐怖 。
#不想更改任何内容,就在现有的条件下,提高查询效率
#游标分页 这个是推荐的方式
select * from testindex where uid>=9327540 limit 10
上面这个SQL语句和上上面的执行结果是一致的,但是执行效率快了10几倍
#延迟关联
#先查询出分页的uid,再做关联查询
#这种关联查询速度会快于直接limit但是会慢于游标分页
select * from testindex a inner join
(select uid from testindex limit 9000000,10) b
on a.uid=b.uid
```
**#B+树索引 只适合建立高基数的列中**
因为像性别,学历,婚否这些东西都属于低基数的列,加了索引反而速度不快。
那么低基数的列,应该用BitMap索引。但是免费版没有。
```
select * from testindex where uid=9000000
update testindex set age =102 where uid=9000000
#800多万数据 没有加索引 查询时间 2.23秒
select * from testindex where age=102;
#给当前age列加索引
create index idx_test_age on testindex(age);
#索引已经建立过了 索引建立之后,会自动使用,只要你的SQL写的ok
#索引建立后,时间变为0.12秒
select * from testindex where age=102;
```
数据结构的学习网站
```
https://www.cs.usfca.edu/~galles/visualization/Algorithms.html
```
任务:把需求分析,概要设计,详细设计三个文档。
## SpringBoot
### 最基础的SpringBoot:
一个控制器+Swagger。
1: 建立项目
2:新建模块
3:设置
4:更改模型中的pom.xml文件
```xml
6:Springboot基础环境就搭建完了。
在核心启动类上鼠标右键,运行,就ok了。
```
Tomcat started on port 8080 (http) with context path ''
```
404也是成功
### 最简单的控制器
新建Java类

写入controller.TestController,则完成控制器类的构建。
```java
package com.han.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
//所有的SpringBoot的控制器,都必须添加注解
@RestController //只有加了这个注解才能被SpringBoot扫描到
public class TestController {
@GetMapping("/say") //将来你也页面的请求地址:http://localhost:8080/say
//http://127.0.0.1:8080/say
public String say(){
return "你好,河北理工大学!";
}
}
```
```
localhost 本机
127.0.0.1 本机
192.168.43.189 这个是我电脑的ip不是你的
主机名也可以正常访问
```
```
package com.han.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
//所有的SpringBoot的控制器,都必须添加注解
@RestController //只有加了这个注解才能被SpringBoot扫描到
public class TestController {
@GetMapping("/say") //将来你也页面的请求地址:http://localhost:8080/say
//http://127.0.0.1:8080/say
public String say(){ //这个代码返回的事String
return "你好,河北理工大学!";
}
@GetMapping("/say2")
public List say2(){
List list = new ArrayList();
list.add("张三");
list.add("李四");
list.add("王五");
return list;
}
}
```

第三段代码
```java
package com.han.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
//所有的SpringBoot的控制器,都必须添加注解
@RestController //只有加了这个注解才能被SpringBoot扫描到
public class TestController {
@GetMapping("/say") //将来你也页面的请求地址:http://localhost:8080/say
//http://127.0.0.1:8080/say
public String say(){ //这个代码返回的事String
return "你好,河北理工大学!";
}
@GetMapping("/say2")
public List say2(){
List list = new ArrayList();
list.add("张三");
list.add("李四");
list.add("王五");
return list;
}
//新版本的Post参数接受,必须要加注解
//如果你是基础数据类型 @RequestParam
//如果你是对象类型 @RequestBody 我要把接受的数据全部转成json
@PostMapping("/save") //这个代码的请求方式是Post 你是不可以在页面直接敲地址
public String save(@RequestParam("username") String username){ //就是我们将来的增加代码
return "保存成功";
}
}
```
### Swagger
写完项目后,要写接口文档。swagger能够帮助你生成接口文档(直接给前端了),也便于我们自己来做测试。
#### 1:改pom.xml 加入
```
### 如何打jar包
更改你的pom.xml
```

已经生完了。
mapper/AqiFeedBackMapper: MybatisPlus官方的持久层类 如果你今后要写改SQL应该在此处
service/IAqiFeedBackService: 服务层的接口类,逻辑层的接口类
AqiFeedBackServiceImpl: 上面接口的实现类,你今后有逻辑的代码,写在此处。
controller/AqiFeedbackController: 这个就是控制器的类,这个类得你自己写了。
#### Junit
就是一个单元测试的工具。来测试你的代码有没有问题的地方。仅仅只用来做测试。
@Test注解必须加在每个方法上。
```java
package com.han.demo;
import com.han.demo.entity.AqiFeedback;
import com.han.demo.service.IAqiFeedbackService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
class DemoApplicationTests {
@Test
void contextLoads() {
}
@Resource
private IAqiFeedbackService aqiFeedbackService;
@Test
void testselect(){
System.out.println("测试");
List* 前端控制器 *
* * @author laohan * @since 2026-09-03 */ @RestController @RequestMapping("/aqiFeedback") @AllArgsConstructor public class AqiFeedbackController { private final IAqiFeedbackService aqiFeedbackService; @GetMapping("/select") public List select(){ return aqiFeedbackService.list() ; } } ``` 启动类 ```java package com.han.demo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @MapperScan("com.han.demo.mapper") //告诉srpging容器 Mybatis在哪里找 public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` ``` http://localhost:9000/aqiFeedback/select ```  早上的问题: ``` jdk17 两个地方牵扯数据库: application.yml CodeGenerate2.java 文件中 ``` #### 控制器CRUD的方法AqiFeedbackController ```java package com.han.demo.controller; import com.han.demo.entity.AqiFeedback; import com.han.demo.service.IAqiFeedbackService; import jakarta.annotation.Resource; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.*; import java.util.List; /** ** 前端控制器 *
* * @author laohan * @since 2026-09-03 */ @RestController @RequestMapping("/aqiFeedback") @AllArgsConstructor public class AqiFeedbackController { private final IAqiFeedbackService aqiFeedbackService; /** * 查询所有反馈 * @return */ @GetMapping("/select") public List select(){ return aqiFeedbackService.list() ; } /** * 根据id查询反馈信息 * @param afId * @return */ // @GetMapping("/find") // find?afid=1 @GetMapping("/find/{afId}") //RestFul 风格 、/find/1 public AqiFeedback findById(@PathVariable Integer afId ){ AqiFeedback feedback = aqiFeedbackService.getById(afId); return feedback; } @GetMapping("/delete/{afId}") //RestFul 风格 、/find/1 public boolean delete(@PathVariable Integer afId ){ return aqiFeedbackService.removeById(afId); } @PostMapping("/save") @ResponseBody public boolean save(@RequestBody AqiFeedback feedback){ return aqiFeedbackService.save( feedback); } @PostMapping("/update") @ResponseBody public boolean update(@RequestBody AqiFeedback feedback){ return aqiFeedbackService.updateById( feedback); } } ``` #### common/ResultVO ``` package com.han.demo.common; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.io.Serializable; @NoArgsConstructor @AllArgsConstructor @Data public class ResultVO* 前端控制器 *
* * @author laohan * @since 2026-09-03 */ @RestController @RequestMapping("/aqiFeedback") @AllArgsConstructor public class AqiFeedbackController { private final IAqiFeedbackService aqiFeedbackService; /** * 查询所有反馈 * @return */ @GetMapping("/select") public ResultVO select(){ List list = aqiFeedbackService.list() ; return new ResultVO(200,"查询成功",list); } /** * 根据id查询反馈信息 * @param afId * @return */ // @GetMapping("/find") // find?afid=1 @GetMapping("/find/{afId}") //RestFul 风格 、/find/1 public ResultVO findById(@PathVariable Integer afId ){ AqiFeedback feedback = aqiFeedbackService.getById(afId); return new ResultVO(200,"查询成功",feedback); } @GetMapping("/delete/{afId}") //RestFul 风格 、/find/1 public ResultVO delete(@PathVariable Integer afId ){ boolean success = aqiFeedbackService.removeById(afId); return new ResultVO(200,"删除成功",success); } @PostMapping("/save") @ResponseBody public ResultVO save(@RequestBody AqiFeedback feedback){ boolean success = aqiFeedbackService.save( feedback); return new ResultVO(200,"保存成功",success); } @PostMapping("/update") @ResponseBody public ResultVO update(@RequestBody AqiFeedback feedback){ boolean success = aqiFeedbackService.updateById( feedback); return new ResultVO(200,"更新成功",success); } } ``` #### 异常处理 ```java package com.han.demo.common; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice //会在所有controller中进行异常处理 public class GlobalExceptionHandler { //处理所有的异常 @ExceptionHandler(Exception.class) public ResultVO handleException(Exception e){ return new ResultVO(500,"操作失败",e.getMessage()); } //处理参数错误的异常 @ExceptionHandler(IllegalArgumentException.class) public ResultVO handleIllegalArgumentException(IllegalArgumentException e){ return new ResultVO(400,"参数错误",e.getMessage()); } } ``` 有了这个全局异常处理类,今后你就不用管异常了。 最后的控制器 ```java package com.han.demo.controller; import com.han.demo.common.ResultVO; import com.han.demo.entity.AqiFeedback; import com.han.demo.service.IAqiFeedbackService; import jakarta.annotation.Resource; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.*; import java.util.List; /** ** 前端控制器 *
* * @author laohan * @since 2026-09-03 */ @RestController @RequestMapping("/aqiFeedback") @AllArgsConstructor public class AqiFeedbackController { private final IAqiFeedbackService aqiFeedbackService; /** * 查询所有反馈 * @return */ @GetMapping("/select") public ResultVO select(){ List list = aqiFeedbackService.list() ; return new ResultVO(200,"查询成功",list); } /** * 根据id查询反馈信息 * @param afId * @return */ // @GetMapping("/find") // find?afid=1 @GetMapping("/find/{afId}") //RestFul 风格 、/find/1 public ResultVO findById(@PathVariable Integer afId ){ AqiFeedback feedback = aqiFeedbackService.getById(afId); return new ResultVO(200,"查询成功",feedback); } @GetMapping("/delete/{afId}") //RestFul 风格 、/find/1 public ResultVO delete(@PathVariable Integer afId ){ boolean success = aqiFeedbackService.removeById(afId); return new ResultVO(200,"删除成功",success); } @PostMapping("/save") @ResponseBody public ResultVO save(@RequestBody AqiFeedback feedback){ boolean success = aqiFeedbackService.save( feedback); return new ResultVO(200,"保存成功",success); } @PostMapping("/update") @ResponseBody public ResultVO update(@RequestBody AqiFeedback feedback){ boolean success = aqiFeedbackService.updateById( feedback); return new ResultVO(200,"更新成功",success); } } ``` #### 日志 很简单。日志很重要。程序出错看控制台。日志中的数据才最完整。 SLF4J application.yml ``` server: port: 9000 springdoc: api-docs: path: /api-docs swagger-ui: path: /swagger-ui.html #数据库的链接配置 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver #数据库驱动 url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowPublicKeyRetrieval=true&useInformationSchema=true&allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&allowPublicKeyRet username: root password: ilovelaohan #这个你们一定要改成自己的 #配置日志 logging: level: root: info #日志级别为 info 几乎是所有日志都是info级别 全部默认已经使用上了 com.han.demo: debug #额外设置这个包的日志级别 debug级别 #还希望经常能看到mybatis生成执行的SQL语句 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #其实也可以把日志放在文件中 #日志的级别 #OFF 全部关闭 #ERROR 系统发生的错误,需要立即关注 #WARN 警告,需要关注,但不 影响系统运行 #INFO 信息,一般不需要关注 #DEBUG 调试信息,一般最多在开发过程中,程序需要调试的时候才打开。 #TRACE 最细颗粒度的日志输出,需要看的内容,需要看的非常详细才打开。 ``` AqiFeedBackController.java ```java @Slf4j public class AqiFeedbackController { private final IAqiFeedbackService aqiFeedbackService; /** * 查询所有反馈 * @return */ @GetMapping("/select") public ResultVO select(){ List list = aqiFeedbackService.list() ; log.info("查询所有反馈信息,级别为info:{}",list); return new ResultVO(200,"查询成功",list); } /** * 根据id查询反馈信息 * @param afId * @return */ @GetMapping("/find/{afId}") //RestFul 风格 、/find/1 public ResultVO findById(@PathVariable Integer afId ){ AqiFeedback feedback = aqiFeedbackService.getById(afId); log.debug("查询反馈信息,级别为info:{}",feedback); return new ResultVO(200,"查询成功",feedback); } } ``` 常识 ``` public class AqiFeedbackController { private Integer age; private Userinfo info; #但实际全部生命周期中只有一份,全局共享。 如果你这个类没有受spring注解描述,没有在spring管理中,只是普通的java类这样写是可以的。 如果是controller,service,mapper你这么写就是找死。 } @Scope("singleton") //单例 spring整个生命中这个对象只有一份实例 只new一次 如果你不该,默认就是单例 @Scope("prototype") //原型 每次请求都new一个对象 在Spring中,你写的实例变量其实就是静态变量。 如果要写变量,请你一定写在参数中。 ``` 1+40000这个结果竟然是随机的 ``` package com.han.demo; public class Test { //从1+1加到40000.理论上应该就是40000. CAS 算法 public static int count = 0; public static void main(String[] args) { for (int i = 0; i < 20; i++) { new Thread(() -> { for (int j = 0; j < 2000; j++) { count++; } }).start(); } //等到上面的代码执行完毕,我想看count的值是多少 while (Thread.activeCount() > 2){ //idea启动运行main,默认2个线程。 Thread.yield(); //线程礼让 让出当前cpu的执行权,让其它线程有执行的机会 } System.out.println(count); } } ``` i++天然存在这个问题。 ``` i = 0 A:getField 0 B:getField 0 C:getField 0 A:add 1 B:add 1 C:add 1 A: putfield 1 B: putfield 1 C: putfield 1 ``` 解决方式1:上锁。 解决方式2:CAS。 ``` i = 0 A:getField 0 存储了主存中的值 0 B:getField 0 存储了主存中的值 0 C:getField 0 存储了主存中的值 0 A:add 1 A: putfield 1 检测我存储的主存的值0 和实际主存值是否相等。如果相等,证明没有人改过 执行 i=1 B:add 1 B: putfield 1 检测我存储的主存的值0 和实际主存值是1。不相等,自旋(再来一遍) B:getField 1 存储了主存中的值 1 B:add 2 B: putfield 2 检测我存储的主存的值1 和实际主存值是1。如果相等,证明没有人改过 执行 这个执行效率远高于synchronized。但是毕竟耗费CPU。 ``` 只要改成AtomicInteger就不存在多线程的问题了。 List集合删除要用从尾删法 ``` package com.han.demo; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; public class Test { public static void main(String[] args) { List| ID | provinceName | cityName | 地址 | afDate | afTime | estimatedGrade | state | telId | information |
|---|---|---|---|---|---|---|---|---|---|
| {{ item.afId }} | {{ item.provinceName }} | {{ item.cityName }} | {{ item.address }} | {{ item.afDate }} | {{ item.afTime }} | {{ getGradeText(item.estimatedGrade) }} | {{ getStateText(item.state )}} | {{ item.telId }} | {{ item.information }} |
| ID | provinceName | cityName | 地址 | afDate | afTime | estimatedGrade | state | telId | information | 操作 |
|---|---|---|---|---|---|---|---|---|---|---|
| {{ item.afId }} | {{ item.provinceName }} | {{ item.cityName }} | {{ item.address }} | {{ item.afDate }} | {{ item.afTime }} | {{ getGradeText(item.estimatedGrade) }} | {{ getStateText(item.state )}} | {{ item.telId }} | {{ item.information }} |
* 前端控制器 *
* * @author laohan * @since 2026-09-08 */ @RestController @RequestMapping("/province") @AllArgsConstructor public class GridProvinceController { private final IGridProvinceService provinceService; //省的service private final IGridCityService cityService; //市的 service @GetMapping("list") // 查询所有省 @CrossOrigin public ResultVO list() { List