发布于 

JavaWeb

简单请求参数

http://localhost:8080/xxx?name=1&age=2

1
2
3
4
5
6
@RequestMapping("/simpleParam")
public String simpleParam(HttpServletRequest request){
String name = request.getParameter("name");
String age = request.getParameter("age");
return name+age;
}
1
2
3
4
5
@RequestMapping("/simpleParam")
public String simpleParam(String name, Integer age){

return name+age;
}

如果参数名不匹配,则使用@RequestParam建立映射

1
2
3
4
5
@RequestMapping("/simpleParam")
public String simpleParam(@RequestParam(name="name",required = false)String username, Integer age){

return username+age;
}

将参数封装到实体类

1
2
3
4
5
@RequestMapping("/simplePojo")
public String simplePojo(User user){

return user.getName() +user.getAge();
}

接收数组参数

http://localhost:8080/xxx?hobby=1&hobby=2&hobby=3

1
2
3
4
5
6
@RequestMapping("/arrayParam")
public String arrayParam(String[] hobby){

System.out.println(Arrays.toString(hobby));
return "ok";
}
1
2
3
4
5
6
@RequestMapping("/arrayParam2")
public String arrayParam2(@RequestParam List<String> hobby){

System.out.println(hobby);
return "ok";
}

日期参数

http://localhost:8080/xxx?updateTime-2022-12-12 10:05:45

1
2
3
4
5
6
@RequestMapping("/arrayParam3")
public String arrayParam3(@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")LocalDateTime updateTime){
System.out.println(updateTime);

return "ok";
}

Json参数

json数据键名与形参属姓名相同,定义pojo类型即可接收参数,需要使用@RequesBody标示

1
2
3
4
5
6
7
8
{
"name":"Tom",
"age":10,
"address":{
"name":"shanghai"
}

}
1
2
3
4
5
6
@RequestMapping("/arrayParam4")
public String arrayParam4(@RequestBody User user){
System.out.println(user);

return "ok";
}

路径参数

通过请求URL直接换地参数用{}来识别路径参数,需要使用@PathVariable来获取路径参数

http://localhost:8080/arrayParam5/1

1
2
3
4
5
6
@RequestMapping("/arrayParam5/{id}")
public String arrayParam5(@PathVariable Integer id){
System.out.println(id);

return "ok";
}

http://localhost:8080/arrayParam5/1/2

@RequestMapping(“/arrayParam5/{id}/{name}”)
public String arrayParam5(@PathVariable Integer id,@PathVariable Integer name){
System.out.println(id+name);

return "ok";

}

相应数据

都来源于注解@ResponseBody只不过@RestController中已经包含

返回字符串就直接但会

实体类对象会转json

数组依然是数组

统一都响应结果

1
2
3
4
5
6
7
8
public class Result{
//相应码,1代表成果,0代表失败
private Integetr code;
//提示信息
private String msg;
//返回数据
private Object data;
}

分层解耦

分层

三层架构:数据访问(Dao)、逻辑处理(Service)、接收请求响应数据(Controller)

  • controller:控制层,,接收前端发送到请求,对请求进行处理,并响应数据。
  • service:业务逻辑层,处理具体的业务逻辑
  • dao:数据访问层(Data Access Object)(持久层),负责数据访问操作,包括增删改查

解藕

  • 内聚:软件中各个功能模块内部的功能联系
  • 耦合:衡量软件中各个层/模块之间的依赖、关联的层度。
  • 软件设计原则:高内聚低耦合

控制反转:IOC,对象的创建控制权由程序自身转移到外部(容器)

依赖注入:DI,容器为应用程序提供运行时,所依赖的资源

Bean对象:IOC容器中传教、管理的对象

IOC&DI入门

控制反转

@Component 将当前类交给IOC容器,成为IOC容器中的bean

依赖注入

@Autowired 运行时,IOC容器会提供该类型的bean对象,并复制给该变量

IOC详解

@Componet 声明bean的基础注解
@Controller 标注在控制器类上
@Service 标注在业务类上
@Repository 标注在数据访问类上(由于mybiatis整合,用到少)

Bean默认名字为首字母小写的类名

如果需要改名字可以在注解后面加括号设置

@Repository(“daoA”)

@SpringBootApplication默认包含扫描器范围为当前包和子包

@ComponentScan为扫描

@ComponentScan({“dao”,”com.alan”})指定扫描那个包(不推荐)

DI详解

因为默认是按类型进行的,如果存在多个相同类型的bean会报错

通常使用一下几种方案来解决

  • @Primary 优先级
  • @Qualifier 指定需要注入对应名字的bean
  • @Resource 指定需要注入对应名字的bean,且不需要autowired注解了

Mysql

