一、数据库
1.1数据库概念
管理数据的仓库就是数据库。Datebase;db;
1.2数据库管理系统
方便我们管理数据库的系统(Database Management) DBMS。它里面可以由n个数据库,每个数据库中可以由n张表
1.3数据库的分类
关系型数据库
根据存储方式进行分类,存储方式有自己的结构,主要以表和二维表组成
常用的: mysql、oracle、sqlserver、DB2、postgreSQL
优点:
- 结构稳定、操作简单、容易上手
- 可以进行事物的处理;
- 可以直接使用通用的SQL语句对数据进行操作
缺点:
- 修改结构上相对来说比较复杂;
- 数据量过大的时候,查询相对较慢
非关系型数据库
存储方式:以特殊文档结构进行存储
redis:缓存 以键值对的方式进行存储;
mongodb:以json格式进行存储;
优点:
- 数据量过大的时候,查询效率高
缺点:
- 没有事物的处理
- 通用SQL不可行
- 学习成本相对较高
1.4 SQL
structure sqery language (结构化查询语言)简写sql,用来查询、维护数据的一种规范.
2 数据库服务
数据库只安装了服务器:mysql server ;
直观:打开服务界面,以管理员身份运行,mysql80进行启动操作
命令:
net stop mysql80
MySQL80 服务正在停止.
MySQL80 服务已成功停止。
C:\Windows\system32>net start mysql80
MySQL80 服务正在启动 .
MySQL80 服务已经启动成功。
mysql -uroot -hlocalhost -p 登录数据库:
输入密码:
3. sql
3.1通用语法
-- sql不分大小写,比如说列是data的时候,可能会有冲突,需要大写;
-- 写sql的时候一定要去加空格:适当多加以便规范化,格式化
-- 通用的sql注释:
注释单行: --
注释多行: /* 1111111 */
mysql注释单行: #+空格
3.2 DDL语句
数据库定义语言:用来描述数据存储的实体的语言。通俗来讲就是操作库和表结构的语言
# 新建数据库
-- 语法: create database 数据库名称;
create database school;
-- 使用数据库: use 数据库名称;
use school;
-- 创建表;
-- 语法: create table 表名称(列名 数据类型,列名 数据类型,列名 数据类型);
create table student(id int,stu_name varchar(10),age int);
-- 数据库查询当前数据库中有多少表
show tables from school;
-- 查询表的表结构
desc student;
-- 对列的操作:
-- 新增;
-- alter table 表名称 add 列名 数据类型;
alter table student add sex varchar(10);
-- 修改;
-- 修改列数据类型
-- alter table 表名 modify 列 新的数据类型;
alter table student modify sex int ;
-- 修改列名字
-- alter table 表名 change 原列名 新列名 新的数据类型;
alter table student change sex phone varchar(11);
-- 删除;
-- alter table 表名 drop 列名;
alter table student drop phone;
-- 复制表
-- create table 新表明 like 旧表;
create table stu1 like student;
-- 删除表
-- drop table 表名;
drop table stu1;
-- 删除数据库
drop database school;
3.3 DML语句
管理数据的语句:和主要用来是对表中数据进行增删改的语句
-- dml语句:
-- 增加(insert into)
-- 语法:
-- 新增一条:
-- insert into 表名(列名,列名n)values(属性值,属性值);
-- insert into 表名 values(属性值,属性值);
insert into student(id,stu_name,age)values(1,'张三',10);
insert into student values(2,'李四',20);
-- 新增多条:
-- insert into 表名 values(属性值,属性值),(属性值,属性值)
insert into student values(3,'王五',12),(4,'老六',15),('5','老八',22);
-- 修改(update set)
-- update 表名 set 列名 =修改后的值 ,列名2=修改后的值 条件(where 拼接)
-- 修改id=8的stu_name 修改为张三三
update student set stu_name='张三三' where id=8;
update student set stu_name='张大炮' ,age =35 where id =8;
-- 删除(delete)
-- 删除通常也是跟条件的:
-- delete from 表名 where 条件;
delete from student where id=8;
/*
truncate table 表名 效果和delete from 表效果相同
区别:
1.前者删除数据的时候,先将表drop删除掉,再创建表结构;
后者删除数据的时候,是一行一行数据进行删除
2.如果删除数据的量很大的时候推荐使用前者
3.前者的范畴属于DDL,没有事物的处理;
后者删除具备事务处理;
4.前者后面不能跟条件
后者可以
*/
3.4 常用的数据类型
数值、字符串相关
数据类型 | 简单说明 | 长度 |
---|---|---|
int | 整形 | 4字节(0~65535) |
bigint | 较大的整形 | 8字节 |
float | 单精度浮点数 | 4字节 |
double | 双精度浮点数 | 8字节 |
decimal(a,b) | 和双精度浮点数比较像 | a字节 |
char(n) | 用来表示字符串 | n字节 |
varchar(m) | 用来表示字符串 | n字节 |
char(10)
varchar(10) utf 编码的时候一个汉字占三个字节
大文本
数据类型 | 简单说明 |
---|---|
text | 大文本 |
blob | 大文本(二进制的 可以存放图片、文档等) |
日期
日期类型 | 格式 | 取值范围 |
---|---|---|
date | yyyy-mm-dd | 1000~9999 |
datetime | yyyy-MM-dd HH:mm:ss | 1000~9999 |
timestamp | yyyy-MM-dd HH:mm:ss | 1970~2038-12-31 精度为1S; |
3.5 DQL
data query language(数据查询语句)
-- 最基本的写法: select * from 表模式名.表名
select * from 表; -- select 后面跟的是想看到的字段,如果是所有字段可以写成*
-- 查询全表:
# select * from 表;
# select 列1,列2,列n from 表;
1.对列去重查询
select distinct 列名 from 表名;
2.起别名查询
-- 取别名查询方式
-- 可以对列取别名,多表查询的时候通常对表取别名,再去调用列
-- select stu_name (as) 别名 from student;
3.条件查询
-- 条件查询:
select
列名
from 表名
where 条件
group by -- 分组
order by -- 排序
limit -- 分页
/*
where 列名 条件:
1.运算符: = < > <= >= !=(不等于) <>(不等于)
2.逻辑运算符: and(且) or(或者)
select * from student where stu_name='张三' or id=2;
*/
select * from student where stu_name='张三' or id=2;
select * from student where id between 1 and 5;
4.区间进行取值
x属于[a,b] x>=a x<=b;
between a and b
select * from student where id between 1 and 5;
5.查询某个集合
-- 查询某个集中: in | not in
select * from student where id in (1,2,3);
6.模糊查询
-- 模糊查询:
-- select * from 表 where 列 like '%内容%'
select * from student where stu_name like '%张%';
-- 如果只有一个百分号,且百分号在最前面,以内容结尾的进行模糊查询
-- 如果只有一个百分号,且百分号在最后面,以内容开头的进行模糊查询
-- 查询非空数据 或者查询空数据
-- where 列名 is not null | is null;
select * from student where stu_name is null;
3.5.1排序查询
升序
默认情况下升序排序
order by 列名 或者 order by 列名 asc
-- 根据id进行排序:
select * from student where id is not null order by id asc;
倒序
order by 列名 desc;
-- 根据id进行排序:
select * from student where id is not null order by id desc ;
多个字段排序
-- 根据id升序、年龄降序进行排列展示:
select * from student where id is not null order by id asc ,age desc ;
order by 先按a排序,b 排序
3.5.2 常用聚合函数
max(列名)
min(列名)
sum(列名)
avg(列名)
**count(x)** --显示表中多少条数据
-- x为列名:也可以查询表中多少数据,列中必须具备条件不为null
-- x为* 查询表中的数据
-- x为1 比*要快一点,所以通常见到的为1
select count(*) from student;
select count(1) from student;
select count(id) from student;
3.5.3 分组查询
where 条件 --可不写;
group by
select age, stu_name,count(*) from student
#where count(*)>1
group by age
having count (*)>1;
实例:
select name,count(*) from book group by price;
-- where 和having的区别:
1.执行顺序上:所有sql先执行where条件再分组;
having是先分组再进行having条件判断
2.having后面一般都是跟聚合函数,而where后面不可以跟聚合函数查询:
3.5.4 分页查询
-- mysql分页最简单;
select 列 from 表 limit a,b;
-- a代表的是最开始的索引; a=(当前页-1)*b;
-- b代表的是每页显示的行数;
实例:
select name from book limit 4,2;
3.6 约束
constraint : 约束用来保证数据的真实性、有效性、合法性。
3.6.1 非空约束
-- 作用:保证当前列数据不能为空;
-- 建表的时候:
create table 表名(列名 数据类型 not null ,列名 数据类型)
-- 建表后:
alter table 表名 modify 列名 数据类型 not null;
-- 删除非空约束:
alter table 表名 modify 列名 数据类型
3.6.2 唯一约束
-- 唯一约束:起到的作用 保证数据是唯一的
-- 创建表的时候添加:
create table 表名(列 数据类型 unique)
-- 建表后的操作:
alter table 表名 modify 列 数据类型 unique;
-- 建表后删除唯一约束:
1. drop index 列名 on 表名
2. alter table 表名 drop index 列名
3.6.3 主键约束
特点:当前列非空且唯一
特点:非空且唯一
-- 创表的时候创建主键
create table 表名(列名 数据类型 primary key,列名 数据类型 primary key)
-- 建表后添加主键:
1.alter table 表名 modify 列名 数据类型 primary key;
2.alter table 表名 add constraint 主键名字 primary key (列名)
3.alter table 表名 add primary key (列名)
-- 删除主键
alter table 表名 drop primary key;
-- ----------------------------------------------------------------------------------------------------
-- 建表后添加主键
-- 如果是int型主键,通常会给他加一个约束(辅助主键):自增约束:
-- 创建表的时候添加
create table 表名(列名 数据类型 primary key auto_increment)
-- 建表后添加自增约束
alter table 表名 modify 列名 数据类型 auto_increment;
-- 删除自增约束:
alter table 表名 modify 列名 数据类型;
3.6.4 外键约束
外键:是为了让多张表发生关系,且保证数据的准确性和有效性
外键要添加在多的一方(从表),通常指向一的一方主键
建表后创建外键:
alter table 从表名字
add constraint 外键名字
foreign key (从表中的外键列)
references 主表名字(主表中的主键列或者说唯一的列)
实例:
alter table student
add constraint classroom_student
foreign key(classroom_id)
references classroom(id);
-- 建表的时候创建外键约束
create table 表名(
所有列,
constraint 外键名字
foreign key (从表中的外键列)
references 主表(主表中的主键列或者唯一的列)
)
外键的特点:
- 外键列的值可以为null;
- 如果从表要修改数据或者新增数据,需要遵守外键,也就是外键列中的值必须是合法值(相当于必须是主表中主键列的值或者null);
- 如果主表和从表要删除数据,首先删除从表数据再删除主表数据
- 如果要先建表,应该先建立主表在建立从表
-- 删除外键:
alter table 从表 drop foreign key 外键名字;
3.7 DCL语句(了解)
数据库控制语句
4.数据库
4.1 多表之间的关系
4.1.1 一对一
4.1.2 一对多
4.1.3 多对多
4.2数据库设计范式
4.2.1概念
设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
4.2.2分类
关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)
4.2.3 三大范式
第一范式
关系型数据库中必须满足第一范式。第一范式:每一列都是原子不可分割的–没有重复的列
补充函数依赖: a b
b依赖与a:如果通过a属性或者a属性的值,可以确定唯一b属性的值,那么我们就说b依赖与a
完全函数依赖:
如果a是一个属性组,b属性的值确定需要依赖于a中所有属性的值,那么我们就说b完全函数依赖于a
(学号,课程)—》成绩
部分函数依赖:
如果a是一个属性组,b属性的确定只需要依赖于a属性组中某个或者某些属性的值,那么我们就说b部分函数依赖于a
第二范式
在1NF的基础上,消除非主键对主键的部分函数依赖
第三范式
在2nf的基础上,消除传递依赖
传递依赖
a—》b----》C
学号----》姓名------》系主任
通过a属性或者属性组的值,可以确定唯一b属性的值,再通过b属性的值可以确定唯一C属性的值我们就说C传递依赖于a。
5.还原于备份
备份:将源文件复制一份—》本地
还原:将本地的sql文件还原到新的数据库里面
5.1 备份
cmd窗口命令方式:
-
到安装路径下的bin目录下输入cmd;
-
输入
mysqldump -uroot -h127.0.0.1 -p 需要备份的数据库名字>路径\文件名字.sql
5.5 还原
再还原前需要新建一个空数据库,以备还原
bin目录下命令:
mysql -uroot -p 新数据库名字《路径\.sql文件
6多表查询
6.1 内连接
取到的是两个表的交集部分。
6.1.1 隐式内连接
-- 语法
select 想看到的字段 from 表a,表b where a.主键=b.外键
6.1.2 显示内连接
-- 语法
select 想看到的字段
from 表a
(inner) join -- inner是可以省略的
表 b
on a.主键=b.外键 --如果继续拼别的表:需要继续写join 表 on 条件
实例
-- 显示内连接 查询员工的姓名和员工的部门名称、职位名称
-- 分析: 想看到 emp t1,dept t2 ,job t3
-- t1.ename,t2.dname,t3.jname
-- 关系:t1.dept_id=t2.id t1.job_id=t3.id
select t1.ename, t2.dname,t3.jname from emp t1
join dept t2 on t1.dept_id = t2.id
join job t3 on t1.job_id = t3.id
6.2 外链接
相对于内连接查到的交集来说,外连接查到的不仅仅是交集
6.2.1 左外连接
-- 语法:
select 想看到的字段 from 表a
left join 表b on 条件(a.主键=b.外键)
-- 左外连接看到的是左表的左右和其他表的交集。
实例:
select t1.ename, t2.dname,t3.jname from emp t1
left join dept t2 on t1.dept_id = t2.id
left join job t3 on t1.job_id = t3.id;
6.22 右外链接
语法:
select 想看到的字段
from 表a
right join 表b on 条件
-- 右外连接看到的是右表的所有和其他表的交集
实例:
select t1.ename, t2.dname,t3.jname from emp t1
right join dept t2 on t1.dept_id = t2.id
right join job t3 on t1.job_id = t3.id
6.3 字查询
将简单的dql查询出来的结果作为条件、或者结果集为进行的嵌套查询,我们称之为子查询。
-- 一、查询工资高于平均值的员工信息
/*
分析:
想看到的字段: emp (t1) t1.*
条件: t1.工资(salary)>平均值
平均值来自:
select avg(salary) from emp t1
*/
第一种:如果查询显示的字段是同一张表,条件是聚合函数,条件与显示也是同一张表,那么通常我们使用运算符进行条件判断。
第二种:如果查询显示的字段是一张表,条件与另外一张表关联,且条件获取到的是集合,我们常用in进行操作。
-- 查询学工部和教研部的员工个人信息:
/*
分析:想看到的字段: emp t1 t1.*;
用到的表: emp t1 dept t2;
条件:
获取到学工部和教研部的id:
select t2.*
from dept t2 where t2.dname='学工部'
or t2.dname='教研部'
emp表中的dept_id in (所获取到的)
*/
select t1.*
from emp t1
where t1.dept_id
in (select t2.id
from dept t2 where t2.dname='学工部'
or t2.dname='教研部')
-- 左外连接:
select t1.* from emp t1
left join dept t2 on t1.dept_id = t2.id
where t1.dept_id
in (select t2.id
from dept t2 where t2.dname='学工部'
or t2.dname='教研部')
-- 查询学工部和教研部的员工个人信息和部门名称:
7 常用的函数
7.1数值
-- 取绝对值:
select abs(数);
-- 取随机数:(0~1)
select rand();
-- 四舍五入
select round(100*rand());
-- 向上取整
select ceil(-5.2);
-- 向下取整
select floor(1.9);
7.2 字符串
-- 字符串:
-- 字符串拼接:
-- 字符串中拼接的如果有null,则返回null;
-- 如果内容直接是数字,会将数字变成字符串返回
select concat('1','a');
select '1' || 'a'; -- oracle数据库中字符串拼接
-- 如果mysql也想使用|| 拼接 需要设置:
set sql_mode =PIPES_AS_CONCAT;
-- 截取字符串
-- (字符串,从第几位开始截取,截取几位)
select substr('你好abc' ,3,4);
-- 替换字符串:
-- (原字符串,原字符串中要替换的字符串,将第二个参数替换成哪些字符串)
select replace('你好abc','abc','sb');
-- 去掉首位空格
select trim(' aaa b ');
-- 字符串的反转:
select reverse('abc');
7.3 日期函数
-- 当前时间
select sysdate();
-- 查看当前年
select year(sysdate());
-- 日期格式化
-- mysql中
select date_format(sysdate(),'%Y-%m-%d'); -- 2023-03-03
select date_format(sysdate(),'%Y-%m-%d %H-%i-%s'); -- 2023-03-03 11-02-14
-- oracle 数据库
-- select to_char(日期,'yyyy-MM-dd HH:mm:ss')
7.4流程控制函数
-- 流程控制语句
-- 如果条件成立,返回表达式1,否则返回表达式2
-- select if(条件,表达式1,表达式2);
-- 如果参数1是空,则返回参数2,否则返回参数1
-- select ifnull(参数1,参数2);
-- 查询dept表中有多少条数据
select count(stu_name)from student;
select count(ifnull (stu_name,1)) from student;
8 事务
8.1 事物的概念
如果一个包含了多个步骤的业务操作,同时被事务所管理,要不同时成功,要么同时失败
start transaction -- 事物
commit; -- 事物提交
rollback; -- 事物回滚
8.2* 特性
- 原子性(a):事物本身是不可分割的最小单位,要么同时成功、要么同时失败
- 一致性(c):事物的操作,数据总量是不变的;
- 隔离性(i):多个事务之间是相互独立的;
- 持久性(d):如果事物提交那么数据将会持久或者说永久发生改变
8.3 *事物的隔离级别
隔离界别:多个事务之间是相互独立的,但是如果多个事物来操作同一批数据,就会出现一些问题。设置不同的隔离级别可以解决相对应的问题
出现问题
1.脏读:一个事物读取了另外一个事物未提交的数据;
2.不可重复读:两个事物,第一个事物进行修改并提交,第二个事物中两次读取到的数据不一样
3.幻读:一个事物操作表中所有数据,另一个事物进行添加或者删除一行或者多行数据,再第一个事物中看不到自己的操作s
隔离级别 | 会出现的问题 |
---|---|
读未提交 | 脏读、不可重复读、幻读 |
读以提交 | 不可重复度、幻读 |
可重复读 | 幻读 |
可串行化 | 可以解决所有问题 |
事物的隔离级别越高越安全,但是效率越低
9 视图
9.1 概念
视图是一张虚拟表,视图的内容来自查询语句或者说穿结果集
9.2 语法
-- 创建视图的语法:
/*
create view 试图语法 as(查询语句);
*/
-- 需求:创建一个视图,视图中存放emp表中的id和name 还有tx表中的id和name
/*
union all 和 union相同点和不同点:
相同:都可以实现联合查询,拼接数据的时候
不关心当前页的数据类型是否一致都可拼接在一起
不同:union all 不去重
union 去重
*/
create view v1 as(
select t1.id,t1.ename from emp t1
union
select t2.id ,t2.name from tx t2
);
-- 查看视图
select * from vv;
-- 视图的删除
drop view vv;
9.3视图和表的区别
- 视图没有真实的物理记录(结构),但是表有;
- 表占用物理空间,视图只是一种逻辑概念的存在,因此视图不能进行修改、也不能进行修改、新增、删除等操作,如果视图中数据存在问题,需要修改,那么应该修改建立视图所需要的表中数据‘
- 视图的建立和删除只会影响到视图本身并不会影响到表
- 视图相对来说比较安全,因为使用者不需要关心表中字段具体由哪些组成
9.4 优点
- 安全性高;
- 操作和调用简单,工作中会将最常用的查询数据作为一张视图
- 视图是逻辑数据;具备独立性
10 索引
10.1概念
索引是排好序的数据结构,相当于字典里的目录:
MYSQL 5版本 存储引擎 MYISAM
如果是MySQL 5的版本是不具备事物的处理;
MYSQL 8版本 存储引擎 INNODB
MySQL的索引底层是b+tree;
10.2 分类
聚簇索引和非聚簇索引区别:
- 聚簇索引一个表中只能有一个,通常是主键索引如果该表没有主键,则第一列唯一索引就是聚簇索引
- 聚簇索引找具体数据的时候,最底层叶子节点存的是主键和真实数据
- 非聚簇索引一个表中可以有多个,根据应用不同,有的时候可以分为普通索引、唯一索引等
- 非聚簇索引查数据的时候有回表操作,也就是非聚簇索引中存的是数据并不是真实数据,而是主键,找到主键以后会去主键索引中查找数据,改操作叫回表
10.3 创建非聚簇索
普通索引
-- 创建普通索引语法:
create index index1
on tx(name)
2、alter table 表名 add index 索引名字(列名)
-- 删除索引:
alter table 表名 drop index 索引名字;
-- 创建表的时候创建普通索引:
create table 表名(
所有列,
index 索引名字(列名(length))
-- 如果是text|blob需要给length设置值;
)
-- 创建唯一索引:
-- 创建表时候创建
create table 表名(
所有列,
unique index 索引名字(列名(length))
-- 如果是text|blob需要给length设置值;
);
-- 建表后添加:
create unique index index1
on tx(name)
2、alter table 表名 add unique index 索引名字(列名)
-- 复合索引:
create index 索引名字 on 表(列1,列2)
10.4 *创建索引的条件
1.主键就是聚簇(聚集)索引;
2.频繁查询的条件字段可以创建索引
3.外键列可以创建索引;
4.查询中经常用来排序的字段可以放进索引
10.5 *不创建索引的条件
- 表记录太少不能创建索引
- 频繁去更新的字段不适合创建
- 经常增删改的表,不适合创建
- 如果有某一列中有大量重复的数据,不适合创建;
- where条件用不到的字段不适合创建;
11 存储过程(了解)
11.1 概念
存储过程是一组为了完成特点功能的语句集,事先编译好存放在数据库内部,一经编译,永久生效,可以重复调用
11.2优缺点
缺点:
- 可移植性很差;
- 对简单的sql写存储过程毫无意义
- 团队开发需要一个固定的标准,不然维护极其困难
- 开发调试极不方便
- 复杂的sql业务使用起来极不方便
优点:
- 存储过程优点类似方法,调用来说安全;
- 一般sql关联几张表,需要进行数据库链接好几次,但是存储过程只需要一次;
- 创建的时候可以直接进行编译,sql语句是先编译后执行
-- 语法:
-- 创建输入参数的存储过程:
-- 需求:给tx表新增一条数据:
delimiter
||
create procedure abc(in id int,in name varchar(10),money int)
begin
-- 声明变量赋值:
declare i int default 0;
declare i2 varchar(10) default null;
declare i3 int default 0;
-- 将输入参数与之对应赋值:
set i=id;
set i2=name;
set i3=money;
insert into tx values (i,i2,i3);
end ||
-- 调用:
call abc(null,'王五',1000)
12 触发器(了解)
12.1 触发器概念
触发器(trigger)是SQL server 提供给程序员和数据分析员来保证数据完整性的一种方法,它是与表事件相关的特殊的存储过程,它的执行不是由程序调用,也不是手工启动,而是由事件来触发**,*比如当对一个表进行操作( insert,delete, update)时就会激活它执行 。触发器经常用于加强数据的完整性约束和业务规则等。
触发器可以查询其他表,而且可以包含复杂的SQL语句。**它们主要用于强制服从复杂的业务规则或要求。**例如:您可以根据客户当前的帐户状态,控制是否允许插入新订单。
12.2 语法
-- delimiter 声明是一段代码 程序:
DELIMITER |
CREATE TRIGGER `<databaseName>`.`<triggerName>`
< [ BEFORE | AFTER ] > < [ INSERT | UPDATE | DELETE ] >
ON [dbo]<tableName> //dbo代表该表的所有者
FOR EACH ROW -- 对于 每一行数据的操作
BEGIN
--do something
END |
12.3 案例
创建两张表:商品表(goods) 、订单表(orders);
goods(id,goods_name,goods_num);
orders(id,goods_id,num);
当订单表下订单的时候,商品表中对应的商品数量会减少。
create table goods(
id int primary key auto_increment,
goods_name varchar(20),
goods_num int
);
create table orders(
id int primary key auto_increment,
goods_id int,
num int
);
insert into goods values(null,'华为手机',100)
,(null,'小米手机',100);
-- 创建触发器:
delimiter ||
create trigger order_insert_after after insert
on orders for each row
-- 只要订单表新增一行就会触发触发器:
begin
-- 触发触发器以后的具体操作:
update goods set goods_num=goods_num-new.num
where id=new.goods_id;
end ||
delimiter ;
delimiter ||
补充知识点:
触发器针对的是数据中每一行的记录,每行数据在操作前后都会有一个对应的状态,操作前的状态保存到Old关键字中,操作后的保存到new关键字中。
触发器类型 | new和old使用 |
---|---|
insert | 没有old,只有new,new表示的是将要插入进去的数据或者说表示已经插入进去的数据。 |
update | 有old、也有new。old表示的是更新之前的数据,new表示更新以后的数据。 |
delete | 没有new,只有old,表示删除前数据。 |
-- 查看当前触发器:
show triggers;
-- 删除触发器:
drop triggers 触发器名字;
需求:假如当前下单110个小米手机,会怎样?
在新增以前触发:
干的事情: 去查询一下当前的库存有多少,并进行判断,如果库存不够,则不执行;
create trigger order_insert_before before insert
on orders for each row
-- 只要订单表新增一行就会触发触发器:
begin
-- 触发触发器以后的具体操作:
select goods_num from goods where id = new.goods_id
into @num;
-- 进行判断:
if @num < new.num then
insert into xxx values (111);
end if;
end ||
delimiter ;
需求:假设现在订单中数量有问题,需要修改;(触发器的前提),商品表中数量也需要改变:
delimiter ||
create trigger order_update_after after update
on orders for each row
-- 只要订单表新增一行就会触发触发器:
begin
-- 触发触发器以后的具体操作: 10-5=5;
-- orders;10+10 goods:90-10
update goods set goods_num=goods_num-
(new.num-old.num)
where id=new.goods_id;
end ||
delimiter ;
需求:假设现在删除了一条订单(触发器前提):
商品表中数量应该新增:
delimiter ||
create trigger order_delete_after after delete
on orders for each row
-- 只要订单表删除以后,商品表恢复:
begin
update goods set goods_num=goods_num+
old.num
where id=old.goods_id;
end ||
delimiter ;
12.4 课堂作业
例如:您可以根据客户当前的帐户状态,控制是否允许插入新订单。
-- 如果state=0说明账户不可用,如果state等于1说明账户可用。
-- 客户表: user(id,username,state);
-- 订单表: order(id,goods_id,num,user_id)
3.6 DCL(了解)
3.6.1 概念
数据控制语言 (Data Control Language) 在SQL语言中,是一种可对数据访问权进行控制的指令,它可以控制特定用户账户对数据表、查看表、存储程序、用户自定义函数等数据库对象的控制权。由 GRANT 和 REVOKE 两个指令组成。
3.6.2 语法
-- 查看当前所有用户和连接名字:
select user,host from mysql.user;
-- 创建用户和密码:
create user 'xiaowang'@'localhost'
identified by '123';
-- 修改密码:
set password for 'xiaowang'@'localhost'
='666';
-- 赋权限:
grant 权限(增,删,改,查) on 库.表
to 用户@'localhost';
grant select on demodb.goods
to 'xiaowang'@'localhost';
-- 取消权限:
revoke 权限 on 库.表
from '用户'@'服务器名字';
二、JDBC
1 概念
java database connectivity(java和数据库的连接)。
jdbc是sun公司提供了java代码连接数据库的接口,各数据库厂商需要根据接口来进行编写各个数据库的实现类(通常情况下是.jar包),实现java代码与各数据库的连接。
2 案例
public class JdbcUpdate {
public static void main(String[] args) {
// jdbc步骤:
try {
// 加载驱动:
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取连接对象;
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/demodb",
"root",
"root");
// 获取执行sql的对象:
Statement stmt = conn.createStatement();
// 执行sql 获取返回值:
String sql="update tx set money=money-100 where id=1";
int i = stmt.executeUpdate(sql);
System.out.println(i);
// 释放资源
// 遵循后使用先释放:
stmt.close();
conn.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3 jdbc类详讲
3.1 DriverManager
用于管理一组JDBC驱动程序的基本服务。
常用方法 | 含义 |
---|---|
static void registerDriver(Driver driver) | 用来加载驱动; |
Connection getConnection(String url, String user, String password) | 获取连接对象 |
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
// 获取连接对象:
// 主要解释参数:
Connection getConnection(String url, String user, String password)
// 连接:
url:jdbc:数据库类型://mysql服务器名:端口号/数据库名称
//如果服务器名字是本地,且端口号是3306
jdbc:mysql:///数据库名字
user: 用户名
password:密码;
3.2 Connection
- 获取执行sql的对象
Statement createStatement()
// 创建一个Statement对象——用于将SQL语句发送到数据库
- 事务的处理
void commit() //事务提交;
void rollback() // 事务回滚;
setAutoCommit(boolean autoCommit) // 开启事务;
3.3 Statement
用于执行静态SQL语句并返回其生成的结果的对象。
int executeUpdate(String sql)
// 执行ddl或者dml 使用该方法:
// 返回值:(1)SQL数据操作语言(DML)语句的行计数;(2)ddl的话,返回值是0;
ResultSet executeQuery(String sql)
//执行给定的SQL语句,返回单个ResultSet对象。
3.4 ResultSet
通常通过执行查询数据库的语句生成。
boolean next()
//`ResultSet`对象光标位于第一行之前。`next`方法将光标移动到下一行,next()方法直到最后一行数据的下一行返回`false` ,因此可以在`while`循环中使用循环来遍历结果集。
xxx getXxx();
例如: rs.getString()
String getString(String columnLabel)
// string 参数 表示的是:列名或者说别名;
String getString(int columnIndex)
//int 参数:columnIndex - 第一列是1,第二列是2,...
作业1: jdbc的删除、ddl、新增‘’ 对tx表;
作业2: jdbc去查询emp表数据并遍历出来;
作业3: 编写主方法:
使用控制台输入: 用户名;
控制台输入:密码;(Scanner)
布尔值 flag = login (用户名,密码);
if(flag){
打印"登录成功";
}else{
打印"登录失败";
}
login(){
jdbc
String sql ="select * from user where username= 用户名 and password= 密码 "
}
3.5 PreparedStatement
解决sql注入现象;
-- select * from user where username='a'
and password ='a'
or '1'='1'
-- 因为Statement在传入字符串参数的时候,是字符串拼接起来的,因此可以拼接or恒等式的情况出现sql注入现象。
PreparedStatement对象执行预编译sql,并且执行效率要比Statement高。
//connection创建PreparedStatement
PreparedStatement prepareStatement(String sql); //注意:此处的sql可以是静态sql;如果是带有参数的时候,通常选择用?来代替;
//void setXxx(parameterIndex,xxx)方法:
给?进行赋值的作用:
parameterIndex 表示的是第几个问号,问号从1开始;
xxx :表示输入进来的参数值;
setXxx()——》xxx和数据库有关
// 执行sql:返回结果:
// 可以使用在dml|ddl:
int executeUpdate();
// 执行dql语句:
ResultSet executeQuery();
4 事务
4.1 回顾mysql事务
如果一个包含了多个步骤的业务操作,被事务所管理要么同时成功,要么同时失败。
4.2 事务在jdbc中的编写
void commit() //事务提交;
-- 当代码正常执行完成的时候进行事务提交;
void rollback() // 事务回滚;
// 当代码出现异常或者逻辑错误的时候进行事务回滚;
setAutoCommit(boolean autoCommit) // 开启事务;
// 如果不写开启事务方法,表示默认自动提交,即相当于参数autoCommit=true;如果要手动提交需要将autoCommit参数设置为false;
// 当获取到Connection对象以后,可以开启事务;
5 工具类
工具类通常是使用类名.方法来调用,因此工具类中方法为静态方法:
1、最常重复的莫过于加载驱动和获取连接对象;
2、释放资源也在疯狂重复,其中dql和dml|ddl最大的区别在于dql中多了一个参数,因此可使用方法重载进行创建同名静态方法实现释放资源。
public class JdbcUtil {
private static String url=null;
private static String user=null;
private static String password=null;
private static String driver=null;
// Properties类表示一组持久的属性。
// Properties可以保存到流中或从流中加载。
// 属性列表中的每个键及其对应的值都是一个字符串。
// String getProperty(String key) // 根据key取值:
//
static{
try {
// 创建Properties对象:
Properties properties =new Properties();
// 找到该文件:
InputStream is = JdbcUtil.class.getClassLoader().
getResourceAsStream("jdbc.properties");
// 从文件中获取键值对:
properties.load(is);
// 根据键获取值:
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
driver = properties.getProperty("driver");
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 封装 加载驱动和获取连接对象:
public static Connection getConnection(){
try {
return DriverManager.getConnection(url,user,password);
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
// 封装 释放资源:
// dml和dql释放资源释放的不同:dql比dml多一个ResultSet;
// 封装查询的释放资源
public static void close(ResultSet rs, Statement stmt, Connection conn){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 封装dml和ddl的释放资源:
public static void close(Statement stmt, Connection conn){
close(null,stmt,conn);
}
}
6 池化技术
6.1 概念
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
**连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中(连接池厂商),****当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。**使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。 [1]
6.2 优点
- 高效复用;
- 明显提高连接数据库性能;
6.3 连接池替换
以前通过DriverManager.getConnection()获取连接对象的方式;选择使用DataSource接口获取连接对象;
注意: JDBC 2.0 API中新增的DataSource
接口提供了另一种连接到数据源的方法。 使用DataSource
对象是连接到数据源的首选方法。
6.4 c3p0连接池
//导入jar包:c3p0-版本号;mchange-commons-java-版本号; 放lib里面 添加到库(add as library)
// 将配置文件导入至src目录下:文件名必须是:c3p0.properties or c3p0-config.xml
// 创建连接池或者说创建数据源(DataSource);
ComboPooledDataSource c = new ComboPooledDataSource();
// 获取连接对象
Connection conn = c.getConnection();
6.5 druid 连接池
// 导入jar包放置在lib下面;
// 将.properties文件放置在src目录下,修改里面的值;
// druid采用工厂类创建方式进行创建数据源:
// 使用连接池获取到DataSource:
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
// 使用获取到的DataSource获取Connection;
Connection conn = ds.getConnection();
三、Servlet
1 概念
server applet指的是运行在服务器端的小程序。
对于web服务器(tomcat来讲),并不是所有java代码都可以直接放置在web服务器中进行操作(获取请求信息或者做出响应),如果要放置在tomcat中,则必须实现Servlet接口,这样就可以被tomcat所识别。
2 创建Servlet
1、新建java类——》实现Servlet接口——》重写方法init()|service()|destroy();
<!-- 2、在web.xml文件中进行配置Servlet映射 -->
<servlet>
<servlet-name>abc</servlet-name>
<servlet-class>com.ry.servlet.Demo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>abc</servlet-name>
<url-pattern>/demo</url-pattern>
</servlet-mapping>
3 运行原理
- 浏览器通过url发送请求到tomcat;
- tomcat解析请求获取到url中真实资源(除了项目以外)即 /demo;
- 根据/demo去web.xml配置文件中Servlet-mapping去找url-pattern与之对比,如果一样;
- 获取到servlet-mapping中的servlet-name去找对应的servlet-class;
- 找到servlet-class以后,通过Class.forName(“全限定类名”)反射创建对象newInstance();
- 去执行里面的方法。
4 生命周期
- init() 初始化:
- 默认情况下是第一次访问servlet的时候执行;
- 可以设置n 设置为正数的时候是tomcat启动后,servlet被初始化;
- servlet是单例还是多例? 单例。
- service() 提供服务
- 每访问一次执行一次提供服务方法;
- destroy() 销毁
- 垂死的状态。当服务正常关闭的时候执行该方法;如果是非正常关闭不执行该方法(杀死进程的方式);(linux 系统: kill -9 进程号)
5 体系结构
课堂作业
- 创建类 继承GenericServlet创建Servlet;
- 继承HttpServlet创建Servlet;
必须掌握的知识
- request对象的使用
- response对象的使用
6 http协议
6.1概念
hyper text transfer protocol(超文本传输协议),定义了客户端和服务器端通信规则。
6.2特点
- http协议是基于tcp/ip的高级协议;
- http协议默认端口号是80;https协议默认端口号是443
- 严格遵守一次请求对应一次响应;
- 每次请求之间是相互独立的,不能交互数据。
6.3版本
1.0 1.1
1.1版本开始具备复用连接的效果,请求完成以后不会立马断开连接,会进行等待,如果等待时间内还需要发送信息,就会选择复用。
7 Request对象
7.1 请求消息数据格式
*post请求和get请求的区别
- get请求请求参数和参数值在请求行中出现,并且跟在url的后面;post请求请求参数和参数值在请求体中出现;
- 由于get请求请求参数在浏览器地址栏中显示,因此相对来说不安全,所以登录和注册应该使用post请求。
- get请求请求参数的长度是有限制的,因此get请求不能实现文件上传,如果要文件上传必须得是post请求。
请求行: GET /abc/demo3?username=zhangsan HTTP/1.1
POST /abc/demo3 HTTP/1.1
请求方式 /项目名称/真实资源 协议版本号;
请求头: 键值对格式;
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,de;q=0.8
Cache-Control: max-age=0
Connection: keep-alive
Host: localhost
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
sec-ch-ua: "Chromium";v="112", "Google Chrome";v="112", "Not:A-Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
请求体:
get请求没有请求体,post请求才有请求体(请求体中存放的就是参数=参数值)
7.2 获取请求头消息数据(了解)
s// 以 String 的形式返回指定请求头的值。
String getHeader(String name)
// 此请求包含的所有头名称的枚举
Enumeration<E> getHeaderNames()
7.3 获取请求信息数据
*必须得会
// *获取请求的URI: /项目名字/真实资源
String requestURI = request.getRequestURI();
System.out.println(requestURI);
// *获取项目名字或者动态获取项目名字:
String contextPath = request.getContextPath();
System.out.println(contextPath);
了解即可
// 获取请求方式:
String method = request.getMethod();
System.out.println(method);
// 获取具体访问的资源 真实资源:
String servletPath = request.getServletPath();
System.out.println(servletPath);
// 获取get请求方式的参数值:
String queryString = request.getQueryString();
System.out.println(queryString);
// 获取请求的URL:
StringBuffer requestURL = request.getRequestURL();
System.out.println(requestURL);
// 获取协议以及版本:
String protocol = request.getProtocol();
System.out.println(protocol);
7.4 获取请求体中的数据
// 返回的是字符输入流,只能操作字符数据
BufferedReader reader = request.getReader();
String s = reader.readLine();
System.out.println(s);
7.5 请求信息获取乱码问题
// 设置获取请求不乱码: request.setCharacterEncoding("utf-8");
tomcat8开始,tomcat自己解决了get请求乱码问题,现在只需要设置一下,解决post请求乱码问题即可。
7.6 * 核心方法
7.6.1 获取请求参数的四种方式
方法 | 含义 |
---|---|
1 String getParameter(String name) | 根据请求参数名获取参数值 |
2 String[] getParameterValues(String name) | 根据参数名获取返回来的数组:chechbox; |
public java.util.Enumeration getParameterNames() | 获取所有请求参数的名字; |
java.util.Map<K, V> getParameterMap() | 获取所有请求参数名和参数值,并将其封装到Map中,其中参数名是key,参数值是value。 |
课堂作业
1、获取所有请求头和请求头的值,以键值对方式显示。
2、 获取所有请求参数的名字,并相对应的获取参数值;
3、获取所有请求参数的map集合,并遍历。
7.6.2 *域对象
简而言之,域对象就是一个有作用域的对象。主要负责在自己的作用域范围内进行数据共享。
request作用域范围:用于一次请求之间共享数据。
// 给域对象赋值:
//域对象使用键值对赋值:键是字符串类型可以随便写,值是传入的真实值;
void setAttribute(String name, Object o)
// 使用域对象根据键名称取值
Object getAttribute(String name)
// 根据键名移除值:
void removeAttribute(String name)
7.6.3 *请求转发
// 获取RequestDispatcher对象
RequestDispatcher getRequestDispatcher(String path)
//使用该对象转发:
void forward(ServletRequest request, ServletResponse response)
路径
- 绝对路径: 以"/"开头;/ServletDemo
- 相对路径: 以.开头或者直接是资源;ServletDemo
需求:ServletDemo1负责获取表单中发送过来的用户名,并转发值ServletDemo2,在ServletDemo2中获取并打印。
*特点
- 转发是一次请求,因此可以使用request域对象进行赋值传值;
- 转发浏览器地址栏内容不变;
- 转发可以直接写绝对路径进行跳转,因为转发是项目内部资源进行跳转;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oxMDbPQt-1679210949846)(H:\2211Q\三阶段\zhuanfa.png)]
8 Response对象
8.1 响应消息数据格式
响应行: HTTP/1.1 200
格式: 协议/协议版本号 响应状态码
响应状态码:是三位数字组成:
1xx:服务器接收客户端信息,但是接收还没有完成,过了很长一段时间,服务器给客户端响应1xx状态码确定是否发送完成。
2xx: 200:响应成功.
3xx: 302:重定向; 304:访问缓存;
4xx: 客户端报错: 404:路径问题;
5xx: 500:服务器端报错。代码问题
响应头:
Content-Length: 0
Date: Fri, 10 Mar 2023 02:21:35 GMT
Keep-Alive: timeout=20
Connection: keep-alive
content-type:服务器告诉客户端响应体数据格式是怎样的;
content-disposition: 默认情况 in-line(以页面形式打开);attachment;filename=xxx(属性值为它用来文件下载)
响应体:
响应到页面上的内容.
8.2 * 重定向
void sendRedirect(String location)
response.sendRedirect(request.getContextPath()+"/真实资源")
*特点
- 重定向是两次请求,因此无法使用request域对象进行赋值取值;
- 重定向地址栏会发生变化;
- 重定向可以跳转到任何站点资源。如baidu;因此重定向需要跟应用上下文(动态获取)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gnuMrmgu-1679210949847)(H:\2211Q\三阶段\chongdingxiang.png)]
8.3 响应到页面上的方法
// 对字符串响应到页面上的方法:
PrintWriter writer = response.getWriter();
writer.write("1231232332");
// 返回的是字节输出流——》验证码
ServletOutputStream outputStream = response.getOutputStream();
课堂案例:
验证码本身是一张图片。
使用response.getOutputStream();
package cn.ry.servlet;
import javax.imageio.ImageIO;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;
@WebServlet("/ServletYzm")
public class ServletYzm extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置获取请求不乱码:
request.setCharacterEncoding("utf-8");
// 创建一个图片对象:
int width=80;
int height=50;
BufferedImage image1 = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
// 对创建出来的图片对象进行美化
// 渲染对象:
Graphics g = image1.getGraphics();
// 包括修改背景色
g.setColor(new Color(210,200,200));
g.fillRect(0,0,width,height);
// 往里面填写字体
// 获取所有的字母和数字放置在字符串里:
String s="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
// 从字符串中取到字符并填充到图片:
Random r = new Random();
for (int i = 1; i <=4; i++) {
// 随机获取字符串中的字符:
// 随机获取字符串的索引:
int i1 = r.nextInt(s.length());
// 通过索引获取字符:
char c = s.charAt(i1);
// 为渲染对象设置新的字体颜色
g.setColor(new Color(
r.nextInt(100),
r.nextInt(100),r.nextInt(100)
));
// 设置字体:
g.setFont(new Font("Times New Roman",Font.BOLD,18));
// 将上面获取的字符给画入到图片里:
g.drawString(c+"",width/5*i,height/2);
}
// 将图片响应到浏览器:
ImageIO.write(image1,"jpg",
response.getOutputStream());
}
}
9 ServletContext对象
9.1 概念
代表的是整个web应用来通信的对象。
9.2 主要方法
ServletContext对象是一个域对象。因此具备域对象的所有方法。它的作用域范围很大,当前项目内共享数据。
// 给域对象赋值:
//域对象使用键值对赋值:键是字符串类型可以随便写,值是传入的真实值;
void setAttribute(String name, Object o)
// 使用域对象根据键名称取值
Object getAttribute(String name)
// 根据键名移除值:
void removeAttribute(String name)
// 获取mime类型: 大类型/小类型 如果想看的更仔细点在tomcat/conf/web.xml可以看到
String getMimeType(String file)
// 返回真实资源在服务器上的详细路径:
String getRealPath(String path)
//例如:例如,可以通过对 "http://host/contextPath/index.html" 的请求使路径 "/index.html"
9.3 文件下载
课堂案例
<a href="/abc/DownloadServlet?filename=download.txt">点击下载</a>
package com.ry.servlet;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.FileInputStream;
import java.io.IOException;
@WebServlet("/DownloadServlet")
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置获取请求不乱码:
request.setCharacterEncoding("utf-8");
// 设置响应信息不中文乱码:
response.setContentType("text/html;charset=utf-8");
// 根据参数名获取到txt的名字:
String filename = request.getParameter("filename");
// 获取真实的路径: tomcat/项目/webapp
//具体资源
String realPath = request.getServletContext().getRealPath(filename);
// 用字节流将其加载进入内存:
FileInputStream fileInputStream = new FileInputStream(realPath);
// 响应:
String mimetype=request.getServletContext().getMimeType(filename);
response.setHeader("content-type",mimetype); //s代表响应头,s1代表响应内容
// 如果是图片或者文件还得专门设置响应格式;
response.setHeader("content-disposition","attachment");
// 将输入流中的内容写到输出流中:
ServletOutputStream outputStream = response.getOutputStream();
//
//
//
byte[] bytes= new byte[1024*1024];
int len=0;
while ((len=fileInputStream.read(bytes))!=-1){
// 代表的是缓冲区内部的字节总数,如果为-1的时候,就读完了
outputStream.write(bytes,0,len);
}
}
}
10 会话技术
10.1 概念
一次会话: 浏览器第一次发送请求,会话成立,直到有一方断开为止(浏览器关闭或者服务器停),会话结束。一次会话期间可以有多次请求和响应。
10.2 分类
客户端会话技术 Cookie
服务器端会话技术 Session
根据数据存储位置进行划分的,客户端会话技术Cookie将少部分信息存储在浏览器中;服务器端会话技术Session将信息存储在服务器端。
10.3 Cookie
需求:演示cookie实现原理:
在ServletCookie1中新建Cookie,响应对象将其发送至浏览器进行保存,不关闭浏览器和服务器的情况下,直接访问ServletCookie2在控制台 打印Cookie1中存储的信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fCLWPcKW-1679210949848)(H:\2211Q\三阶段\cookie运行原理.png)]
// 第一步:创建Cookie:
new Cookie(String key,String value);
// 第二步:服务器端发送Cookie到客户端:
Response.addCookie(cookie对象);
// 第三步:获取客户端所存储的Cookie信息:
Request.getCookies();
10.4 相关方法
String getName() //用来获取Cookie对象中的键;
String getValue() //用来获取Cookie对象中的值;
10.5 特别之处
一次会话中,是否可以发送多个Cookie?
可以。实现方式:可以一次多创建几个Cookie对象,然后将每个Cookie对象进行发送。
Cookie可以在浏览器中保留多长时间?
默认情况下,浏览器关闭,cookie数据就失效,如果强行获取会报空指针异常;
如何设置Cookie在浏览器中保存时间:
public void setMaxAge(int expiry)
设置 cookie 的最大生存时间,以秒为单位。
cookie中是否可以存储中文?
从tomcat8开始,支持存储中文数据;
Cookie共享数据:
在同一个tomcat服务器部署了多个项目,默认情况下无法实现项目中Cookie数据的共享;如果非要实现, setPath(String uri) ;
如果不同的tomcat服务器部署了不同的web项目,默认情况下也是无法实现Cookie数据共享;如果非要实现,使用到的是域名方式实现:void setDomain(String pattern)
10.6 Cookie的特点
- 每个Cookie存储的大小有限制4kb;
- cookie在同一域名的情况下存储数量最多20个;
- cookie存储在浏览器上。
分析:cookie存储的数据往往是不敏感的数据:
记住密码。
10.7 Session
服务器端会话技术。用于一次会话中多次请求之间实现数据共享,将数据保留在对象HttpSession中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RgKDorGv-1679210949848)(H:\2211Q\三阶段\session原理.png)]
10.8 *session重要方法
//如何获取Session对象:
//使用request对象获取HttpSession对象
HttpSession getSession()
// session是一个域对象,作用域范围:一次会话多次请求数据共享。
10.9 Session运行原理
创建ServletSession01和ServletSession02;
在session01中创建Session对象,在Session02中也创建Session对象;
在session01中使用域对象赋值方式赋值,在02中获取;
10.10 Session特别之处
当客户端关闭,服务器不关闭,第二次获取的Session是不是同一个?
这个操作相当于两次会话,因此肯定不是同一个。
实现是基于cookie持久化实现的。
//获取Session对象:
HttpSession session = request.getSession();
session.setAttribute("user","张三");
//将session对象的id放置在cookie中,进行实现。
Cookie c= new Cookie("JSESSIONID",session.getId());
// 设置Cookie持久化操作.
c.setMaxAge(30);
response.addCookie(c);
当客户端不关闭,服务器端关闭,两次获取到的是否相同?
默认情况下肯定不是同一个。session活化(在服务器启动的时候,将session文件转化到内存中。)与钝化(在服务器正常关闭的时候,将session对象序列化到硬盘上)。如果使用tomcat启动,是遵守活化钝化规则。
销毁session:
1、服务器关闭;
2、session对象中方法: session. invalidate() 强行销毁session对象;
3、在conf/web.xml中 修改session-config即可(设置session销毁时间)。
10.11 session的特点
- session用于存储一次会话多次请求数据,存在服务器端;
- session可以存储任意类型数据,大小不固定;
- session因为存储数据在服务器端,相对来说安全;
课堂作业
作业一:
1、新建一个html页面,表单跳转到servlet;
2、如果是第一次访问该servlet,那么是没有cookie信息的,我们就持久化一下;
3、如果不是第一次访问,就返回上一次访问的时间。
四、JSP
- 了解jsp运行原理
- 掌握常用指令;
- 熟练掌握四大作用域并使用(记住)
- ‘熟练掌握九大内置对象(记)
- 熟练掌握el和 jstl 表达式(会用)
1 JSP概念
java server pages 运行在服务器端的java页面.可以理解为即可以写java代码,又可以写java代码又可以写静态页面(html CSS)
2 jsp原理
jsp本身是一个servlet
3 JSP脚本
<% 代码 %> 应该会出现service()方法中;
<%! 代码 %> 相当于全局变量,不推荐使用成员变量
<%= 代码 %> 此处代码应该是表达式:会直接显示到页面上
4JSP的注释
<%---注释的内容---%> 如果使用JSP的注释在f12的元素中是完全看不到的
<!--代码--> 在HTML可以看到注释情况
5 JSP的指令
5.1 page指令
-
contentType 指的是响应到页面上的格式:text/html;charset=UTF-8
language JSP可支持的语言:java
isErrorpage:当前页面是否是异常页面,默认是false,如果是异常页面的话需要改为true.改为true以后可以使用exeception内置对象捕获异常
errorpage: 如果当前页面发生异常会跳转到某个页面
isELIgnored: 当前页面是否可以写el表达式;默认是false,允许编写el表达式
pageEncoding:设置页面请求编码,如果是eclipes需要加: idea不需要加
import:导包
5.2 include指令
包含的页面
<%@include file="资源文件" %>
5.3 taglib指令
和jstl表达式一块讲
指令主要用来导入JSP相关标签库的操作。
<%-- prefix 使用前缀 uri:资源 -->
<%@taglib prefix="" uri="" %>
6 四大作用域
实际上指的是servlet中的域对象,稍微有点区别
在范围上,从大到小:
application作用域 等同于 servletContext对象
session作用域 等同于 HttpSession;
request作用域 等同于 httpservletRequset对象
pageContext作用域 作用范围 :仅仅是当前JSP页面范围内共享数据。
7 九大内置对象
内置对象:在JSP页面中,不需要进行创建就可以直接使用的对象就是内置对象
application
session
request 可以请求转发:
pageContext
page 真实的类型:Object
response 最主要的方法:重定向;响应数据到页面上
out 真是类型:jspwriter,相当于一个带缓存的printWriter,即:如果要使用out对象响应到页面上,那么首先会响应到缓存 中,因此一定会比response对象响应的要靠后
config servlet自身配置
exception 异常
8 el表达式
8.1 概念
express language 表达式语句
8.2 语法
${} //用来简化jsp页面中的java代码
8.3 用法
8.3.1 运算
- 支持算术运算符; + - * / (div)%(mod)
- 支持逻辑运算符: && ||
- 还支持比较运算符: >= <= == != < >
- 还支持三元运算符
- 支持空运算符:可以判断对象、数组、集合是否为空
- ${empty 对象} 如果是空返回的是true,否则false
- ${not empty 对象}
8.3.2 *el表达式取值
注意:el表达式中取值,只能获取域对象中传过来的值
<%=session.getAttribute("user")%>
换做el表达式:
${sessionScope.user} 凡是域对象都有 作用域Scope
但是我们一般情况下都省略了作用域:如果省略了作用域名字,页面会从最小的作用域开始去查找,直到根据键名找到值为止
8.3.3 el表达式获取对象
${键名} 取到对象以后 ${对象.属性}
//要求在类中需要有get set方法
8.3.4 el表达式获取list
${键名[索引]} 取到对象以后取属性 ${键名[索引].属性}
8.3.5 el表达式获取map
${键名["map的key"]}取到对象取属性 ${键名["map的key"].属性}
${键名.map中的key.属性名} 取到对象取属性 ${键名.map中的key.属性}
8.3.6 禁用el表达式
\${}
8.3.7 el 表达式特殊
el表达式的隐式对象: el表达式有11个隐式对象。*主要用法:
JSP中动态获取项目名字(应用上下文)
${pageContext.request.contextpath}
9 jstl 表达式
java server taglib language(jsp的标准标签库)
使用前必须
导包: jstl-版本号 standard-
java server taglib language(jsp的标准标签库);
使用前必须
导包:jstl-版本号 standard-版本包;
使用taglib指令引入:
9.1 c:if
<%--
c:if 标签相当于java代码中的if判断:
test="" 相当于java代码中的条件
if(条件){
...内容
}
<c:if test="条件">内容</c:if>
注意:java里面有else但是c:if里面没有,如果要实现else,可以直接
<c:if test="">内容</c:if>即可.
--%>
<c:if test="${user.name=='张三'}">
<input type="radio" name="sex" value="男" checked>男
<input type="radio" name="sex" value="女">女
</c:if>
<c:if test="${user.name!='张三'}">
<input type="radio" name="sex" value="男" >男
<input type="radio" name="sex" value="女" checked>女
</c:if>
9.2 c:choose
<%--
c:choose 相当于java代码中的switch case
<c:when test=""></c:when> 相当于case 几:
<c:otherwise></c:otherwise> 相当于default
--%>
<c:choose>
<c:when test="${user.id==1}">今天星期二</c:when>
<c:when test="${user.id==11}">今天星期三</c:when>
<c:otherwise>天天星期天</c:otherwise>
</c:choose>
9.3 c:foreach
<%--
foreach分两种情况,
一种是相当于fori这种基本for循环;
var 声明变量;
begin:变量初始化的值;
end:相当于变量结束的值;
step: 递进相当于i++;
循环体在标签内部;
--%>
<c:forEach var="i" begin="0" end="10" step="1">
今天是三阶段学习的第${i}天<br>
</c:forEach>
<%--
第二种:遍历集合:
var 属性 我们通常放置的是集合里面的对象:
items属性未来将要填写集合,必须el表达式填写;
varStatus 表示循环的状态,实际上是循环了多少次,次数;
--%>
<table>
<tr>
<th>序号</th><th>id</th><th>姓名</th>
</tr>
<c:forEach var="user" items="${list}" varStatus="s">
<tr>
<td>${s.count}</td><td>${user.id}</td><td>${user.name}</td>
</tr>
</c:forEach>
</table>
- 了解概念Javabean
- 掌握三层架构模式
- 登录
- 基本查询
- 过滤器
10 JavaBean
javabean实际上是特殊的java类
- 类名必须被public修饰;
- 属性名必须被private修饰;
- 类中必须有无参构造;
- 必须要get set方法;
11 三层架构
mvc 主要目的:适合团队协作,可以解耦合。
m代表的是model层:m层主要用来完成与数据库之间的映射以及增删改查的处理。(其实就是我们的实体类和JDBC)
v代表的是视图层: v层主要用来将数据可视化进行展示的内容。(对于当下来说此处应该是我们的JSP页面)
c代表的是控制层: c代表的是controller,业务逻辑层,进行业务逻辑的编写,以及资源的跳转。(专门写servlet的包、service 包)
12 过滤器
过滤器、监听器(已经不用)、selevt被称为javaweb三大组件
12.1 作用
当进行web访问目标资源的时候,可以使用过滤器进行过滤,对请求起到请求拦截的作用或者说增强请求的作用。
12.2 用途
- 敏感字的过滤;
- 设置编码
- 登录验证;
12.3 创建
两种方式:
12.3.1 web.xml配置
<filter>
<filter-name>d1</filter-name>
<filter-class>com.ry.filter.FilterD1</filter-class>
</filter>
<filter-mapping>
<filter-name>d1</filter-name>
<!-- 此时的url指的是需要过滤的资源-->
<!-- 如下所示,访问index.jsp会过滤,其余不会进入过滤器 -->
<!-- 如果所有资源都要经过过滤器 要加上/* -->
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
12.4注解配置
@WebFilter("/*") ///*指的是所有资源
12.4 生命周期
- init():当服务器正常启动的时候会执行,并且只执行一次;
- doFilter()当访问需要过滤的资源时,执行一次,并且每访问一次执行一次
- destory():和servlet中销毁一样,正常关闭服务器的时候执行一次
12.5 过滤器链
简单来说,过滤器链就是多个过滤器同时同个资源进行过滤
多个过滤器执行顺序:
如果是web.xml中进行配置,则从上到下依次执行,遵守先进入后出去的规则
如果是注解配置的过滤器,根据过滤器的类名进行排序,数字优先级最高,如果没有数字从a-z排序,排完以后如果字母相同,则按照位数上的数字从小到大排序(FilterDemo01优先于FilterDemo1执行),依旧遵守先进入后出去的规则
12.6 项目中过滤器的使用
1、在三阶段项目中创建过滤器,对所有资源进行过滤;
2、默认情况下放行部分资源,如login.jsp等;
使用request.getRequestURI可以获取到资源,在使用字符串中的contains()方法进行判断;
如果包含login.jsp就放行,否则就过滤:
3、在第2步的基础上再一次进行判断,如果当前会话中包含了用户的信息,则放行;否则判断当前未登录,无法访问系统别的页面,使其跳转到login.jsp进行登录。
课后作业
新增用户:
1、在menu.jsp中点击新增客户,跳转到/customer/add.jsp中;
2、add.jsp中存在文件上传元素,因此需要应用今天的文件上传案例;如果使用文件上传enctype="multpart"则request.getParameter()等方法会失效;选择使用文件上传中的获取普通表单元素进行赋值操作,此处可:Map map = new HashMap();
map.put(“普通表单元素的name属性值”,“真实值”);
文件上传的元素,也可以使用map进行操作,存的真实值应该是文件上传的真实路径以及名字;
封装到map以后 使用该工具类:
只要对象中的属性和map中的键完全一致,即可成功BeanUtils.populate(new 对象,map);
3、封装到对象以后调用Service (无复杂业务逻辑)——》dao去新增用户到数据库;
4、如果新增成功,则转发或重定向到成功页面;否则到失败页面。
删除用户:
delete from 表 where id=?
<a href="${pageContext.request.contextPath}/Servlet?参数名=${id值}">删除</a>
传过来id以后 再Servlet中 request.getParameter(“参数名”);
将其传入到Service——》dao删除;此处注意 获取到的id是String类型 需要在Service中转成int型 传入到dao层中;
操作完毕后,跳转到成功页面或者失败页面。