关系型数据库(RDBMS)建立在关系模型基础上,由多张互相连接的二维表组成的数据库

Sql

  • 可以单行或多行书写,以分号结尾
  • 可以使用空格/缩进来增强可读性
  • 不区分大小写
  • 注释
    • 单行:–
    • 多行/* */

分类

分类 说明
DDL 数据定义语言,定义数据库对象(数据库,表,字段)
DMl 数据操作语言,对数据库表中的数据CUDA
DQL 数据查询语言,用来查询数据库表中的记录
DCL 数据库控制语言,用来创建数据库用户、控制数据库的访问权限

需求+原型

设计

概要设计、详细设计、接口设计、数据库设计

Java程序

优化->数据库优化

DDL

**数据库 **

查询

1
查询所有数据库:show databases;
1
查询当前数据库: selsect database();

使用

1
使用数据库:use 数据库名;

创建

1
创建数据库:create database [if not exists] 数据库名;

删除

1
删除数据库: drop database [if exists] 数据库名;

[]可选

创建

1
2
3
4
5
create table 表名(
字段1 字段类型 [约束] [comment 字段1注释],
......
字段n 字段类型 [约束] [comment 字段n注释],
)[comment 表注释]
1
2
3
4
5
6
7
create table tb_user(
id int primary key auto_increment comment 'ID,唯一标示',
username varchar(20) not null unique comment '用户名',
name varchar(10) not null comment '姓名',
age int comment '年龄',
gender char(1) default '男' comment '性别'
) comment '用户表';

约束

概念:约束是作用于表中字段上的规则,用于限制存储在表中的数据

目的:保证数据库中数据的正确性,有效性,完整性

约束 描述 关键字
非空约束 限制改字段值不能为null not null
唯一约束 保证字段的所有数据都是唯一,不重复的 unique
主键约束 主键是一行数据的唯一表示,要求非空且唯一,在后面添加auto_increment自动增加 primary key
默认约束 保存数据时,如果未指定该字段值,则采用默认值 default
外键约束 让两张表的数据建立连接,保证数据的一直性和完整性 Foreign key

数据类型:查表

表结构查询

  • 查询当前数据库下所有表:show tables;
  • 查询表结构: desc 表名;
  • 查询建表语句: show create table 表名;

修改表结构

  • 添加字段: alter table 表名 add 字段名 类型(长度) [comment 注释] [约束];
  • 修改字段类型: alter table 表名 modify 字段名 新数据类型(长度);
  • 修改字段名和字段类型: alter table 表名 change 旧字段名 新字段名 类型(长度) [comment 注释] [约束]
  • 删除字段: alter table 表名 drop column 字段名;
  • 修改表名: rename table 表名 to 新表名;
  • 删除表: drop table if exists 表名;

在删除表结构时,数据也会被删除

DML

插入数据

  • 指定字段添加数据: insert into 表名 (字段1,字段2) values (值1,值2);
  • 全部字段添加数据: insert into 表名 values (值1,值2,…);
  • 批量添加数据(指定字段): insert into 表名 (字段名1,字段名2) values (值1,值2),(值1,值2);
  • 批量添加数据(全部字段):insert into 表名 values (值1,值2…),(值1,值2…);

更新时间可以使用now();

前面有多少个字段,有面就需要多少个值

字符串和日期需要用引号阔起来

插入的数据大小应该在字段限制之内

更新数据

  • 修改数据: update 表名 set 字段名1=值1,字段名2=值2…. [where 条件];

没有where会更新整张表

删除

  • 删除数据: delete from 表名 [where 条件];

没有条件会清空整张表的数据

不能删除某一个字段的值,如果要操作使用update设置为null

DLQ

用来查询数据库中的记录Select

1
2
3
4
5
6
7
8
9
10
11
12
13
14
select
字段列表
from
表名列表
where
条件列表
group by
分组字段列表
having
分组后条件列表
order by
排序字段列表
limit
分页参数

基本查询

  • 查询多个字段:select 字段1,字段2 from 表名;
  • 查询所有字段: select * from 表名;
  • 设置别名: select 字段1 [as 别名1] from 表名;
  • 去除重复记录: select distinct 字段列表 from 表名;

条件查询

  • 条件查询: select 字段列表 from 表名 where 条件列表;
比较运算符 功能
> 大于
>= 大于等于
< 小于
<= 小于等于
= 等于
<>或!= 不等于
between … and … 在某个范围之内(含最小,最大值)
in(…) 在in之后的列表中的值,多选一
like 占位符 模糊匹配使用通配符’_’单个字符‘%’任意字符
is null 是null
逻辑运算符 功能
and或&& 并且
or或|| 或者
not或|

select * from tb_tmp where name=’杨逍’;

select * from tb_tmp where id <= 5;

select * from tb_tmp where job is null;

select * from tb_tmp where job is not null;

select * from tb_tmp where password != ‘123456’;

select * from tb_tmp where entry data >= ‘2000-01-01’ and data <=’2010-01-01’;

select * from tb_tmp where between ‘2000-01-01’ and ‘2010-01-01’;

select * from tb_tmp where between ‘2000-01-01’ and ‘2010-01-01’ and gender =2;

select * from tb_tmp where job = 2 or job = 3 or job= 4;

select * from tb_tmp where job in (2,3,4) ;

select * from tb_tmp where name like ‘__’;

select * from tb_tmp where name like ‘张%’;

分组查询

函数 功能
count 统计数量
max 最大值
min 最小值
avg 平均值
sum 求和

语法:select 聚合函数(字段列表) from 表名;

统计企业员工数量

Select count(id) from tb_emp;//不对null记述

Select count(0) from tb_emp;//不对null记述

Select count(*) from tb_emp;//不对null记述

推荐count(*)

最早入职的员工

select min(entrydate) from tb_emp;

最迟入职员工

select max(entry date) from tb_emp;

统计某列的平均值

select avg(xx) from table;

求和

select sum(xx) from table;

分组查询

只能返回: 分组字段或聚合函数

语法:select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组后的条件];

根据性别分组,统计男生于女生的数量

select gender,count(*) from tb_emp group by gender;

先查询入职之间在xxx-xxx 以前的员工,并结果根据职位分组,获取员工数量大于等于2的职位

select job, count(*) from tb_emp where entrydate <= ‘xxx’ group by job having count(*) >=2;

排序查询

select 字段列表 from 表名 [where 条件] [group by 分组字段] order by 字段1 排序方式,字段2 排序方式…;

asc升序

desc降序

分页查询

select 字段列表 from 表名 limit 起始索引,查询记录条数;

从启始索引0开始查询员工数据,每页展示5条记录

select * from tb_emp limit 0,5;

从启始第1页开始查询员工数据,每页展示5条记录

select * from tb_emp limit 0,5;

从启始第2页开始查询员工数据,每页展示5条记录

select * from tb_emp limit 5,5;

从启始第3页开始查询员工数据,每页展示5条记录

select * from tb_emp limit 10,5;

启始索引(页码-1)*每页展示记录数

案例

按需求完成员工管理的条件分页查询,根据输入条件,查询第一页数据,每页展示10条记录

select * from tb_emp where name like ‘张%’ and gender = 1 and entrydate between ‘xxx’ and ‘xxx’ order by update desc limit 0,10 ;

  • if(条件,true取值,false取值)

select if(gender=1,’男性员工’,’女性员工’) 性别 ,count(*) from tb_emp group by gender;

  • case 表达式 when 值1 then结果1 when 值2 then 结果2 else … end.

select (case job when 1 then ‘班主任’ when 2 then ‘讲师’ else ‘未分配职位’ end) 职位,count(*) from tb_emp froup by job;

多表设计

一对多

多的一方关联一的一方

问题分析:两张表,在数据库层面并未建立关联,所以是无法保证数据的一致性和完整性的

外键语法

创建表时指定(所有字段罗列完后在最后添加)

1
2
3
4
5
create table 表名(
字段名 数据类型
...
[constraint] [外键名称] foreign key (外键字段名) references 主表 (字段名称)
);

建完表后,添加外键

1
alter table 表名 add constraint 外键名称 foreign key (外键字段名) references 主表(字段名);

这种外键叫物理外键

概念:使用foreign key 定义外键关联另外一张表

缺点:

  • 影响CRUD
  • 仅用于单节点数据库,不适用分别是,集群场景
  • 容易引发数据库的死锁问题,消耗性能

逻辑外键

概念:在业务层逻辑中,解决外键关联

通过逻辑外键,就可以很方便的解决上述问题

一对一

关系:一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他字段放在另一张表中,以提升操作效率

如果基本信息查询特别高,查询身份信息很低,可以拆分为两张表

实现:在任意一方加入外键,关联另一方的主键,并且设置外键为唯一的(UNIQUE)

*多对多

实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键

多表查询

内连接查询

查询交集部分

表起别名在表后空格直接加名字

隐式内连接

1
select 字段列表 from1,表2 where 条件...;

显式内连接

1
select 字段列表 from1 [inner] join2 on 连接条件...;

外连接

左外连接(完全包含左表数据)

1
select 字段列表 from1 left [outer] join2 on 连接条件...;

右外连接(完全包含右表数据)

1
select 字段列表 from1 right [outer] join2 on 连接条件...;

子查询

SQL语句中嵌套select语句,成为嵌套查询,又称子查询

形式

1
select * from t1 where column1=(select column1 from t2 ...);

子查询外部的语句可以式insert/update/delete/select

分类

  • 标量子查询:子查询返回的结果为单个值

常用的操作符:= 、<>、 >、 >=、 <、 <=

1
select * from tb_emp where dept_id = (select id from tb_dept where name = '教研部');
  • 列子查询:子查询返回的结果为一列

常用操作符:in 、not in

1
select * from tb_emp where dept_id in (select id from tb_dept where name = '教研部' or name = '咨询部');
  • 行子查询返回的结果为一行

常用操作符:=、<>、in 、not in

1
select * from tb_emp where (entrydate,job)=(select entrydate,job from tb_emp where name='韦一笑');
  • 表子查询:子查询返回的结果为多行多列

常用操作符:in

1
select e.*,d.name from (select * from tb_emp where entrydate > '2006-01-01') e, tb_dept d where e.dept_id = d.id;

事务

事务是一组操作的集合,它是一个不可分割的工作单位。事物会吧所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成果,要么同时失效

注意事项:默认mysql的事物是自动提交的,也就是说,当执行一条dml语句,mysql会立即隐式提交事务

事务控制

  • 开启事务:start transaction;/begin;
  • 提交事物: commit;
  • 回滚事物: rollback;

先开启事务,然后执行多条sql如果成功就使用commit,如果失败则使用rollback

四大特性

  • 原子性:最小单元,要么全部成功,要么全部失败
  • 一致性:事物完成时,必须使所有数据都保持一致状态
  • 隔离性:数据库系统提供的隔离机制,保证事物不受外部兵法操作影响的独立环境下运行
  • 持久性:事物一旦提交或回滚,它对数据库中数据的改变就是永久的

索引

index时帮助数据库高效获取数据的数据结构

优点:

  • 提高数据库查询的效率,降低数据库的IO成本
  • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU消耗

缺点

  • 索引会占用存储空间
  • 索引大大提高了查询效率,同时也降低了insert、updata、delete的效率

B+tree(多路平衡搜索树)

语法

  • 创建索引:
1
create [unique] index 索引名 on 表名(字段名);
  • 查看索引
1
show index from 表名;
  • 删除索引
1
drop index 索引名 on 表名;

MyBatis

快速入门

使用Mybatis查询所有用户数据

1、准备工作(创建springboot工程、数据包user、实体类User)

2、引入Mybatis的相关依赖,配置Mybatis(数据库连接信息)

3、编写SQL语句(注解/XML)

四要素

1
2
3
4
5
6
7
8
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=1234
1
2
3
4
5
@Mapper
public interface UserMapper{
@Select("select * from user")
public List<User> list();
}

JDBC

jdbc就是使用java语言操作关系型数据库的一套API

是一套操作关系型数据库的规范,即接口

各个数据库厂商起实现这套接口,体空数据库的java包

我们可以使用这套接口编程,真正执行的代码是驱动jar包中的实现类

数据库连接池

数据库连接池是个容器,复制分配、管理数据库连接(Connection)

它运行应用程序重复使用一个现有的数据库连接,而不是再重新建立一个

释放空闲时间超过最大空闲时间的连接,来避免因为没有释放连接而引起的数据库连接泄漏

标准接口

功能:获取链接:Connection getConnection() throws SQLException;

常见产品

C3P0、DBCP、DRuid(阿里巴巴开源)、Hikari(springboot默认)

切换链接池

  • 引入依赖
1
2
3
4
5
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
  • 配置链接信息
1
2
3
4
5
6
7
8
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=1234

lombok

lombok是一个实用的java类库,能通过注解的形式自动生成构造器,getter/setter、equals、hashcode、toString等方法,并可以自动化生产日志变了,简化java开发,提高效率

注解 作用
@Getter/@Setter 为所有的属性提供get/set方法
@ToString 会给类自动生成一阅读的toString方法
@EqualsAndHashCode 根据类所拥有的非静态字段自动重写equals方法和hashCOde方法
@Data 提供了更综合的生成代码功能(@Getter+@Setter+@ToString+@EqualsAndHashCode)
@NoArgsConstructor 为实体类生成无参的构造器方法
@AllArgsConstructor 为实体类生成处理static修饰的字段之外代有各参数的构造器方法
1
2
3
4
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

lombok会在编译时,自动生成对于的java代码。我们使用lombok时,还需要安装一个lombok的插件(idea自带)

基础操作

删除

1
2
@Delete("delete from emp where id = #{id}")
public void delete(Integer id);

日志输入

可以在application.properties中,打开mybatis的日志,并输出到控制台

1
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

预编译sql的优势

性能更高

更安全(防止sql注入)

#{id}就会生成预编译sql

${id}会直接将参数拼接在后面,存在sql注入问题

新增

1
2
3
@Insert("insert into emp(username,name,gender,image,job,entrydate,dept_id,create_time,update_time"+
" values (#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{dept_id},#{createTime},#{updateTime})")
public void insert(Emp emp);

主键返回

在数据添加成功后,需要获取插入数据库数据的主键

需要在添加的方法上添加注解

1
@Options(keyProperty = "id",useGeneratedKeys=true)

会将主键封装到id属性中,代表我们需要拿到主键值

更新

1
2
@Update("update emp set username=#{username},name=#{name},gender=#{gender},image=#{image},job=#{job},entrydate=#{entrydate},dept_id=#{deptid},update_time=#{updateTime where id=#{id}}")
public void update(Emp emp);

查询

1
2
@Select("select * from emp where id=#{id}")
public Emp getById(Integer id);

数据封装

实体类书姓名和数据库表查询返回到字段名一致,mybatis会自动封装

如果实体类书姓名和数据库表查询返回的字段名不一致,不能自动封装

  • 方案一:给字段起别名,让别名于实体类属性一致
1
2
@Select("select xxx aaa,xxxx aaaa from emp where id=#{id}")
public Emp getById(Integer id);
  • 方案二,通过@Results,@Result注解手动映射封装
1
2
3
4
5
6
7
8
@Results({
@Result(column = "dept_id",property = "deptId"),
@Result(column = "create_time",property = "createTime"),
@Result(column = "update_time",property = "updateTime"),
})
@Select("select * from emp where id=#{id}")
public Emp getById(Integer id);

  • 方案三:开启mybatis的驼峰命名自动映射开关 a_column —>aColumn
1
mybatis.configuration.map-underscore-to-camel-case=true

条件查询

1
2
@Select("select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and entrydate between #{begin} and #{end} order by update_time desc")
public List<Emp> list(String name,Short gender,LocalDate begin,LocalDate end);

XML映射文件

规范

  • XML映射文件的名称与Mapper接口名称一致,并且将XML映射文件和Mapper接口放置在相同包下(同包同名)
  • XML映射文件的namespace属性为Mapper接口全限定名一致
  • XML映射文件中sql语句的id与Mapper接口中的方法名一致,并保持返回类型一致

在resources下创建包级目录用‘/’

1
2
3
4
5
6
7
8
9
10
<?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">
<mappser namespace="com.alan.mapper.EmpMapper">
<select id="list(方法名)" resultType="com.alan.pojo.Emp(返回类型)">
select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and entrydate between #{begin} and #{end} order by update_time desc

</select>
</mappser>

动态SQL

  • :条件都不成立去除where,而且会自动去除开头多余的and和or
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<where>
<if test="name != null">
name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
<if test="name != null">
name like concat('%',#{name},'%')
</if>
</where>

案例

动态更新

alt+end,mybtisx插件自动生成sql

  • ,会自动去除后面多余的逗号

forech

  • 遍历元素
1
2
3
4
delete from emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>

Collection:遍历的集合

Item:遍历出来的元素

separator:分隔符

open:遍历开始前拼接的SQl片段

close:遍历结束后凭借的SQL片段

sql,include

  • 定义可重复使用的SQL片段
  • 通过属性refid,指定包含的SQL片段
1
2
3
4
5
<sql id="xx">
这里方重复使用的sql片段
</sql>

<include refid="xx"/>引用sql

开发规范-Restful

  • REST 表述性状态转换,他是一种软件架构风格
1
2
3
4
5
http://localhost:8080/users/1 GET:查询id为1到用户
http://localhost:8080/users POST:新增用户
http://localhost:8080/users PUT:修改用户
http://localhost:8080/users/1 DELTETE:删除id为1到用户

  • url定位资源
  • HTTP动词描述操作

REST是风格,是约定方式,约定不是规定,可以打破

描述模块的功能通常使用复数,也就是加s的格式来描述,表示此类资源,而非耽搁资源如:users、emps、books…

统一响应结果

code:响应码

msg:响应信息

data:返回数据

开发流程

  1. 查看页面原型明确需求
  2. 阅读接口文档
  3. 思路分析
  4. 接口开发
  5. 接口测试
  6. 前后端链调
  7. 开发流程

明确需求 接口文档 思路分析 接口开发

  1. 接口调试

postman测试 前后端联调

  1. 日志小技巧

@Slf4j

1
log.info()

pageHelper插件

1
2
3
// 员工信息查询
@Select("select * from emp")
public List<Emp> list();
1
2
3
4
5
6
7
8
9
10
11
@Override
public PageBean page(Integer page, Integer pageSize){
// 设置分页参数
PageHelper.startPage(page,pageSize);
// 执行查询
List<Emp> empList = empMapper.list();
Page<Emp> p = (Page<Emp>) empList;
// 封装PageBean对象
PageBean pageBean = new PageBean(p.getTotal(),p.getResult());
return pageBean;
}
1
2
3
4
5
6
<!--PageHelper分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>

springboot文件上传,默认单个文件最大允许为1M,如果需要上传大文杰,可以进行如下配置

1
2
3
4
配置耽搁文件最大上传大小
spring.servlet.multipart.max-file-size=10MB
配置单个请求最大上传大小(一次请求可以上传多个文件)
spring.servlet.multipart.max-request-size=100MB
函数名 功能
String getOriginalFilename(); 获取原始文件名
void transferTo(File dest); 将接收的文件转存到磁盘文件中
Long getSize() 获取文件的大小,单位,字节
Byte[] getBytes(); 获取文件内容的字节数组
InputStream getInputStream(); 接收到的文件内容的输入流

yml

  • 大小写敏感
  • 数值前边必须有空格作为分隔符
  • 使用缩进表示层级关系,缩进时,不允许使用Tab键,只能使用空格
  • 缩进的空格数目不重要,只要相同层级的元素左侧对其即可
  • #表示注释,从这个自负一致到行尾,都会被解析器忽略
  • 对象/Map集合
1
2
3
4
user:
name:zhangsan
age: 18
password: 123456
  • 数组/LIst/Set集合
1
2
3
4
hobby:
- java
- game
- sport

配置文件遍历注入

@Value(“${key}”)

1
2
3
@Data //提供get set方法
@Component //成为bean
@ConfigurationProperties(prefix="前缀")//提供变量名的前缀

解决报错

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

数据校验

会话

  • 会话:用户打开浏览器,访问web服务器的资源,会话建立,知道一方断开连接,会话结束,在一次会话中可以包括多次请求和响应

  • 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话多次请求间共享数据

  • 会话跟踪方案:

    • 客户端会话跟踪技术:Cookie
    • 服务端会话跟踪技术:Session
    • 令牌技术

Cookie

优点:HTTP协议中支持的技术

缺点:

  • 移动端APP无法使用Cookie
  • 不安全,用户可以自己禁用Cookie
  • Cookie不能跨域
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//设置Cookie
@GetMapping("/c1")
public Result cookie1(HttpServletResponse response){
response.addCookie(new Cookie("login_username","itheima")); //设置Cookie/响应Cookie
return Result.success();
}

//获取Cookie
@GetMapping("/c2")
public Result cookie2(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if(cookie.getName().equals("login_username")){
System.out.println("login_username: "+cookie.getValue()); //输出name为login_username的cookie
}
}
return Result.success();
}

跨域

跨域区分三个维度:协议、ip/域名、端口号

任何一个不一样,都会跨域

Session

优点:存储在服务端,安全

缺点:

  • 服务器集群环境下无法直接使用Session
  • Cookie的缺点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@GetMapping("/s1")
public Result session1(HttpSession session){
log.info("HttpSession-s1: {}", session.hashCode());

session.setAttribute("loginUser", "tom"); //往session中存储数据
return Result.success();
}

@GetMapping("/s2")
public Result session2(HttpServletRequest request){
HttpSession session = request.getSession();
log.info("HttpSession-s2: {}", session.hashCode());

Object loginUser = session.getAttribute("loginUser"); //从session中获取数据
log.info("loginUser: {}", loginUser);
return Result.success(loginUser);
}

令牌技术

优势

  • 支持PC端,移动端
  • 解决集群环境下的认证问题
  • 减轻服务器存储压力

缺点:需要自己实现

JWT (Json Web Token)

官网:https://jwt.io/

定义了一种简洁的,自包含的格式,用于通信双方一json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的

组成

  • 第一部分:Header(头),记录令牌类型、签名算法等
  • 第二部分:Payload(有效载荷),携带一些自定义信息、默认信息等
  • 第三部分:Signature(签名),防止Token被篡改、确保安全性、将header、payload、并加入指定密钥,通过指定签名算法计算而来

Base64:是一种基于64个可打印字符(A-Z,a-z,0-9+/)来表示二进制数据等编码方式,=补位符号

引入JWT

1
2
3
4
5
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>

JWT校验时使用的签名密钥,必须和生成JWT令牌时使用的密钥时配套的

如果JWT令牌解析校验时报错说明被篡改或过期

过滤器Filter

概念:Filter过滤器时javaWeb三大组件(Servlet、Filter、Listenser)之一

过滤器可以把对资源的请求拦截下来,从二实现一些特殊的功能

过滤器一般完成一些通用的操作,比如登陆校验,统一编码处理,敏感字符处理

快速入门

  1. 定义Filter:定义一个类,实现Filter接口,并重写其所有方法
  2. 配置Filter:Filter类上加@WebFilter(urlPatterns = “/*”)注解,配置拦截资源路径。引导类上加@ServletComponentScan开启Servlet组件支持
1
2
3
4
5
6
@WebFilter(urlPatterns = "/*")
public class DemoFilter implements Filter {

@ServletComponentScan
@SpringBootApplication
public class SpringbootItemQuickstartApplication {

放行后访问对呀资源,资源访问完成后,还会回到Filter中嘛? 会

如果回到Filter中,时重新执行还是执行放行后到逻辑呢?执行放行后逻辑

拦截路径 urlPatterns值
拦截具体 /login
拦截目录 /emps/*
拦截所有 /*

一个web应用中,可以配置多个过滤器,这个过滤器就形成了一个过滤器链

顺序:注解配置的Filter,优先级时按照过滤器类名(字符串)的自然排序

快速捕获异常command+option+t

拦截器interceptor

快速入门

概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制方法的执行

作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码

定义拦截器 实现HandlerIterceptor接口,并重写所有方法

注册拦截器

1
2
3
4
5
6
7
8
9
@Configuration
public class WebConfig implements WebMvcConfigurer{
@Autowired
private LoginCheckInterceptor loginCheckInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addIntercepttor(LoginCheckInterceptor).addPathPatterns("/**");
}
}

addPathPatterns**(“/**”)需要拦截

excludePathPatterns**(“/login”)不需要拦截

拦截路径 含义
/* 一级路径
/** 任意级路径
/depts/* /depts下的一级路径
/depts/** /depts下的任意级路径

Fillter于Interceptor

接口规范不同:过滤器需要实现Filter接口,二拦截器需要实现HandlerInterceptor接口

拦截范围不同:过滤器Filter会拦截所有的资源,而Intercetor只会拦截Spring环境中的资源

异常处理

方法一:在controller的方法中进行try…catch

方案二:全局异常处理器 @TestControllerAdvice

1
2
3
4
5
6
7
8
@RestControllerAdvice
public class GlobalExceptionHandler{
@ExceptionHandler(Exception.class)
public Result ex(Exception ex){
ex.printStackTrace();
return Result.error("对不起,操作失败,请联系管理员")
}
}

事物处理&AOP

事物是一组操作的集合,它是一个不可分割的工作单位,这些操作,要么同时成功,要么同时失败

操作:

  1. 开启事务(一组操作开始前,开始事物):start transaction/begin;
  2. 提交事物(这组操作全部成功后,提交事物):commit;
  3. 回滚事物(中间任何一个操作出现异常,回滚事物):rollback;

@Transactional

位置:业务层的方法上、类上、接口上

作用:将当前方法交给spring进行事物管理,方法执行前,开启事务;成功执行完毕,提交事物,出现异常,回滚事物

1
2
3
4
#spring事务管理日志
logging:
level:
org.springframework.jdbc.support.JdbcTransactionManager: debug

事物进阶

rollbackFor

默认情况下,只有出现RuntimeExcception才回滚异常,rollbackFor属性用于控制出现何种异常类型,回滚事物

propagation

事物传播行为:指的就是当一个事物方法被另一个事物方法调用时,这个事物方法一个如何进行事物控制

1
2
3
4
5
6
7
8
9
@Transactional
public void a(){
UserService.b();
}

@Transactional(propagation = Propagation.REQUIRED)
public void b(){

}
属性值 含义
REQUIRED 默认值,需要事物,有则加入,无则创建新事物
REQUIRES_NEW 需要新事物,无论有无,总是创建新事物
SUPPORTS 支持事物有则加入,无则在无事物状态中运行
NOT_SUPPORTED 不支持事物,在无事物状态下运行,如果当前存在已有事物,则挂起当前事物
MANDATORY 必须有事物,否则抛异常
NEVER 必须每事物,否则抛异常

AOP

快速入门:面向切面编程,其实就是面向特定方法编程

动态代理是面向切面编程最主流的实现,二springAOP是Spring框架的高级技术,旨在管理bean对象的过程中,主要通过代理机制,对特定的方法进行编程

导入aop依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Slf4j
@Component
@Aspect //声明AOP
public class TimeAspect {
@Around("execution(* com.itheima.*.*(..))")//指定方法
public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
long begin = System.currentTimeMillis();
Object result = joinPoint.proceed();//执行方法
long end = System.currentTimeMillis();
log.info(joinPoint.getSignature()+"方法执行耗时:{}ms",end-begin);
return result;
}

}

AOP核心概念

连接点:JoinPoint,可以被AOP控制的方法(暗含执行时的相关信息)

通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)

切入点:PointCut,匹配连接点的条件,通知仅在切入点方法执行时被应用

切面:Aspect,描述通知于切入点点对应关系(通知+切入点)

目标对象:Target,通知所有应用对象

通知类型

@Around环绕通知,次注解标注的通知方法在目标方法前,后都被执行

@Before前置通知,此注解的通知方法在目标方法前执行

@After 后置通知,此注解标注的通知方法后被执行,无论是否异常都会被执行

@AfterReturing 返回通知,次注解标注的通知方法在目标方法后被执行,有异常不会被执行

@AfterThrowing:异常后通知,此注解的通知方法发生异常后执行

注意事项

  • @Around环绕通知需要自己调用ProceedingJoinPoint.proceed()来让原始方法执行,其他通知不需要考虑目标执行方法
  • @Around环绕通知方法的返回值,必须指定为Object,来接收原始方法的返回值

切入点表达式

@PointCut该注解的作用时将公共的切点表达式抽取出来,需要用到时引用该切点表达式即可

1
2
3
4
5
6
7
8
9
10
11
@Pointcut("excution(* com.itheima.service.impl.DeptServiceImpl.*(..))")

Private void pt()



@Before("pt()")

public void before(){

}

通知的执行顺序

当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行

和类名字母排序有关

1.不同切面类中,默认按照切面类的类名字母排序;

目标方法前的通知方法:字母排名靠前的先执行

目标方法后的通知方法:字母排名靠前的先执行

2.@Order(num)设置执行顺序

目标方法前的通知:数字小的先执行

目标方法后的通知:数字小的后执行

切入点表达式

切入点表达式:描述切入点方法的一种表达式

作用:主要用来决定项目中哪些方法需要加入通知

常见形式:

  • execution(…)根据方法的签名来匹配
1
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)

其中带?的表示可以省略的部分

访问修饰符:可省略

包名.类名:可省略(不建议省略)

Throws 异常:可省略

  • 可以使哟童年通配符

*:单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分

1
execution(* com.*.service.*.update*(*))

..:多个连续的任意符号,可以通配任意层级的包、或任意类型、任意个数的参数

1
execution(* com.itheima..DeptService.*(..))

|| 或

书写建议:

所有业务方法名在命名时尽量规范,方便切入点表达式快速匹配

描述切入点方法通常给予接口描述,而不时直接描述实现类,增强拓展性

在满足业务需要的前提下,尽量缩小切入点的匹配范围。

  • @annotation(…)根据注解匹配

第一步新建一个自定义注解Mylog

加两个原注解描述

@Retention(RetentionPoliycy.RUNTIME)//运行时有效

@Target(ElementType.METHOD)//作用目标(方法)

第二部在业务方法上加@Mylog注解

然后在切入点表达式

@Pointcut(“@annotation(com.itheima.aop.Mylog)”)去引入这个注解

连接点

在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息、方法名、方法参数等

对于@Around通知,获取连接点信息只能使用ProceedingJoinPoint

对于其他四种通知,获取连接点信息只能使用JoinPoint,它是ProceedingJoinPoint的父类型

获取目标对象的类名 obj.getTarget().getClass().getName()

获取目标方法的方法名obj.joinPoint.getSignature().getName();

获取目标方法运行时传入的参数obj.getArgs()

放行目标方法执行obj.proceed()

获取目标方法运行的返回值obj就是上面proceed的返回值

配置优先级

Maven分模块开发

分模块开发需要先正对模块功能进行设计,再进行编码,不会先将工程开发完毕,然后进行拆分

创建maven模块tlias-pojo,存放实体类

创建maven模块tlias-utils,存放工具类

1.创建一个maven模块,最外面的包名保持一致,吧pojo全部类复制到爆虾

2.引入xml

继承

子标签使用这个标签说明那个是父工程

  1. 创建maven模块tlias-parent,该工程为父工程,设置打包方式为pom:pom

一个工程只能继承一个父工程,单支持多重继承

jar:普通模块打包,springboot项目基本都是jar包内嵌tomaacat运行

war:普通web程序打包,需要部署在外部的tomacat服务器中运行

pom:父工程或聚合工程,该模块不屑代码,进行依赖管理

在子工程中,配置了继承关系之后,坐标中groupId是可以省略的,业务会自动继承父工程的

relativePath指定父工程的pom文件的相对位置(如果不指定,将从本地仓库/远程仓库查找该工程)

若父子工程都配置了同一个依赖的不同版本,以子工程的为标准

版本锁定

来统一管理依赖版本

在父工程使用此标签,在子工程引入时就可以不用指定版本

中通过自定义集中维护版本号

聚合

将多个模块组装成一个整体,同时进行项目构建

聚合工程:一个不具有任何业务功能的“空”工程(有切仅于一个pom文件)

作用:快速构建项目

maven中可以通过设置当前聚合工程所包含的子模块名称

在父工程中