MySQL
MySQL数据库:就是指数据库管理系统
数据库:指文件夹,在目录中的位置会有专门存储数据的文件夹
表:其实指文件,数据库中的某一个文件
MySQL数据库的服务开启与关闭
services.msc
dos窗口 输入 启动 :net start mysql
停止:net stop mysql
MySQL数据库的登陆和退出
登陆:mysql -uroot -p密码
退出:exit
SQL
结构化查询语言
SQL的工业化标准
就是一套规范,使用这套规范做出的SQL语句,可以操作任意关系型数据库的,通用的
方言:由数据库厂商自己研发出的SQL语句,只能操作自己研发的数据库
SQL的分类
DDL: 数据定义语言,这个分类的sql语句,主要操作数据库和表结构
*DML: 数据操作语言,这个分类的sql语句,主要操作表中的数据,增加、删除、修改
*DQL: 数据查询语言,这个分类的sql语句,主要操作的是表中的数据的查询
DCL: 数据控制语言,这个分类的sql语句,主要操作的是用户的权限等级,账号和密码等操作
DDL(数据定义语言)
针对数据库结构的操作:
查看有哪些数据库:
show databases;
创建数据库db2:
create database db2;
删除数据库db2
drop database db2
查看创建数据库的细节,查看数据库所用的创建语句
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9x5ZMez3-1615994809475)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200827115513348.png)]
show create database db2;
使用数据库db2
use db2
针对表结构的操作:
查看有哪些表:
show tables;
创建一张表
create table user(
name varchar(20),
age int
);
数据类型
int:整型
decimal(8,2):长度2,保留两位小数
date:日期 0000-00-00
datetime: 日期:0000-00-00 00:00:00
timestamp
varchar(10):字符串最大长度
删除user表
drop table user
查看表的创建细节
show create table user;
查看表结构
desc user;
添加一个新字段
alter table user add sex varchar(20)
修改字段的数据类型
alter table user modify sex int;
修改字段的名字
alter table user change sex gender varchar(20);
modify和change的区别
modify修改的是字段的数据类型
change修改的是字段的名字,也能修改数据类型
alter table user change gender gender int;
删除字段
alter table user drop gender;
修改表的名字
rename table user to person;
*DML(数据操作语言)
添加
向表中添加一条记录
insert into user (id,name,age) values (9527,'tom',18);
insert into user(name,age) value ('herry', 19);
insert into user values(9528,'rose',17);
错误格式
insert into user values('jeck');//错误
向表中添加多条记录
insert into user values(9529,'jeck',19),(9530,'tony',23),(9531,'mary',32)
注意:如果在dos里面添加中文,需修改临时编码
set names gbk
修改
将名字为jerry的年龄改成19
update user set age=19 where name='jerry'
注意: where 后面跟条件判断
修改所有记录
将所有年龄都改为20;
update user set age=19;
删除
删除一条记录
delete from user where name='jack';
删除所有记录
第一种
delete from user;
第二种
truncate user;
区别:
第一,逐行扫描,扫描一行删除一行
第二:先删除表,然后再重新创建一个。属于DDL里的
第三:第一种id自增的情况下会id不会归0,第二种会
*DQL(数据查询语言)
查询:
查看表中所有记录
select * from user;
注意:*代表通配符,所有的意思
查询所有记录(只查询指定字段)
select name,age from user;
复杂的条件查询
= > < >= <= != <>(不等于符号)
between…and…
in…
is null
is not null
and &&
or ||
distinct 去重
like 模糊查询
as 别名
查询名字为rose的记录
select * from user where name='rose'
查询id值为3的
select * from user where id=3;
查询年龄大于18的
select * from user where age>18;
查询年龄不等于18的
select * from user where age!=18;
select * from user where age<>18;
查询年龄在18到23之间
select * from user where age between 18 and 23;
//双边都包含的
查询年龄为17、19、55的有哪些
select * from user where age in(17,19,55);
select * from user where age=17||age=19||age=23
select * from user where age=17 or age=19 or age=23
查询名字为tom、rose、tony的
select * from user where name in('tom','rose','tony')
查询为性别为null的
select * from user where sex is null;
//错误写法
select * from user where sex=null;
查询性别不为null的
select * from user where sex is not null
查询名字为tony并且年龄为33的
select * from user where name='tony' and age=33;
select * from user where name='tony' && age=33;
查询年龄都有哪些(去重查询)
select distinct age from user;
查询名字中第二个字为三的
select * from user where name like '_三%';
_:在模糊查询中,代表任意一个字符
%:在模糊查询中,代表任意个字符
_或者%一定要和like连用
查询名字为3个字,中间为’三’的
select * from user where name like '_三_';
查询名字带’三’的
select * from user where name like '%三%';
查询名字姓张的
select * from user where name like '张%';
查询所有的记录(带别名)
查询名字为3个字,中间为’三’的
方式一
select id as 编号 ,name as 姓名,age as 年龄,sex as 性别 from user ;
方式二
select id 编号 ,name 姓名,age 年龄,sex 性别 from user ;
注意别名后面的汉字不加引号
注意除了给查询的字段起别名,还可以给表起别名
select * from user u;
select u.* from user u;
select u.id,u.name,u.age,u.sex from user u;
聚合函数
count() //统计记录条数
查询表中有多少条记录
select count(*) from user;
//或者
select count(id) from user;
sum() //统计某一个字段的总和
查询表中所有的年龄总和
select sum(age) from user;
max() //获取某个字段的最大值
查询年龄最大的是多少岁
select max(age) from user;
查询姓名最大(字母字典顺序最大)的是多少岁
select max(name) from user;
min() //获取某一字段的最小值
查询年龄最小的是多少岁
select min(age) from user;
avg() //获取某一字段的平均值
查询年龄平均值是多少岁
select avg(age) from user;
//或者
select sum(age)/count(id) from user;
select truncate(avg(age),0) from user;
排序查询
order by 字段名 asc/desc
asc 升序
desc 降序
按照年龄从小到大进行查询
select * from user order by age asc;
select * from user order by age;
注意: 默认排序是由小到大,叫做升序,asc
按照年龄从大到小进行查询
select * from user order by age desc;
注意: 是由大到小,叫做降序,desc
按照年龄从大到小进行排序,如果年龄相同再按照姓名的字典排序
select * from user order by age desc,name asc;
分组查询
group by 字段名
按照性别进行分组,查询出每组的人数
select sex,count(*) from user group by sex;
按照年龄进行分组,查询出每组中id值大于2的每组人数
select age,count(*) from user where id>2 group by age;
按照年龄进行分组,查询出每组总人数大于2的每组信息(人数,哪一组)
select age,count(*) from user group by age having count(*)>1;
having:也是条件判断,但是指分组后再进行判断,写在group by后面
where和having的区别
都是进行条件判断的关键字
1、where条件判断再分组前
having条件判断是在分组后
2、 where关键字要写在group by前面
having 关键字写在group by 后面
3、where 条件不能写聚合函数
having条件里可以写出聚合函数
分页查询
limit ?, ?
第一个问号:起始数
第二个问哈:每页显示的记录数
查询出第一页的数据
select * from user limit 0,3;
查询出第二页是数据
select * from user limit 3,3;
查询出第三页是数据
select * from user limit 6,3;
查出第n页的数据
select * from user limit (n-1)*每页显示的记录数,3;
约束
非空约束
not null;
非空: 不可以为null
在创建表的时候添加非空约束
create table user (
id int,
name varchar(11) not null
);
删除非空约束
alter table user modify name varchar(11);
为字段添加非空约束(需要保证表中指定字段没有null的数据)
alter table user modify name varchar(11) not null
唯一约束
unique
唯一:数据不能重复
在创建表的时候,添加唯一约束
create table user(
id int,
name varchar(20) unique
);
注意:null可以重复添加因为null根本不算一个值,所有没有约束
删除唯一约束
alter table user drop index name;
创建表之后再给某一个字段添加唯一约束
alter table user modify name varchar(20) unique;
主键约束
primary key;
主键约束:加了,就代表非空且唯一
注意:一般每张表都需要由主键约束,而且施加主键约束的字段 一般没有什么特殊含义
//在创建表的时候添加约束
create table user(
id int primary key,
name varchar(20),
age int
);
//删除主键
alter table user drop primary key;
然后需要继续删除非空约束才能彻底删除主键约束
//创建表之后再添加主键约束
alter table user modify id int primary key;
自动增长
auto_increment
删除表中全部记录
第一种:delete from user ;
第二种:truncate user;
区别二:在于如果主键设置了自动增长,方式一删除后再插入记录时主键是接走往删除前的递增,方式二 是会重新由1开始递增
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fu7cA5TD-1615994809477)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200828144754036.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uciCmerT-1615994809478)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200828144811787.png)]
在创建表的时候,添加自动增长
create table user(
id int primary key auto_increment,
name varchar(20)
);
删除自动增长
alter table user modify id int;
在创建表之后添加自动增长
alter table user modify id int auto_increment
外键约束
想要施加外键约束,必须至少有两张表
foreign key
格式一: constraint 外键名字 foreign key (字段名) references 表名 (字段名);
格式二: foreign key(字段名) references 表名(字段名);---没有外键名,但是系统会自动生成一个外键名
创建表的时候添加外键约束
create table user(
id int primary key auto_increment,
name varchar(20)
);
create table computer(
id int primary key auto_increment,
name varchar(20),
uid int,
constraint c_u_key foreign key (uid) references (id)
);
删除外键约束
alter table computer drop foreign key c_u_key;
在创建表之后,添加外键约束
方法一
alter table computer add constraint c_u_key foreign key (uid) references user(id)
方法二
alter table computer add foreign key (uid) references user(id)
当使用方式二时,向查看自动生成的外键,可以通过 show create table computer的方式进行查看
表间关系
一对一、一对多(多对一)、多对多
*一对多
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E7exTlEk-1615994809480)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200828162342619.png)]
create table user(
id int primary key auto_increment,
name varchar(10)
);
create table computer(
id int primary key auto_increment,
name varchar(20),
uid int,
constraint c_u_key foreign key (uid) references (id)
);
*多对多
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ywMHeCzS-1615994809482)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200828170654434.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R5luLYrk-1615994809484)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200828170716961.png)]
create table teacher(
id int primary key auto_increment,
name varchar(20)
);
create table student(
id int primary key auto_increment,
name varchar(20)
);
create table t_s(
tid int,
sid int,
primary key(tid,sid),//联合主键
constraint t_s_t foreign key (tid) references teacher (id),
constraint t_s_s foreign key (sid) references student (id)
);
作业:有三张表,用户表(user, id, name),分类表(category, id, name),商品表(goods, id, name)
create table user( (
id int(11) primary key auto_increment,
` name varchar(10),
);
create table category( (
id int(11) primary key auto_increment,
name varchar(10),
);
create table goods(
id int primary key auto_increment,
name varchar(10),
cid int,
foreign key (cid) references category(id)
);
create table g_u(
uid int,
gid int,
primary key(uid,gid),
foreign key (uid) references user (id),
foreign key (gid) references goods (id)
);
多表查询
练习案例:
create table a(
id int,
name varchar(20)
);
insert into a values(1,'aaa');
insert into a values(2,'bbb');
insert into a values(3,'ccc');
create table b(
id int,
name varchar(20)
);
insert into b values(1,'张三');
insert into b values(2,'李四');
insert into b values(3,'王五');
笛卡尔积查询
注意:是最没有用没有意义的擦好像,但是是最所有查询中最原始的查询、
select * from a,b;
+------+------+------+------+
| id | name | id | name |
+------+------+------+------+
| 1 | aaa | 1 | 张三 |
| 2 | bbb | 1 | 张三 |
| 3 | ccc | 1 | 张三 |
| 1 | aaa | 2 | 李四 |
| 2 | bbb | 2 | 李四 |
| 3 | ccc | 2 | 李四 |
| 1 | aaa | 3 | 王五 |
| 2 | bbb | 3 | 王五 |
| 3 | ccc | 3 | 王五 |
+------+------+------+------+
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bch3e6c9-1615994809485)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200831095611898.png)]
内连接
关键字: inner join
注意:隐式内连接和显示内连接查询出的结果相同只是格式不一样
隐式内连接
select * from a,b where a.id=b.id;
+------+------+------+------+
| id | name | id | name |
+------+------+------+------+
| 1 | aaa | 1 | 张三 |
| 2 | bbb | 2 | 李四 |
| 3 | ccc | 3 | 王五 |
+------+------+------+------+
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p21da4fK-1615994809486)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200831102006593.png)]
显示内连接
//会将a,b两表对应条件的记录都查询出来,并且此连接和隐式查询出的结果相同
select * from a inner join b on a.id=b.id;
+------+------+------+------+
| id | name | id | name |
+------+------+------+------+
| 1 | aaa | 1 | 张三 |
| 2 | bbb | 2 | 李四 |
| 3 | ccc | 3 | 王五 |
+------+------+------+------+
外连接
左外连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wel1C57J-1615994809487)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200831103310831.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zdEYEzyT-1615994809487)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200831104815720.png)]
//左边的全部会显示完,如果右边的没有对应的会以null进行填充
select * from a left join b on a.id=b.id;
+------+------+------+------+
| id | name | id | name |
+------+------+------+------+
| 1 | aaa | 1 | 张三 |
| 2 | bbb | 2 | 李四 |
| 3 | ccc | 3 | 王五 |
| 4 | ddd | NULL | NULL |
+------+------+------+------+
右外连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WQhakOeP-1615994809488)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200831104025622.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xGcUjnrT-1615994809489)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200831104830603.png)]
//会查询出右边所有的,如果左边没有对应id的会以null进行填充
select * from a right join b on a.id=b.id;
+------+------+------+------+
| id | name | id | name |
+------+------+------+------+
| 1 | aaa | 1 | 张三 |
| 2 | bbb | 2 | 李四 |
| 3 | ccc | 3 | 王五 |
| NULL | NULL | 5 | 赵钱 |
+------+------+------+------+
子查询
嵌套查询: 指一条sql语句中嵌套一条sql语句
第一种情况:子查询中的里面的查询语句,得到的结果是一个数据
需求:查询出a表中(id值等于(b表中名字为李四的id值))的名字
查询出a表中的名字
select name from a
查询出a表中的名字,前提是id值等于...
select name from a where id=??
b表中名字为李四的id值
select id from b where name = '李四'
综合:
select name from a where a.id=(select id from b where name = '李四');
第二种情况:子查询中里面的查询语句,得到的结果是多个数据
需求:查询出a表中id值等于b表中名字为张三,李四的id值的名字有哪些
select * from a where id in((select id from b where name ='张三' or name='李四'));
select * from a where id in((select id from b where name in('张三','李四')));
第三种情况:子查询中的查询语句,得到的结果是一张表(表的重构)
需求:查询a表中的数据,按照id值倒序排列(两种写法)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j4lI0xgW-1615994809490)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200831114542722.png)]
写法一:
select * from a order by id desc;
写法二(白痴写法):
select * from (select * from a order by id desc) as newA;
select * from (select * from a order by id desc) newA;
事务
就是要做的事,增加、删除、查询、修改都算事务
简单的事一步作为,复杂的事多步做完
转账案例:
create table user(
id int primary key auto_increment,
name varchar(20),
money int
);
需求:向表中添加一条记录
insert into user values(null,'tom',1000);
修改表中钱数
update user set money=900 where name='tom';
删除名字为tom的记录
delete from user where name='tom';
以上这几条是一步可以完成,所以根本不需要事务
需求: tom给jerry转账,100块
update user set money=900 where name='tom';
update user set money=1100 where name='jerry';
加了事务之后就可以实现,这两步要么都执行成功,要么都失败
使用事务的原因
为什么一条sql语句可以做的不需要事务管理,而2条以上的需要事务管理:(保证数据的安全性)
因为一条sql能做的事,要么成功要么失败
而多条sql一起做的事,有三种可能,要么成功,要么失败,要么做了一半
而在数据库的操作中不允许事情做一半,只规定必须成功或者失败,加了事务之后就可以实现,要么都执行成功,要么都失败
事务的操作
方式一:
开启事务
start transaction;
提交事务
commit;
注意:只有commit提交事务后才算真正的结束,如果没有提交数据依然会还原
回滚事务:写入此条语句后事务同样也会结束
rollback;
方式二 修改系统默认的提交方式,默认是自动提交我们需要修改为手动提交:
查看当前默认提交方式:
select @@autocommit;
修改当前默认提交方式
set @@autocommit=0 或 1;
方式一:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iqPSsspD-1615994809491)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200831165512726.png)]
方式二:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ndUtm9uk-1615994809491)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200831165448364.png)]
事务的四大特性(面试)
原子性:不能再分隔了已经最小了,也就是说事务最小了,不能分割了,如果把事务分隔了就会出现问题
持久性:通过事务进行操作后的数据,是永久保存的
隔离性:事务与事务之间没有任何的关系,互相不干涉,但是在实际开发中经常会打破隔离性,当多个事务共同操作同一张表的数据时,就会打破隔离性,一旦打破隔离性就会出现严重的问题
一致性:事务操作之前,和事务操作之后的数据是保持不变的
打破隔离性引发出的安全问题:
脏读:事务A读到了事务B没有提交的数据
不可重复读:事务A读到了事务B已经提交后的修改的数据
虚读(幻读):事务A读到了事务B,已经提交后的添加的数据
解决事务安全的方式:(事务的隔离级别)
read uncommitted:如果设置了这一隔离级别,脏读,不可重复读,虚读一个不能解决
read committed:能解决脏读,,不能解决不可重复读、虚读 Oracle默认
repeatable read 能解决脏读不可重复读,不能解决虚读 MySQL默认
串行化:serializable :都能解决
查看当前隔离级别:
select @@tx_isolation
mysql8.0: select @@transaction_isolation;
修改隔离级别
set global transaction isolation level ??
JDBC:Java数据库连接
作用:让我们使用Java代码操作数据库
本质:一些接口或抽象类(一套规则,一套规范)
产生原因:制定规则
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sWIJw50Y-1615994809493)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200901105934611.png)]
入门案例:
//0、导入jar包
原因:因为JDBC只提供了一些接口和抽象类,而具体的实现是由数据库厂商完成的
//1、注册驱动
//这句话是奖com.mysql.cj.jdbc包下的Driver类的字节码文件从本地磁盘加载到方法区中
Class.forName("com.mysql.cj.jdbc.Driver");
//注册驱动原始代码
DriverManager.registerDriver(new Driver())//告知我们用的数据库是哪个数据库
//更新写法的原因:
//1、原始写法太过依赖jar包
//2、在Driver.class中的静态代码块也有以上这句,这样原始写法会造成二次注册
//所以我们用第一种写法将类加载到方法区就可以只执行一次注册驱动。
//2、获取数据库连接对象
注意:如果是IP和端口是localhost:3306可以省略不写
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/db2","root","123456");
//第一个参数,"jdbc:mysql://localhost:3306/db2" : (jdbc:mysql:)协议,(localhost)IP,(3306)端口,(db2)数据库名
//第二个参数 数据库用户名
//第三个参数 数据库密码
//一共方式有3种可以获取连接
第一种:
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/db2","root","123456");
第二种
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/db2?user=root&password=123456");
第三种:
Properties p=new Properties();
p.setProperty("user","root");
p.setProperty("password","123456");
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/db2",p);
//3、获取执行sql语句的对象
Statement stat=conn.createStatement();
//实际写法: PrepareStatement ps=conn.prepareStatement(sql);
//4、执行sql语句
ResultSet rs=stat.executeQuery();
//执行sql语句的方法3种
int executeUpdate(sql);//针对数据库的增删改操作
返回值为int:代表实际影响的行数
ResultSet executeQuery(sql);//针对数据库的查询操作
返回值为ResultSet(结果集):代表一个结果集类型
boolean execute(sql);//针对数据库的增删改查,一般不会使用,jdbc的底层会使用
返回值为boolean :指如果执行的sql是增删改返回false;如果实现的是查询,返回true;
//5、获取结果集,解析结果集
while(rs.next()){//判断结果集有没有数据
int id=rs.getInt("id");//
String name=rs.getString("name");
}
//或者可以
while(rs.next()){
Object id = rs.getObject(1);
Object name = rs.getObject(2);
}
//6、释放资源
rs.close();
stat.close();
conn.close();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n3aLahNz-1615994809494)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200901114529587.png)]
实现 JDBCUtils的封装
package com.jdbc.utils;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
public class JdbcUtils {
private static String driver;
private static String user;
private static String password;
private static String url;
/**
* 使用静态代码块保证读取文件与注册驱动只执行一次
*/
static {
Properties p = new Properties();
try {
// 读取配置文件
p.load(new FileInputStream("jdbc.properties"));
driver = p.getProperty("driver");
url = p.getProperty("url");
user = p.getProperty("user");
password = p.getProperty("password");
// 注册驱动
Class.forName(driver);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 获取数据库连接
*
* @return
*/
public static Connection getConnection() {
Connection conn = null;
try {
// 获取数据库连接
conn = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
/**
* 增删改释放资源
*
* @param conn
* @param stat
*/
public static void close(Connection conn, Statement stat) {
close(conn, stat, null);
}
/**
* 查询释放资源
*
* @param conn
* @param stat
* @param rs
*/
public static void close(Connection conn, Statement stat, ResultSet rs) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
练习:模拟用户登陆功能
public class Demo8 {
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
boolean b = isLogin(username, password);
if(b) {
System.out.println("登录成功~");
} else {
System.out.println("登录失败~");
}
}
private static boolean isLogin(String username, String password) throws Exception {
//获取数据库连接对象
Connection conn = JdbcUtils.getConnection();
//获取执行sql语句的对象
Statement stat = conn.createStatement();
//执行sql语句
String sql = "select * from user where username = '" + username + "' and password = '" + password + "'";
ResultSet rs = stat.executeQuery(sql);
return rs.next();
}
}
这么写完是不完整的,有缺陷的,如果我们键盘录入的数据是这样的,也会登录成功。
请输入用户名:
asdlfaldsfjkdsflasdfkjdslf
请输入密码:
asdfjdalfj’ or ‘1’ = '1
为什么会登陆成功?
select * from user where username = 'asfasdfladlsjfad' and password = 'a;fjasdkl' or '1' = '1';
以上问题称为Sql注入问题:
解决方法:使用PreparedStatement,因为它采用占位符会对sql语句进行预编译从而避免了SQL注入问题
/获取执行sql语句的对象
PreparedStatement ps = conn.prepareStatement("select * from user where username = ? and password = ?");
//给占位符赋值
ps.setString(1, username);//给第一个占位符,赋值username
ps.setString(2, password);//给第二个占位符,赋值password
//执行sql语句
ResultSet rs = ps.executeQuery();
JDBC针对事务的处理
Connection正对事务有三个方法
//相当于,mysql命令中的
//修改当前默认提交方式
//set @@autocommit=0 或 1; 其中0为手动提交1为自动提交
//true为自动提交
//false为手动提交
conn.setAutoCommit(false);
//提交事务
conn.commit();
//回滚事务
conn.rollback();
JDBC数据库连接池
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kpamtaik-1615994809495)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201113101903749.png)]
定义:
提供一种池子思想,就是提供一个容器,用来装东西的,如果是线程池就用来存储线程对象,如果是连接池,就是存储Connection对象(数据库连接对象)
先提前创建好一些连接对象,存储到池子中,如果以后用的话,直接从池子里面取,用完再放回池子中。
作用:
使用池子之前:如果我们要使用Connection对象的话,要现创建,然后再使用,用完要释放资源,如果获取次数过多的话,创建的次数多,释放的次数也多,浪费资源。
使用池子之后:不需要创建,直接从池子中拿出来使用,用完再放回去,不需要释放资源,节约资源。
自定义数据库连接池
package com.jdbc.pool;
import java.sql.Connection;
import java.util.LinkedList;
import com.jdbc.utils.JdbcUtils;
/**
* 自定义数据库连接池
* @author 18146
*
*/
public class SimpleConnectionPool {
/**
* 创建一个池子,取出和放回相当于增删,所以选中链表做完池子容器
* @return
*/
private static LinkedList<Connection> pool=new LinkedList<Connection>();
//给池子装几个连接对象,使用静态代码块,只创建一次Connection对象,
static{
try{
for (int i = 0; i < 5; i++) {
pool.add(JdbcUtils.getConnection());
}
}catch(Exception e){
System.out.println("数据库连接池初始化失败");
}
}
//获取数据库连接对象方法
//普通同步方法锁对象是this
//静态方式锁对象是类.class
public synchronized static Connection getConnection(){
//从池子中将对象拿出来,而不应该创建
//此方法会返回删除的值
if(!pool.isEmpty()){
return pool.removeFirst();
}
//主动抛出异常
throw new RuntimeException("连接池内没有Connection对象了");
}
//归还数据库连接对象方法
public static void release(Connection conn){
//归还,将conn放入到池子中
pool.addFirst(conn);
}
}
别人写好的数据库连接池
JDBC给我们提供一个数据库连接池的接口,叫做DataSource
里面提供了1个重要的方法:
getConnection();//获取数据库连接对象
Connection接口中的方法:
close();//归还数据库连接池对象
注意:如果这个数据库连接对象,是通过DriverManager.getConnection()获取到的,那调用close方法就是释放的意思,
如果这个数据库连接对象,是通过从数据库连接池中获取到的,那调用close方法就是归还的意思。
DataSource dataSource = 连接池对象;//返回的子类对象是谁,就看使用的是哪个第三方提供的数据库连接池
常见的数据库连接池
c3p0:
由于是第三方提供的,所以要导入jar包
使用步骤:
方式一(使用配置文件):
1、导入jar包
c3p0-0.9.5.2.jar
mchange-commons-java-0.2.12.jar
2、将配置文件拷贝到src目录下
3、创建数据库连接池对象
DataSource dataSource=new ComboPooledDataSource();
4、获取数据库连接对象
Connection conn=dataSource.getConnection();
5、归还连接
conn.close();
方式二:(不使用配置文件)
1、导入jar包
c3p0-0.9.5.2.jar
mchange-commons-java-0.2.12.jar
2、创建数据库连接池对象
ComboPooledDataSource dataSource=new ComboPooledDataSource();
3、给4大参数设置值
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/db2?serverTimezone=CTT");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setUser("root");
dataSource.setPassword("123456");
4、获取数据库连接对象
Connection conn=dataSource.getConnection();
5、归还资源
conn.close();
Druid:
由于是第三方提供的,所以要导入jar包
使用步骤:
方式一(使用配置文件):
1、导入jar包
2、将配置文件拷贝到src目录下
3、创建Properties对象
Properties p=new Properties();
p.load(new FileInputStream("src/druid.properties"));
4、创建数据库连接池对象
DataSource dataSource=DruidDataSourceFactory.createDataSource(p);
5、获取数据库连接对象
Connection conn=dataSource.getConnection();
6、归还连接
conn.close();
封装Druid数据库连接池的工具类
package com.jdbc.pool;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
/**
* 创建数据库连接池工具类
* @author 18146
*
*/
public class DruidJDBCUtils {
private static DataSource datasource=null;
static{
Properties p=new Properties();
try {
p.load(new FileInputStream("src/druid.properties"));
datasource=DruidDataSourceFactory.createDataSource(p);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//获取数据库连接池对象
public static DataSource getDataSource(){
return datasource;
}
//获取数据库连接对象
public static Connection getConnection() throws SQLException{
return datasource.getConnection();
}
//提供归还连接的方法
public static void Close(Connection conn,ResultSet rs,PreparedStatement ps){
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
方式二(不使用配置文件):
方式一(使用配置文件):
1、导入jar包
2、创建数据库连接池对象
DruidDataSource dataSource=new DruidDataSource();
3、给四大参数设置值
dataSource.setUrl("jdbc:mysql://localhost:3306/db2?serverTimezone=CTT");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("123456");
4、获取数据库连接对象
Connection conn=dataSource.getConnection();
5、归还连接
conn.close();
单元测试
在要测试的方法上面加上注解@Test就可以进行测试,进行运行
@Test
@Before //在方法执行前执行
@Afte //在方法执行后进行执行
JDBC的工具类
JdbcTemplate,是spring框架针对JDBC的简单封装,一个简单的JDBC小框架,工具类。提供了JdbcTemplate对象,对JDBC进行简化开发。
JdbcTemplate的使用步骤
1、导入相关的jar
五个相关jar包
2、创建JdbcTemlate对象
JdbcTemplate jt=new JdbcTemplate(数据库连接池对象);
3、调用方法,执行sql语句
int i = jt.update("insert into user values(?,?),1,'tom'");
JdbcTemplate对象的创建方式
方式一:
JdbcTemplate jt=new JdbcTemplate(数据库连接池对象);
如果数据库连接池是c3p0
JdbcTemplate jt=new JdbcTemplate(new ComboPooledDataSource());
如果数据库连接池是druid
Properties p=new Properties();
p.load("src/druid.properties");
JdbcTemplate jt=new JdbcTemplate(DruidDataSourceFactory.createDataSource(p))
方式二:
JdbcTemplate jt=new JdbcTemplate();
jt.setDataSource(数据库连接池对象);
调用方法,执行sql语句
update();主要针对数据的增删改操作
//主要针对数据的增删改操作
update();
//添加:
jt.update("insert into user values(?,?)",1,"tom")
//注意:因为是可变长度参数,所以还可以传一个Object数组进去
public int update(String sql,Object ... arr){
}
//修改:
jt.update("update user set name= ? where id=?","张三",id=4);
//删除:
jt.update("delete from user where id=?",1);
//查询
query();主要针对数据的查询操作
List<User> list=jt.query("select * from user where id=?", new BeanPropertyRowMapper<User>(User.class),2);
queryForObject();主要针对数据的查询操作
作用:
查询一条记录:
User user = jt.queryForObject("select * from user where id = ?", new BeanPropertyRowMapper<User>(User.class), 1);
其中1是指前面?
查询一个数据
String name = jt.queryForObject("select name from user where id=?", String.class,2);
int count=jt.queryForObject("select count(*) from user ",int.class);
枚举
定义:就是特殊的单例,因为枚举就是多个单例,就是一个类,有且只有几个该类的对象,但不能过多,一般为2~5个对象。
自定义枚举类:
package com.enums;
public class EnumDemo {
public static void main(String[] args) {
Week one=Week.one;
one.show();
Week tow=Week.Tow;
tow.show();
Week three=Week.Three;
three.show();
}
}
/**
* 自定义枚举方式一
* @author 18146
*
*/
class Week{
public static final Week one=new Week();
public static final Week Tow=new Week();
public static final Week Three=new Week();
private Week(){
}
}
/**
* 自定义枚举方式二
* @author 18146
*
*/
class Week{、
private String name;
public static final Week one=new Week("星期一");
public static final Week Tow=new Week("星期二");
public static final Week Three=new Week("星期三");
private Week(String name){
this.name=name;
}
}
/**
* 自定义枚举方式三
* @author 18146
*
*/
abstract class Week{
private String name;
public static final Week one=new Week("星期一"){
@Override
void show() {
// TODO Auto-generated method stub
System.out.println("星期一");
}
};
public static final Week Tow=new Week("星期二"){
@Override
void show() {
// TODO Auto-generated method stub
System.out.println("星期二");
}
};
public static final Week Three=new Week("星期三"){
@Override
void show() {
// TODO Auto-generated method stub
System.out.println("星期三");
}
};
private Week(String name){
this.name=name;
}
abstract void show();
}
jdk中自带的枚举类写法
package com.enums;
public class EnumSystemDemo {
public static void main(String[] args) {
// System.out.println(Week.one);
System.out.println(Week.one.getName());
System.out.println(Week.tow.getName());
System.out.println(Week.three.getName());
}
}
/**
* 方式一
* @author 18146
*
*/
enum Week{
one,tow,three;
}
/**
* 方式二
* @author 18146
*/
enum Week{
one("星期一"),tow("星期二"),three("星期三");
private String name;
private Week(String name){
this.name=name;
}
public String getName() {
return name;
}
}
/**
* 方式三
* @author 18146
*
*/
enum Week{
one("星期一") {
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("星期一");
}
},tow("星期二") {
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("星期二");
}
},three("星期三") {
@Override
public void show() {
// TODO Auto-generated method stub
System.out.println("星期三");
}
};
private String name;
private Week(String name){
this.name=name;
}
public abstract void show();
}
使用枚举类需要注意:
1、任何枚举类都要由enum来定义
2、每个枚举类都默认继承了Enum抽象类
3、所有枚举类都有默认的私有的构造方法,就算我们没有写也是自带private构造器
4、枚举项必须放在第一行
5、枚举项与枚举项之间要用逗号隔开,最后一个枚举项后面加分号,但是如果最后一个枚举项后面没有内容,可以不写。
6、枚举类中可以有抽象方法但是一旦有抽象方法枚举项必须实现
7、枚举类中可以放在switch语句语句中当选项
枚举类中常见的方法有哪些
//System.out.println(mon.toString());
//System.out.println(mon.ordinal());
//System.out.println(mon.name());
/*int i = mon.compareTo(Week.TUE);
System.out.println(i);*/
/*Week aaa = Week.valueOf(Week.class, "WOD");
System.out.println(aaa);*/
/*Week[] weeks = Week.values();
for (Week week : weeks) {
System.out.println(week);
}*/
泛型
定义:参数化类型,就像传递参数一样
格式:
<E>
<E,T>
优势:提高了代码的可读性
避免了强制类型转化
自定义泛型类
/**
* 自定义泛型
* @author 18146
*
*/
public class GenericityDemo {
public static void main(String[] args) {
// A a=new A();
// a.show(1);
// a.show("aaa");
// a.show(true);
A<String> a=new A<>();
a.show("aaa");
}
}
/**
* 自定义泛型类
* @author 18146
*
* @param <E>
*/
class A<E>{
public void show(E e){
System.out.println(e);
}
}
泛型类是在什么时候明确的具体的数据类型:在创建对象的时候,就明确了泛型的具体数据类型
自定义泛型方法:将泛型定义在方法的声明上,
package com.genericity;
/**
* 自定义泛型方法
* @author 18146
*
*/
public class GenericityFunction {
public static void main(String[] args) {
B b = new B();
b.show(1);
b.show("13123");
}
}
class B{
/**
* 一旦定义了泛型方法,相当于什么都可以传,不能限制只能传的参数类型
* @param e
*/
public <E> void show (E e) {
System.out.println(e);
}
}
泛型方法是在什么时候明确数据类型的:在调用方法的时候明确数据类型的
注意:只能放在修饰符和返回值类型之间
面试题:泛型就是Object吗?
答:不是意义不同
public void show (E e){//虽然可以传递任何引用数据类型的参数但是传的什么类型,E就是什么类型,是确定的
}
public void show(Object o){//而Object就是固定了Object类,如果需要转为其他类型我们是需要向下转型的
}
自定义泛型接口:
package com.genericity;
/**
* 自定义泛型接口
* @author 18146
*
*/
public class GenericityInterface {
public static void main(String[] args) {
InterImpl<String> inImpl=new InterImpl<>();
inImpl.show("abc");
//inImpl.show(1);
}
}
interface Inter<E>{
public void show(E e);
}
/**
* 自定义泛型接口
* @author 18146
*
* @param <E>
*/
class InterImpl<E> implements Inter<E>{
@Override
public void show(E e) {
System.out.println(e);
}
}
泛型接口在上面时候明确的数据类型:在创建子类对象的时候明确了数据类型
泛型通配符:?号
例子:
List<?> list=new ArrayList<Integer>();
泛型通配符上限
//本身或者其子类
<? extends Number>
List<? extends Number> list1=new ArrayList<Integer>();
List<? extends Number> list2=new ArrayList<Short>();
错误的: //List<? extends Number> list3=new ArrayList<Object>();
泛型通配符下限
只能是本身或者其父类
<? super Number>
// 错误的 List<? super Number> list3=new ArrayList<Integer>();
List<? super Number> list4=new ArrayList<Object>();
类加载器
定义:用来加载,class文件的东西就被称为类加载器
作用:可以将本地磁盘上的字节码文件加载到JVM方法区,生成字节码文件对象
类加载器的分类:
引导类加载器(AppClassLoader):负责加载的区域是jdk中的jre中的lib中的rt.jar(核心类库)
扩展类加载器(ExtClassLoader):负责加载的区域是jdk中的jre中lib中ext中的资源文件
系统类加载器:负责加载的区域是classpath路径下的资源文件,默认情况下是项目bin目录下的
注意:classpath路径不是固定的,是可以自己设置的
类加载器的分层:
最上层:引导类加载器
中层:扩展类加载器
最下层:系统类加载器
类的加载顺序是什么:
类加载器,有一种委托机制。
系统类加载器:任何类一开始都是由系统类加载器来加载,也就是说,最下层来加载的,但是由于类加载器具有委托机制,所有它不会马上区自己加载,而是委托给上一层类加载器,即扩展类加载器,如果被扩展类加载器加载到了,就会进入内存,如果没有加载到,就自己再去加载,如果它自己也没有加载到,就会报异常ClassNotFoundException();
扩展类加载器:如果轮到扩展类加载器来加载,由于类加载器有委托机制所以会交给上一层来进行加载,即引导类加载器,如果引导类加载器加载到了就会进入内存,如果没有加载到,再由自己来加载,如果自己也没有加载到,就会返回给系统类加载器来进行加载
引导类加载器:如果轮到引导类加载器来加载的话,由于它没有上一层,所以它自己来进行加载,如果加载到了就进入内存,如果没有加载到就会返回给扩展类加载器来进行加载。
class Demo{
public static void main(String[] args){
String s=new String();//jdk提供的类
Person p=new Person();//自己定义的类
}
}
String 类是由谁来加载的?
引导类加载器来加载的
Person 类是有谁来进行加载的?
系统类加载器来加载的
引导类加载器、扩展类加载器、系统类加载器又是由谁来加载的呢?
扩展类加载器和系统类加载器由于也是类,也是jdk中的提供的类,所以由引导类加载器进行加载
引导类加载器没有谁来加载,,因为它不是一个类,是JVM的一部分由C语言编写的
类加载器相关的API
如何获取类加载器对象
1、先获取类的字节码文件对象
2、通过字节码文件对象获取类加载器对象
3、获取类加载器的上一层类加载器
package com.classloader;
/**
* 获取类加载器对象
* @author 18146
*
*/
public class ClassLoaderDemo {
public static void main(String[] args) {
//获取到字节码文件对象
Class person = Person.class;
//获取到类加载器对象
ClassLoader classLoader=person.getClassLoader();
System.out.println(classLoader);
//获取父 级类加载器对象,就是获取类加载器的上一层类加载器对象
ClassLoader classLoader2=classLoader.getParent();
System.out.println(classLoader2);
}
}
class Person{
}
使用类加载器读取配置文件
原始方式
在src下,有一个jdbc.properties
Properties p=new Properties();
p.load(new FileInputStream("src/jdbc.properties"));
类加载器的方式
方式一:
//获取类的字节码文件对象
Class clazz=Demo2.class;
//获取类加载器对象
ClassLoader classLoader=clazz.getClassLoader();
//使用类加载器读取配置文件
InputStream is=classLoader.getResourceAsStream("jdbc.properties");
Properties p=new Properties();
p.load(is);
System.out.println(p.get("password"));
//可以缩写为
Properties p=new Properties(); p.load(Demo2.class.getClassLoader().getResourceAsStream("jdbc.properties"));
System.out.println(p.get("driver"));
方式二:
//获取字节码文件对象
Class clazz=Demo2.class;
//获取类加载器对象
ClassLoader classLoader=clazz.getClassLoader();
//使用类加载器对象读取配置文件
//URL统一资源定位符
//注意不能路径有中文用这一方式
URL url=classLoader.getResource("jdbc.properties");
String path=url.getPath();
System.out.println(path);
FileInputStream fis=new FileInputStream(path);
Properties p=new Properties();
p.load(fis);
System.out.println(p.get("driver"));
注意:使用类加载器的方式读取配置文件,默认的根目录是相对于classpath,即项目下bin目录里的
有哪些方式可以将字节码文件加载到方法区中
例子:拿Person类为例
1、创建对象
Person p=new Person();
2、类名.方法名
Person.eat();
3、创建子类对象
class Person{}
class Student extedns Person{}
Student stu=new Student();
4、类名.class
Person.class
5、Class.forName()
Class.forName("com.domain.Person");
6、对象.getClass()
new Person().getClass();
反射
正定义:1、先创建对象
2、调用属性或方法
对象.属性
对象.方法
反定义:1、先创建属性和方法
2、然后将属性和方法赋给某一个对象
属性.对象
方法.对象
反射相关的API
1、获取字节码文件对象(方法区中的.class文件对象)
a、类名.class
Class clazz=Pig.class;
b、对象名.getClass();
Pig pig=new Pig();
Class clazz2=pig.getClass();
c、Class.forName(全限定类名(包名+类名))
Class clazz3=Class.forName("com.reflex.Pig");
备注:可以通过字节码文件对象创建对象
clazz.newInstance();
推荐使用第三种,耦合性小
2、获取构造方法对象(constractor)
获取构造方法目的为了创建对象
public Constructor<T> getConstructor(Class<?>... parameterTypes):获取一个公共的构造方法对象
参数:构造方法的参数的字节码文件类型
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取一个构造方法对象
public Constructor<?>[] getConstructors():获取所有的公共的构造方法对象
public Constructor<?>[] getDeclaredConstructors():获取所有的构造方法对象
Constructor类中方法:
newInstance():创建对象的
setAccessible(true):取消检查访问(暴力反射)
3、获取成员变量对象(Field)
获取成员变量对象,是为了给成员变量赋值
public Field getField(String name):获取公共的成员变量对象
public Field[] getFields():获取所有的公共的成员变量对象
public Field getDeclaredField(String name):获取成员变量对象
public Field[] getDeclaredFields():获取所有的成员变量对象
Field类中的方法:
set(对象,该字段赋的值);给指定对象的字段赋值
setAccessible(true):取消检查访问
getName();获取字段名
4、获取成员方法对象(Method)
获取成员方法是为了调用
public Method getMethod(String name,Class<?>... parameterTypes)
public Method[] getMethods()
public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
public Method[] getDeclaredMethods()
第二个参数是方法的方法的参数类型
Method类中的方法:
Object returnValue = 方法对象.invoke(对象,参数);如果参数没有可以不写
returnValue为该方法的返回值
练习:
1.使用反射向一个泛型为String类型的集合里存储Integer类型数据
//我们可以通过在执行的时候添加进行就不受限制
ArrayList<String> list = new ArrayList<String>();
Class clazz=list.getClass();
list.getClass()
Method method=clazz.getMethod("add", Object.class);
method.invoke(list, 1);
for (Object string : list) {
System.out.println(string);
}
2.写一个框架,可以帮助我们创建任意类的对象,并且执行其中任意方法
//可以通过配置文件来实现创建任意类的对象,与执行其方法
Properties p=new Properties();
p.load(new ReflexTest().getClass().getClassLoader().getResourceAsStream("reflex.properties"));
//获取配置文件中指定类的字节码文件对象
Class clazz=Class.forName(p.getProperty("className"));
//获取指定的字节码的指定方法
Method method= clazz.getMethod(p.getProperty("MethodName"));
//执行该方法
method.invoke(clazz.newInstance());
配置文件如下
className=com.reflex.Teacher
MethodName=eat
注解
定义:用来解释说明的
作用:
注释:用来解释说明,是给我们程序员看的
注解:用来解释说明,是给程序看的
1、编写文档:通过代码里表示的注解生成文档【生成文档doc文档】
2、代码分析:通过代码里标识的注解对代码进行分析【使用反射】
3、编译检查:通过代码里标识的注解让编译器实现基本的编译【Override】
注解的使用
将注解放到类,方法,成员变量上面即可
@注解名字
public void show(){}
@注解名字
int i;
@注解名字
class xxx{}
public void show(@注解名字){}
注解的分类:
预定义注解
Java本身写好的:
@Override:用来检测是否是方法的重写的注解
@Deprecated:用来标记方法是否已经过时
@SuppressWarnings;yo用来压制警告的注解
注意:一般情况下我们会在@SuppressWarnings中的参数,可以加all这是压制所有的警告
自定义注解
定义格式
元注解
public @interface 注解名{
}
注解的本质
public interface com.annotation.MyAnnotation extends java.lang.annotation.Annotation {
}
通过反编译:javap ,了解到注解的本质的接口,默认是继承了Annotation接口的接口。
注解的属性:
由于注解的本质就是接口,所以在接口可以定义抽象方法,但是在接口中叫抽象方法,在注解中就叫属性
在注解中,属性的返回值类型不是随便都可以写的,只可以写以下几种数据类型:
基本数据类型
String
注解
枚举
还有以上几种数据类型的数组类型
注解中属性的定义与使用:
package com.annotation;
/**
* 注解中的抽象方法称为属性,使用上也是和属性一样在使用时也是就直接进行赋值
* 可以理解为长得像方法的属性
* 支持基本数据类型、String、注解、枚举、前面类型的数组
* @author 18146
*
*/
public @interface MyAnnotation1 {
int aaa();
String bbb();
MyEnum ccc();
MyAnnotation ddd();
String[] eee();
}
package com.annotation;
@MyAnnotation1(aaa=1,bbb="tom",ccc=MyEnum.E1,ddd=@MyAnnotation,eee={"afd","cde"})
public class Demo {
public static void main(String[] args) {
}
}
我们在使用注解的属性的时候需要注意的问题:
1、如果一个注解中有多个属性的时候,我们需要给全部的属性都赋值。
2、我们可以在注解中对属性设置默认值使用default关键字如果设置了默认值,那我们在使用注解的时候就可以不为这一属性进行赋值
3、在注解中有个属性名叫value,如果一个注解中只有一个属性且属性名为value,我们在使用注解时,赋值可以不写这一属性名,直接进行赋值
元注解
定义:用来标注注解的注解就是元注解
有哪些元注解:
@Target:作用:约束它所标注的注解所能放的位置
ElementType.TYPE:如果Target注解的值为TYPE则说明被Target注解所标注的注解只能放在类名上面
ElementType.FIELD:如果Target注解的值为FIELD则说明被Target注解所标注的注解只能放在成员变量上面
ElementType.METHOD:如果Target注解的值为METHOD则说明被Target注解所标注的注解只能放在成员方法上面
@Retention:作用:限制被改元注解标注的注解的生存时长
RetentionPolicy.SOURCE:如果该注解的值为这个,则该注解的生存时长值保留在编译前时期,编译后会自动丢弃
RetentionPolicy.CLASS:如果该注解的值为这个,则该注解的生存时长值保留到编译后成为.class文件中,但是不进入内存,即不进入运行状态,JVM会忽略它
RetentionPolicy.RUNTIME:如果该注解的值为这个,则该注解的生存时长值保留到编译后成为.class文件中,并且进入内存,即进入运行状态,JVM也会进行处理
@Documented: 表示被该元注解标注的注解可以存在于帮助文档中
@Innerited:表示被改元注解所标注的注解可以被子类继承
注解的解析
就是指使用反射技术,获取注解的属性值。
注意:如果想要获取到注解中属性的值,一定要在注解中加上@Retention 而且要设置为RetentionPolicy.RUNTIME
XML
定义:可扩展标记语言
可扩展:就是标签中的元素名,可以自己随意写的
标记语言:由一对尖括号括起来的,即<内容>,这就称为标记,标签。代码都是由标签组成的,就称为标记语言
作用:用来当做配置文件
xml的配置文件和properties的配置文件的区别:
什么时候用properties配置文件:
如果配置的是单项数据,使用properties
如:driver=com.mysql.cj.jdbc.Driver
什么时候用xml配置文件:
如果配置的是多项数据,使用xml
<databases>
<database id="mysql">
<driver>...</driver>
<url>....</url>
<user>....</user>
<password>....</password>
</database>
<database id="oracle">
<driver>....</driver>
<url>....</url>
<user>....</user>
<password>....</password>
</database>
</databases>
xml的语法:
1、xml文件的后缀名必须为".xml"
2、xml文件中的第一行一定是文档声明
<?xml version = '1.0' encoding='utf-8'?>
version:版本号
encoding:告诉浏览器打开该xml文件所使用的编码格式
3、xml文件中有且只有一个根标签
4、标签中可以定义属性,在给属性赋值的时候,值要用引号括起来,单引号双引号均可
5、标签名区分大小写
6、标签名的嵌套
有头有尾
自闭和
正确嵌套格式
<aaa>
<bbb>
</bbb>
</aaa>
7、标签名的命名规则:
(1)可以由数字,字母,一些符号来组成
(2)开头不能是数字和标点符号
(3)标签名中不能有空格
(4)标签名不能为xml或者XML
8、CDATA区(当参数中的值含有<>时可以使用)
<![CDATA[内容]]>
<name>
<!--<aaa>jerry-->
<![CDATA[
<aaa>jerry
]]>
</name>
xml的入门案例
<?xml version='1.0' encoding='utf-8'?>
<students>
<student id="9527">
<name>tom</name>
<age>18</age>
</student>
<student>
<name>jerry</name>
<age>19</age>
</student>
</students>
xml的约束:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VF0GZNMk-1615994809497)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20200914113848062.png)]
用来限制我们所写的东西
xml文件是配置文件,是给框架使用的,而框架呢,又是半成品软件,即里面一部分代码已经写好了,我们需要写没有写好的地方
dtd约束
schema约束
上述两个约束的区别:
1、schema约束比dtd约束难
2、schema约束比dtd约束好
推荐使用schema约束
约束文档:不需要自己写,我们要做的是通过查看约束文档,知道怎么写xml
dtd约束文档:
1、创建一个xml文件
2、写xml文档声明
3、引入约束文档
方式一:内部引入(不推荐使用)
<!DOCTYPE note [
<!ELEMENT note (to,from,heading,body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>
]>
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend</body>
</note>
方式二:外部引入
<?xml version="1.0"?>
<!DOCTYPE note SYSTEM "note.dtd">
<note>
<to>Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</note>
4、对照约束文档写xml文件
schema约束文档(本质:也是一个xml):
1、创建一个xml文档
2、写xml文档声明
3、引入约束文档
(1)找到根标签
(2)找到默认的名称空间
xmlns="targetNameSpace的内容"
(3)引入约束文档
schemaLocation="默认名称空间的值 + 约束文件的名称"
(4)引入schemaLocation所在的名称空间
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
(5)在schemaLocation前面加上xsi:
xsi:schemaLocation="默认名称空间的值 + 约束文件的名称"
4、对照约束文档写xml文件
解析xml文件:
要将xml文件加载进内存,我们要获取配置文件中的数据,进行各种操作
解析xml的方式(两种方式):
DOM解析:文档对象模型
就是指先将xml文件一次性的加载进内存中,在内存中形成一个树状结构 (DOM树)
优点:我们可以通过DOM方式的解析对xml文件中的数据进行增删改查
缺点:如果树太大了,非常占内存空间
SAX解析:简单应用程序接口
就是指基于事件处理的,逐行扫描,逐行加载
优点:逐行扫描,读取一行,加载一行,然后加载完就扔了,不占用内存空间
缺点:执行过程不可逆,不能对数据进行增删改操作,只能进行查询操作,不能回头
常见的解析器:
JAXP:是SUN公司提供的解析xml的解析器,支持sax和dom两种方式,是JavaEE的一种
DOM4J:是开源组织提供的xml的解析器,用得比较多,综合了dom和sax的解析方式用来解析xml
Jsoup:用来解析html的解析器,用来做爬虫小程序的,但是由于xml和html的结构一样,所以也能解析xml
DOM4J解析:
需要导入相关的jar包
//创建解析器对象
SAXReader saxReader=new SAXReader();
//加载xml文件,进内存
Document document=saxReader.read(new File(Demo01.class.getClassLoader().getResource("students.xml").getPath()));
System.out.println(document);
相关API:
SAXReader:
Document read(File file);
Document:
Element getRootElement();//获取根标签对象
xpath:
List<Node> selectNodes(String xpath);//根据xpath表达式获取指定的标签对象们
Node selectSingleNode(String xpath);//根据xpath表达式获取指定的标签对象
Element:
List<Element> elements();//获取所有的子标签
String getName();//获取元素的名字
String getText();//获取标签内的文本内容
Element element(String name);//根据标签名获取指定第一个标签对象
String attributeValue(String name);//根据属性名获取属性值
Node:
String getName();//获取节点的名字
String getText();//获取节点的文本
//创建解析器对象
SAXReader saxReader=new SAXReader();
//加载xml文件,进内存
Document document=saxReader.read(new File(Demo01.class.getClassLoader().getResource("students.xml").getPath()));
// System.out.println(document);
Element rootElement=document.getRootElement();
System.out.println("根标签:"+rootElement.getName());
//获取所有字标签
List<Element> e=rootElement.elements();
for (Element element : e) {
System.out.println("二层标签"+element.getName());
String str=element.attributeValue("id");
System.out.println("id值"+str);
List<Element> es=element.elements();
for (Element element2 : es) {
System.out.println("三层标签"+element2.getName()+"值"+element2.getText());
}
}
JSOUP解析:
导入相关的jar包
相关的API
static Document parse(File file, String charset);//获取Document对象,第一个参数是配置文件的file类型,第二个参数是字符集
Document:
Element getElementById(String id);//根据id属性值获取标签对象
Elements getElementsByTag(String name);//根据标签名获取标签对象们
Elements getElementsByAttribute(String name);//根据属性名获取标签对象们
Elements getElementsByAttributeValue(String key , String value);//根据指定的属性名和属性值获取标签对象们
Element:
String text();//获取标签中的文本内容
String html();//获取标签中的包含标签的文本内容
String attr(String key);//根据属性名获取属性值
提供了2种更便捷的方式来解析xml
1.选择器
Document:
select("选择器");
document.select("#a9527"); //id选择器,去找的是id属性
document.select(".aaa"); //类选择器,去找的是class属性
document.select("name"); //元素选择器,去找的是标签名
document.select("name[class]"); //属性选择器,去找的是有name标签中具备class属性的
document.select("name[class='bbb']"); //属性选择器,去找的是name标签中具备class属性,并且属性值为bbb
2.xpath
//创建JXDocument
JXDocument jxDocument = new JXDocument(document);
List<JXNode> selN(xpath表达式);//根据xpath表达式获取JX节点对象们
JXNode selNOne(xpath表达式);//根据xpath表达式获取一个JX节点对象
/**
* 更简便的方法1、选择器
*/
Elements elements2=document.select("student");
System.out.println(elements2.text());
/**
* 2、XPath
*/
JXDocument jxDocument=new JXDocument(document);
List<JXNode> jxNode=jxDocument.selN("//student");
for (JXNode jxNode2 : jxNode) {
System.out.println(jxNode2.getElement().text());
}
JXNode jxNode2= jxDocument.selNOne("//name[@class='1']");
System.out.println(jxNode2.getElement().text());
myBatis
定义:是一个基于Java的持久层框架
使用Java代码操作数据库:
最原始状态:jdbc
升级版:JdbcTemplate 只能算是jdbc的工具类
升级版:Mybatis 一个框架
什么是框架:
半成品软件,里面提供 了一些代码,剩下的部分我们自己来实现。
基于ORM(对象关系映射)思想的一个框架
MyBatis的入门案例:
步骤:
1、导入jar包
mybatis-3.4.6.jar
2、创建dao包下的接口(UserDao)
public interface UserDao {
public void findAll();
}
3、创建MyBatis核心配置文件(xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- environments可以配置多个环境,default属性使用指定使用哪一个环境 -->
<environments default="mysql">
<!--环境的详细配置 -->
<environment id="mysql">
<!-- 配置事务管理器,type属性是JDBC,说明事务的管理底层使用的是JDBC事务 Connection setAutoCommit(false)
开启事务 commit():提交事务 rollback();回滚事务 -->
<transactionManager type="JDBC" />
<!--配置数据源 type属性有三个: POOLED:说明使用了数据库连接池,数据库连接池,是mybatis框架底层自己实现的一个连接池
UNPOOLED:说明没有使用数据库连接池 JNDI:JavaEE技术之一,但是一般我们不会使用 -->
<dataSource type="POOLED">
<!--配置四大参数 -->
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://127.0.0.1:3306/db2?serverTimezone=CTT" />
<property name="username" value="root" />
<property name="password" value="123456" />
</dataSource>
</environment>
</environments>
<!-- 配置关联映射文件 -->
<mappers>
<!-- 配置具体的某一个配置文件,resource属性是关联映射文件路径,注意文件夹之间需要使用斜线 -->
<mapper resource="com/dao/UserMapper.xml" />
</mappers>
</configuration>
4、创建MyBatis映射文件(xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dao.UserDao">
<select id="findAll" resultType="com.domain.User">
select * from user
</select>
</mapper>
5、进行测试
(1)获取流对象读取核心配置文件
(2)创建sqlSession工厂的构造者对象
(3)获取SqlSession工厂对象
(4)获取SqlSession对象
(5)获取UserDao代理对象
(6)执行方法
(7)释放资源
//1、获取流对象
InputStream inputStream=Resources.getResourceAsStream("sqlMapConfig.xml");
//2、创建SqlSession工厂的构造者对象
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//3、创建SqlSession工厂对象
SqlSessionFactory sessionFactory=builder.build(inputStream);
//4、获取SqlSession对象
SqlSession session=sessionFactory.openSession();
//5、获取UserDao的代理类对象
UserDao userDao=session.getMapper(UserDao.class);
//6、执行方法
List<User> list=userDao.findAll();
for (User user : list) {
System.out.println(user);
}
构建者模式:是属于创建型的设计模式。用来创建对象
package com.test;
/**
* 构建者设计模式,是建设型的设计模式(创建型)
* @author 18146
*
*/
public class BuilderDemo {
public static void main(String[] args) {
Computer computer=new ComputerBuilder().build();
System.out.println(computer);
}
}
/**
* 鼠标
* @author 18146
*
*/
class Mouse{
}
/**
* 显示器
* @author 18146
*
*/
class Display{
}
class CPU{
}
class Computer{
private Mouse mouse;
private Display display;
private CPU cpu;
public Computer(Mouse mouse, Display display, CPU cpu) {
super();
this.mouse = mouse;
this.display = display;
this.cpu = cpu;
}
public Computer() {
super();
}
}
/**
* 就是通过一个构建者的类将我们需要的对象构建出,我们通过调用这一类的方法获取对象
* @author 18146
*
*/
class ComputerBuilder{
public Computer build(){
Mouse mouse=new Mouse();
Display display=new Display();
CPU cpu=new CPU();
Computer computer=new Computer(mouse,display,cpu);
return computer;
}
}
工厂设计模式:创造型设计模式
package com.test;
//工厂设计模式,相当于和工厂一样可以大量产生对象
public class FactoryDemo {
public static void main(String[] args) throws Exception {
Animal animal=new AnimalFactory().getInstance(Cat.class);
animal.eat();
}
}
abstract class Animal{
public abstract void eat();
}
class Dog extends Animal{
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("吃骨头");
}
}
class Cat extends Animal{
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("吃鱼");
}
}
class Pig extends Animal{
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("吃肉");
}
}
class AnimalFactory{
public Animal getInstance(Class animal) throws Exception{
Object obj=animal.newInstance();
if(obj instanceof Dog){
return (Dog)obj;
}else if(obj instanceof Cat)
{
return (Cat)obj;
}else if(obj instanceof Pig){
return (Pig)obj;
}
return (Animal)obj;
}
}
使用Mybatis来实现对数据库的增删改查
注意:在执行增删改操作时需要提交事务,因为Mybatis底层是开启了事务,但是查询不影响所以不用提交,但是增删改操作需要对事务进行提交
<!-- 配置查询操作,id属性是指定UserDao种哪一个具体方法的
resultType属性是指定封装的返回值类型
parameterType属性是指定参数的类型
-->
<!--配置映射文件的详细信息,namespace属性是配置这个映射的操作UserDao的各种方法 -->
查询操作:
UserDao:
public List<User> findAll();
UserMapper.xml:
<select id="findAll" resultType="com.domain.User">
select * from user;
</select>
添加操作
UserDao:
public void add(User user);
UserMapper.xml:
<insert id="add" parameterType="com.domain.User">
insert into user values(null,#{name});
</insert>
修改操作
UserDao:
public void update(User user);
UserMapper.xml:
<update id="update" parameterType="com.domain.User">
update user set name=#{name} where id=#{id}
</update>
删除操作
UserDao:
public void delete(int id);
UserMapper.xml:
<delete id="delete" parameterType="java.lang.Integer">
delete from user where id=#{id}
</delete>
根据id查询一条记录
UserDao:
public User findById(int id);
UserMapper.xml:
<select id="findById" resultType="com.domain.User" parameterType="java.lang.Integer">
select * from user where id=#{id};
</select>
查找总记录数
UserDao:
public int count();
UserMapper.xml
<select id="count" resultType="int">
select count(*) from user;
</select>
根据姓名查找数据
//根据姓名模糊查询
public List<User> findByName(String name);
格式一:
<select id="findByName" parameterType="string" resultType="com.domain.User">
select * from user where name like #{name}
</select>
Test:
List<User> users=userDao.findByName("%o%");
格式二:
<select id="findByName" parameterType="string" resultType="com.domain.User">
select * from user where name like '%${value}%'
</select>
Test:
List<User> users=userDao.findByName("o");
注意:方式二只能写value,不能用别的替代,一般写第一种
分页查询
方式一:
UserDao:
public List<User> findByPage(Map<String,Integer> map);
UserMapper.xml:
<select id="findByPage" parameterType="map" resultType="com.domain.User">
select * from user limit #{index},#{page}
</select>
Test:
Map<String,Integer> map=new HashMap<>();
map.put("index", 0);
map.put("page", 2);
List<User> list=userDao.findByPage(map);
方式二:
UserDao:
public List<User> findByPage(@Param("index") int index,@Param("page") int page);
UserMapper.xml:
<select id="findByPage" parameterType="map" resultType="com.domain.User">
select * from user limit #{index},#{page}
</select>
Test:
List<User> list=userDao.findByPage(0, 2);
根据Person查询一条记录?
Person:
private User user;
public User getUser() {
return user;
}
UserDao:
public User findByPerson(Person p);
UserMapper.xml
<select id="findByPerson" parameterType="com.domain.Person" resultType="com.domain.User">
select * from user where id=#{user.id}
</select>
Test:
User user=new User();
user.setId(2);
Person person=new Person();
person.setUser(user);
User user1=userDao.findByPerson(person);
添加一条记录(添加之后返回一个id值)
UserDao:
//添加一条记录
public void add2(User user);
UserMapper:
<!-- 添加一条记录,获取新添加的记录的id -->
<insert id="add2" parameterType="com.domain.User">
<!--
keyColumn:表中的字段名
keyProperty:类中的属性名
order:
BEFORE:在之前执行
AFTER:在之后执行
-->
<selectKey keyColumn="id" keyProperty="id" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
insert into user values(null, #{name})
</insert>
UserTest:
@Test
public void add2() {
User user = new User();
user.setName("李四");
System.out.println(user);
userDao.add2(user);
System.out.println(user);
}
Mybatis 核心配置文件的相关配置
1、别名的配置
<!-- 配置别名 在SqlMapConfig中配置了之后就可以在Mapper.xml中进行别名的使用-->
<typeAliases>
<!-- 一般不常用这一方式
<typeAlias type="com.domain.User" alias="user"/>
<typeAlias type="com.domain.Person" alias="person"/>
-->
<package name="com.domain"/>
</typeAliases>
2、动态配置连接数据库的四大参数
jdbc.properties:
jdbc.username=root
jdbc.password=123456
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/db2?serverTimezone=CTT
SqlMapConfig.xml:
<!--加载properties中的配置文件-->
<dataSource type="POOLED">
<!--配置四大参数 -->
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
3、配置核心配置文件关联映射文件
<!-- 配置关联映射文件 -->
<mappers>
<!-- 配置具体的某一个配置文件,resource属性是关联映射文件路径,注意文件夹之间需要使用斜线 -->
<mapper resource="com/dao/UserMapper.xml" />
<!-- 注意使用package这一方式是.java文件所映射的.xml文件需要文件名相同
<package name="com.dao"/>-->
</mappers>
Mybatis 映射配置文件(XXXMapper.xml)的相关配置
配置resultMap标签
<!--当实体类中的属性名和数据库中的字段名不匹配时,我们需要配置resultMap来实现resultMap的使用-->
<resultMap type="user" id="userMap">
<id column="id" property="uid"/>
<result column="name" property="uname"/>
</resultMap>
<select id="findAll" resultMap="userMap">
select * from user;
</select>
2、MyBatis执行动态sql语句
根据条件进行查询
if
where
UserMappper.xml
<select id="findByCondition" resultType="user" parameterType="user">
select * from user
<where>
<if test="id!=0">
id=#{id}
</if>
<!-- 在拼接框架底层进行sql语句拼接时候,如果前面id没有则会自动判断后面有无and,如果有会自动删除掉再拼接
(where标签自动去掉满足条件的第一个and)-->
<if test="name!=null">
and name=#{name}
</if>
</where>
</select>
UserDao:
public List<User> findByCondition(User user);
Test:
User user=new User();
user.setName("李四");
List<User> list = userDao.findByCondition(user);
for (User user2 : list) {
System.out.println(user2);
}
foreach:
根据多个id查询
<select id="findByCollection" resultType="user" parameterType="int[]">
select * from user
<where>
<!-- collection:指定输入对象中集合属性,
item: 每个遍历生成对象,
open:开始遍历时拼接串,
close: 结束遍历是拼接的串,
separator: 遍历的两个对象中需要拼接的串
index:index指 定一个名字,用于表示在迭代过程中,每次迭代到的位置,
-->
<foreach collection="array" open="id in (" close=")" item="id" separator="," index="">
#{id}
</foreach>
</where>
</select>
使用Mybatis进行多表查询:
Mybatis操作数据库进行多表查询表间关系:
2种关系:一对一、一对多
查询一个wife的信息与对应husband的信息:
方式一:
WIfeMapper.xml:
<resultMap type="wife" id="WifeHusbandMap">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="hid" property="hid"/>
<!-- 多表关联查询
property:指定哪个属性是联合的对象
column:关联的相同值的字段
javaType 指定这个属性对象的类型
-->
<association property="husband" column="hid" javaType="husband">
<id column="hid" property="id"/>
<result column="hname" property="name"/>
</association>
</resultMap>
<select id="findById" resultMap="WifeHusbandMap" parameterType="int">
select w.id,w.name,w.hid,h.id as hid,h.name as hname from husband as h, wife as w
<where>
w.hid=h.id and w.id=#{id}
</where>
</select>
方式二:
WifeMapper.xml:
<resultMap type="wife" id="WifeHusbandMap" >
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="hid" property="hid"/>
<!-- 这句相当于以column中的值,带入select中的方法里去查询得到的
property:指定哪个属性是联合的对象
column:指定将哪一列的值传给这个方法
javaType 指定这个属性对象的类型
select:表明当前属性是调用select指定的方法查出的结果
-->
<association property="husband" column="hid" javaType="husband" select="com.dao.HusbandDao.findById"/>
</resultMap>
<select id="findById" resultMap="WifeHusbandMap" parameterType="int">
select * from wife where id=#{id}
</select>
HusbandMapper.xml:
<select id="findById" resultType="husband" parameterType="int">
select * from husband where id=#{id}
</select>
1.例子:user表和computer表(一对多的关系,多对一的关系)
create table user (
id int primary key auto_increment,
name varchar(20)
);
create table computer (
id int primary key auto_increment,
name varchar(20),
uid int,
foreign key (uid) references user(id)
);
insert into user values(null, 'tom');
insert into user values(null, 'jerry');
insert into computer values(null, 'apple', 1);
insert into computer values(null, 'dell', 1);
insert into computer values(null, 'thinkpad', 2);
需求:查询所有的电脑,顺带把这些电脑的主人也查询出来(一对一查询)
ComputerDao:
public List<Computer> findAll();
ComputerMapper.xml
//方式一:
<resultMap type="computer" id="computerUserMap">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="uid" property="uid" />
多表关联查询 property:指定哪个属性是联合的对象 column:关联的相同值的字段 javaType 指定这个属性对象的类型
<association property="user" column="uid" javaType="user">
<id column="uid" property="id"/>
<result column="uname" property="name"/>
</association>
</resultMap>
<select id="findAll" resultMap="computerUserMap">
select c.*,u.id uid,u.name uname from computer c,user u where c.uid=u.id
</select>
方式二:
<resultMap type="computer" id="computerUserMap">
<id column="id" property="id" />
<result column="name" property="name" />
<result column="uid" property="uid" />
多表关联查询 property:指定哪个属性是联合的对象 column:关联的相同值的字段 javaType 指定这个属性对象的类型
<association property="user" column="uid" javaType="user" select="com.dao.UserDao.findById" />
</resultMap>
<select id="findAll" resultMap="computerUserMap">
select * from computer
</select>
需求:查询所有的用户,顺带把用户所属的电脑也查询出来(一对多查询)
ComputerDao:
public List<User> findUserAll();
ComputerMapper.xml
方式一:
<resultMap type="user" id="userComputerMap">
<id column="id" property="id" />
<result column="name" property="name" />
<!--
而oftype指定的是映射到list集合属性中domain的类型(本例指的是computer类型)-->
<collection property="computers" column="id" ofType="computer">
<id column="cid" property="id" />
<result column="cname" property="name" />
<result column="uid" property="uid" />
</collection>
</resultMap>
<select id="findUserAll" resultMap="userComputerMap">
select u.*,c.id cid,c.name cname,c.uid from user u,computer c where u.id=c.uid
</select>
方式二:
<resultMap type="user" id="userComputerMap">
<id column="id" property="id" />
<result column="name" property="name" />
<!--
而oftype指定的是映射到list集合属性中domain的类型(本例指的是computer类型)-->
<collection property="computers" column="id" ofType="computer" select="com.dao.ComputerDao.findByUid">
</collection>
</resultMap>
<select id="findUserAll" resultMap="userComputerMap">
select * from user;
</select>
MyBatis的加载方式?
在我们MyBatis中有两种加载方式,一个是立即加载,一个是延迟加载。
而默认情况下是立即加载。
什么是立即加载?
在多表查询中,当我们查询出一张表中的数据的时候,也会立即查询出另外一张表中的数据,就称为立即加载。
什么是延迟加载?
在多表查询中,当我们没有使用另一张表中的数据的时候,是不需要查询另一张表的,只会查询当前表中的数据,称为延迟加载。
什么时候需要使用立即加载?什么时候需要使用延迟加载?
就用user和computer这两张表举例子:
需求:我们想要查询computer的时候,顺带查询它所属的用户是谁的时候(一对一)
立即加载:(推荐)
select * from computer
select * from user;
延迟加载:
用user的时候,会执行select * from user;
没有用user的时候,不会执行select * from user;
需求:我们想要查询user的时候,顺带查询它所有的电脑有哪些的时候(一对多)
立即加载:
select * from user;
select * from computer
延迟加载:(推荐)
用computer的时候,会执行多次select * from computer;
如果没有用computer的时候,就不会执行这些
延迟加载的配置?
sqlMapConfig.xml:
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
MyBatis的缓存?
MyBatis有一级缓存和二级缓存
注意:
一级缓存默认是开启的
二级缓存是没有开启,如果想开启,需要进行配置
为什么要有缓存,有什么用吗?
有了缓存,就会减少读取数据库的次数,从而提高效率
一级缓存?
第一次发起查询sql查询用户id为1的用户,先去找缓存中是否有id为1的用户,如果没有,再去数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。
如果sqlsession执行了commit操作(插入,更新,删除),会清空sqlsession中的一级缓存,避免脏读
第二次发起查询id为1的用户,缓存中如果找到了,直接从缓存中获取用户信息
mybatis默认支持一级缓存。 是SqlSession级别的缓存
验证方式:
@Test
public void findById() {
User user = userDao.findById(1);
System.out.println(user);
User user2 = userDao.findById(1);
System.out.println(user2);
}
结论:两个对象的地址值是一样的,而且sql语句只查询了一次,证明了一级缓存的存在。
我们可以自己清空缓存,怎么清空呢?
执行sqlSession的增删改,clearCache(),commit(),close(),flushCache="true"都可以清空缓存
二级缓存?
sqlsession1查询用户id为1的信息,查询到之后,会将查询数据存储到二级缓存中。
如果sqlsession3去执行相同mapper下sql,执行commit提交,会清空该mapper下的二级缓存区域的数据
sqlsession2查询用户id为1的信息, 去缓存找 是否存在缓存,如果存在直接从缓存中取数据
是SqlSessionFacotry级别的缓存,也可以说是mapper映射文件级别的缓存
如果是同一个sqlSession工厂获取到不同的sqlSession对象的话,这些sqlSession对象,如果操作的是同一个映射文件的话,
就可以使用二级缓存。
开启二级缓存的配置?
sqlMapConfig.xml:
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
XXXMapper.xml:
<cache/>
useCache="true"
验证方式:
@Test
public void findById() {
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserDao userDao1 = sqlSession1.getMapper(UserDao.class);
User user1 = userDao1.findById(1);
System.out.println(user1);
sqlSession1.close();
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserDao userDao2 = sqlSession2.getMapper(UserDao.class);
User user2 = userDao2.findById(1);
System.out.println(user2);
}
问题?
1.为什么两个对象的地址值不一样呢?
反序列化回来之后,相当于重新创建了对象,地址值肯定不一样
2.为什么要序列化?
因为二级缓存是将对象通过序列化流存储到磁盘文件,所以需要类实现序列化接口。
3.为什么一定要关闭第一个sqlSession1的时候,二级缓存才生效?
这个就要说到二级缓存和一级缓存的整个执行流程。
1.先去二级缓存中查找有没有,有就拿来用,没有继续查找
2.如果二级缓存没有,就会一级缓存中查找,如果找到就拿来用,如果没找到就继续查找
3.如果二级缓存和一级缓存都没有,就去数据库中查找
4.从数据库中查找到的数据,会先存储到一级缓存中,不会存储到二级缓存中
5.如果调用了sqlSession.close(),就会将数据从一级缓存扔到二级缓存,并且一级缓存中的数据进行清空
Javaweb
定义:使用Java语言开发基于互联网的项目
软件架构的分类:
1、C/S(Client/Server):客户端/服务器端
是指在用户的本地有一个客户端软件,在远程的服务那边有一个服务器端
QQ.迅雷等
优点:功能强大,用户体验比较好
缺点:占内存,开发,安装,部署,维护,都比较麻烦,成本大
2、B/S(Browser/Server):浏览器/服务器端
是指在用户这边打开浏览器,访问各种路径(URL),向网站的后台服务器发送请求,进行响应就可以看到不同的页面效果了
京东、淘宝
优点:开发,安装,部署,维护,都比较简单,成本小
缺点:1、当项目比较大的时候,用户体验不太好
2、对硬件要求较高
资源的分类
资源定义:其实指的就是文件
静态资源:就是指资源中的数据,是写死的
比如说有一个文本文件,在里面写了很多内容,然后关闭,过来很久再次将次文件打开,依然没有发生任何改变
例如:图片,文本文件,视频、音频,html,css,JavaScript
注意:通过浏览器访问服务器端的静态资源,服务器端会直接将静态资源响应给浏览器,然后再浏览器端进行显示
动态资源:就是指资源中的数据,是活的,是可以发生改变的,可以通过访问数据库对数据进行改变
比如说我们在不同时段开发淘宝页面,会发现,里面的图片等资源都和之前打开的不太相同,而且登陆我的账号会显示欢迎我登陆
有哪些动态资源呢:
jsp,servlet等
注意:通过浏览器访问服务器中的动态资源的时候,动态资源会先转换为静态资源,然后响应给浏览器,然后浏览器进行展示
Html(静态资源)
定义:超文本标记语言,
超文本:比普通文本更强大的文本就是超文本
普通文本:顶多可以设置字体样式、大小等
超文本:可以出来设置上面的级别功能之外,这个文本还可以嵌套 图片、音频、等各种东西
标记语言:代码由标签组成的,就称为标记语言
之前学习的xml也是标记语言,因为里面的代码也是由标签组成的
作用:html是用来写页面的
静态资源三剑客:
html:主要是用来写页面
css:主要用来给页面进行美化
javascript:主要是用来让页面展示动态效果的,增加用户的体验感
注意:xml也是一个标记语言,但是它的功能用来做配置文件的,和html的组用完全不一样
xml是可扩展标记语言,可扩展是相对于html来说的,而html是超文本标记语言,内部的东西都是固定好的
html的语法格式
1、html文件的后缀名,以.htm或者.htm为结尾
2、html文件里面是由一些标签组成的,和之前学习的xml一样的
标签组成:
有头有尾:
<a></a>
自闭和
<a/>
3、html的标签的嵌套
正确:<a><b></b></a>
错误:<a><b></a></b>
4、html的标签名不区分大小写,只不过建议小写
注意:在xml中是严格区分大小写的
5、html中也有属性,是由键值对组成,要由引号括起来,单引号和双引号都可以
<div name="张三"></div>
<div name='张三'></div>
html入门案例
<html>
<head></head>
<title>第一个案例</title>
<body>
Hello World!
<body>
</html>
html的常用标签
1、结构体标签
<!DOCTYPE html>:说明html5中文文档的类型是html文档
html标签:html的根标签
head标签:头标签
里面写的一般都是针对页面的属性的设置相关的标签
title标签:页面的标题标签
meta标签:是给页面的属性进行设置的
常用属性:charset="utf-8":告诉浏览器使用什么编码打开该html文件
body标签:体标签
里面写页面展示的内容
2、文本标签
注释:<!---->
标题标签:<h数字>文本内容</h数字>
数字范围1~6随着数字的增大而字体会变小
如果数字没有在1~6的范围内,就相当于没有加标签
段落标签:<p>文字内容</p>
换行标签:<br/>
水平线标签:<hr/>
属性:
color:设置水平线的颜色
size:设置水平线的粗细,按照数字的增加逐渐变粗
width:设置水平线的宽度,按照所设置的像素大小来决定
align:设置水平线的对齐位置,值一般有 left(靠左)、right(靠右)、center(居中)
加粗标签:<b>文本内容</b>
斜体标签;<i>文本内容</i>
字体标签:<font>文本内容</font>
属性:color:设置字体的颜色
size:设置字体大小范围1~7,如果超过范围和7是一样的效果
face:设置字体样式
color属性的设置:
3种方式:
1)color="颜色的单词"
2)color=”#FFFFFF“
3)style="color:rgb(100,255,255)"范围0~255
width属性的设置:
2种方式:
1)width="200" 单位:像素(px)
2)width="%50" 占整个框的百分比是多少
居中标签
<center>居中标签</center> :但是这一标签已经过时了
3、图片标签
<img/>
属性:
src:设置图片的路径
注意:可以设置绝对路径,也可以设置相对路径
相对路径:./:代表当前目录下
../:代表上一级目录
width:设置图片宽度
height:设置图片高度
align:设置图片位置,left、right
alt:设置图片的文字说明
4、列表标签
1)有符号有序号的
<ol type="1">
<li></li>
<li></li>
</ol>
type属性可以为:1、A、I、a、i
2)有符号无序号
<ul type="circle">
<li></li>
<li></li>
</ul>
type属性可以为:circle(空心圆)、disc(实心圆)、square(方形)
3、无符号无序号
<dl>
<dt>热门游戏</dt>
<dd>王者荣耀</dd>
<dd>刺激战场</dd>
</dl>
5、超链接标签
<a href="#">点我</a>
属性:
href:设置超链接点击后的路径
target:设置跳转路径页面打开方式
两个值:_self:在自己页面打开
_blank:在新的一个页面打开
name:设置锚点名称
href="#锚点名"
回到顶部设置:<a href="#top">回到顶部</a>
6、块级标签
<span></span>
块的大小是根据中间的文本内容来决定的,如果浏览器的一行的块的大小超过了当前行的范围会自动 换行
<div></div>
块的大小默认占一整行
div+css对页面进行布局
7、语义化标签
作用:提高可读性
<header></header>
<footer></footer>
上述两个标签其实就是div标签
8、特殊字符
一个空格:
<:<
>:>
*9、表格标签
表格标签:<table></table>
属性:border="1" 设置边框的大小
*cellspacing:设置外边线和内边线的距离
*cellpadding:设置内边线和内容之间的距离
width:设置表格的宽度
height:设置表格高度
align:设置表格位置 center、left、right
bgcolor:设置表格的背景颜色
行标签:<tr></tr>
属性:align:设置表格位置 center、left、right
bgcolor:设置表格的背景颜色
列标签:<td></td>
属性:align:设置表格位置 center、left、right
bgcolor:设置表格的背景颜色
表格字段标签:<th></th> 作为表格的第一行的字段名来使用的,自带加粗功能与居中
表格标题标签:<caption></caption>
声明表格头标签:<thead></thead> 一般放<th></th>
声明表格尾标签:<tfoot></tfoot>
声明表格其他行标签:<tbody></tbody>
嵌套顺序:table->tr->td
设置属性的优先级:就近原则
合并单元格:
合并行:属性:rowspan
合并列:属性:colspan
注意:表格的嵌套、表格想要放在另一个表格中,必须放在另一个表格的td中
<table>
<tr>
<td>
<table>
<tr>
<td>
</td>
</tr>
</table>
</td>
</tr>
</table>
**4、表单标签
作用:用来采集用户输入的各种数据,用于和服务器进行数据交互
<form></form>
属性:
*action:设置表单提交的路径(表单提交到哪)
*method:设置表单的提交方式
get:默认就是get,设置后数据会在地址栏上显示出来
post(推荐):设置后数据不会再地址栏上显示
常用的表单项:
文本框标签:<input/>
属性:type:根据type不同的值,文本框的样式就会发生变化,比如说普通文本框:type="text"
密码框:type="password"
单选框:type="radio"
日期框:type="date"//一些老的浏览器不支持,如IE浏览器
邮箱文本框(里面有针对邮箱的校验):type="email"
文件框(可以选择各种文件上传):type="file"
复选框:type="checkbox
选色板:type="color"
数字框:type="number"
隐藏文本框:type="hidden"
提交按钮:type="submit"
普通按钮:type="button"
placeholder:设置文本框中的小字,一般作为提示
下拉框标签:<select></select>
下拉框列表标签(嵌套在select中):<option><option>
文本域标签: <textarea></textarea>
各个标签的属性:
*name:每个标签都可以有name属性,代表其名字
*value:文本框中的内容,就算不写出来,相当于也有value=""
点击标签名字跳转到该输入框:<lable for="需要跳转输入框的id"> </lable>
css
定义:层叠样式表(级联样式表),cascading style sheet
多个样式可以施加正在同一个元素的上面
作用:用来美化页面的
可以实现将html的代码和样式展示的代码进行分离
优点:1、功能强大
2、可以实现将html的代码和样式展示的代码进行分离
降低耦合,解耦
提高重用性
提高维护性
提高开发效率
html中css的使用(css的引入方式)
三种
1、再html标签中写上一个属性,style,这个属性的值,由一些小的键值对组成
注意:小键值对之间需要加分号,最后一个小键值对可以不加
数字后面需要加上px
2、在html的head标签中,写一个style标签,在这个标签中写上选择器,在选择器中以键值对的方式写各种属性的值。
<style>
div{
color: red;
font-size: 100px;
}
</style>
3、在html的head 标签中写link标签
<link rel="stylesheet" href="../css/aaa.css"/>
rel="stylesheet"是必须写的,不写样式显示不出来
href="",是引入一个外来的css
注意:用这一方式时在css文件中可以不用style标签,直接写选择器开始设置属性
优先级的问题:
就近原则
哪种方式距离该标签近,就是优先设置哪个样式
三种方式在什么时候需要使用到
第一种:由于样式的设置是写在标签内的,它只能给这一个标签设置样式,当我们只需要给一个元素施加样式时,可以使用第一种方式、
第二种:由于样式是写在style标签,在style标签里面写的是选择器,所以它可以给多个标签施加相同的样式的时候,我们就可以选择第二种
第三种:由于该方式,是引用了外来的css文件,说明哪个文件引入css文件,就可以使用该样式了,当我们需要给多个html文件进行样式的设置是,就可以选择第三种
推荐使用第三种和第二种
css的选择器
选择器:可以帮助我们快速定位到某一个或者某几个标签的,就称为选择器
选择器的定义格式:
选择器{
小键值对;
…
}
常见的选择器有哪些
基本选择器:
元素选择器(标签选择器):
标签名字{
小键值对;
......
}
div{
color:red;
}
id选择器
#id属性值{
小键值对;
.....
}
#aaa{
color:green;
}
类选择器
.class属性值{
小键值对;
.......
}
.aaa{
color:blue;
}
注意:在类选择器中还支持给一个class写多个值,我们可以分别给指定的class 设置样式,在写相同属性键值对时,就会以就近原则来进行判断
格式:
<div class="aa bb">
你好
</div>
<style>
.bb{
font-family: 楷体;
color: grey;
}
.aa{
color: red;
font-size: 500px;
text-align: center;
}
</style>
以上三种选择器优先级问题:id选择器>类选择器>元素选择器
高级选择器
通配符选择器:
*{
key:value;
key:value;
}
*{
color:red;
}
组合选择器:
选择器,选择器,选择器{
key:value;
key:value;
}
.aaa,#bbb{
color:greenyellow;
}
属性选择器:
格式一:
标签名[属性名]{
key:value;
key:value;
...
}
div[class]{
color:red;
}
格式二:
标签名[属性名=属性值]{
key:value;
key:value;
....
}
div[class='aaa']{
color:red;
}
包含选择器:父标签下的所有的子标签,或者子标签的子标签
父标签 子标签{
key:value;
key:value;
}
p b{
color:red
}
注意找的是p标签下所有的b标签
子元素选择器:父标签下直接属于它的子标签
父标签>子标签{
key:value;
key:value;
}
p>b{
color:red
}
注意:找的是p标签下第一层的b
伪类选择器:
标签名:link{
设置标签对象在未被访问前的样式
}
标签名:hover{
设置标签对象在鼠标悬停时的样式
}
标签名:active{
设置标签对象在被用户激活时的样式
}
标签名:visited{
设置标签对象在链接对象访问过时后的样式
}
css的属性:
字体:
font-size:设置字体大小,
font-family:设置字体的字体
color:设置字体颜色
文本:
text-align:设置文本的对齐方式
背景:
background-color:设置背景颜色
background-image:url():设置背景图片
background-repeat:设置背景图像是否平铺
尺寸:
width:设置宽度
height:设置高度
边框:
border:
border-color:设置边框颜色
border-style:设置边框显示样式
border-width:设置边框宽度
复合属性设置:如果没有加上后面的-xxx,相当于全部都设置了:border:10px solid red;
盒子模型:对页面进行布局的
margin:设置外补丁,就是外边线之间的距离
margin-top
margin-left
margin-right
margin-bottom
padding:设置内补丁,内边线的距离
padding-top
padding-left
padding-right
padding-bottom
注意事项:默认情况,如果我们设置内补丁,会影响整个盒子的大小,那我们需要设置一个属性:box-sizing:border-bok
浮动:
float:
left:左浮动
right:右浮动
清除浮动:
clear:
left:清除左浮动
right:清除右浮动
both:清除左右浮动
定位:
position
relative:相对定位
相对于自己原来的位置
top:
left:
注意:对其他的div是没有影响的,之前的位置还是空着的
absolute:绝对定位
如果父级标签没有定位,相对于父级的父级
如果父级标签有定位相对于父级
top
left
注意:对其他div是造成影响的,之前的位置不会空着了
JavaScript(js)
定义:js,是一个脚本语言,也是一门编程语言
JavaScript虽然名字里带有Java,但是其实和Java没有关系 ,只是里面的99%的语法和Java的一模一样
针对JS,每一个浏览器里面都具备JavaScript解析引擎
脚本:就是不像Java语言,需要编译,然后再运行,而脚本不需要编译,可以通过浏览器直接解析运行,浏览器看到js写的一些指令,就知道接下来需要做什么事了
js既然事一门编程语言,那就说它里面也有常量,变量,运算符、逻辑判断、数组、方法、对象
JavaScript=ECMAScript(JS的基础语法)+(BOM(浏览器对象模型):JavaScript特有的语法+DOM(文档对象模型) :JavaScript特有的语法)
作用:js可以让页面的一些元素动起来,提高了用户的体验
js指令可以控制html的元素
js的引入方式
方式一:在html标签中写一个script标签,在script写js代码
<script>
alert("在吗")
</script>
方式二:在head标签中写一个script标签,在script标签中有 src属性用于引入一个外部的js文件
<script src="..js/aaa.js">
</script>
注意:
1、script标签可以写在任意位置
2、script标签可以写多个
3、由于,浏览器解析HTML文件,事自上而下的顺序解析,所有,就算有多个script也是有先后顺序的
JavaScript语法:
注释:
单行注释://
多行注释:/* */
数据类型:
原始数据类型 :
number:
整数,小数,NaN(Not a Number)
string:字符串
boolean:true,false
null:
null,即是数据类型,也是一个值
当成对象的一个占位符来使用
undefined:
未定义,即是数据类型,也是一个值
引用数据类型:
各种对象的类型
Object
js中的变量:
注意:Java强类型语言:指在我们定义变量的时候,会在内存中开辟一块内存空间,会给内存空间定义一个数据类型,定义数据类型之后,该空间中存储的只能是该数据类型范围内的数据
而js是弱类型语言:Java强类型语言:指在我们定义变量的时候,会在内存中开辟一块内存空间,但不会指定任何数据类型,指任何数据类型都可以存在里面
var 变量名 = 数据;
var a = 12;
注意:定义变量,当没有使用var的时候相当于定义了一个全局变量
注意:alert(”内容“):在页面弹出一个对话框
document.write(”内容“):将内容写到页面上展示,内容可以是html的标签
typeof(“变量名”):得到变量的数据类型
特殊例子:
null:
a=null;
alert(a);
alert(typeof a); //之所以是Object是因为自身的bug导致的
undefined
a=undefined;
alert(a);
alert(typeof a);//类型也是underfined
运算符:
算术运算符:+,-,*,/,%
/:js中的除号和Java中的不太一样,
在Java中,整数/整数 = 整数
小数/整数=小数
在js中,数/数=正常的结果
+:
+的意义:
Java中:+号,正数,连接符(和字符串进行拼接)
在js中,多了意义:作用转换符,使用+号可以将字符串(string)转为数字(number),也可以将布尔(boolean)类型转换成数字(number)
连接符、转换符演示格式
string->number:
var i=12;
var a="123";
alert(+"123"+12);//结果135
注意:当加号没有其他意义的时候才能作为连接符,否则优先为其他意义
alert(+"abc");//结果NaN
注意:当字符串里面有字母的时候,转换为数字会得到NaN,但是数据类型还是number
boolean->number:
alert(+true+1);//结果2
alert(+false+1);//结果1
赋值运算符:
=、+=、-=、*=、/=
与Java中一样
比较运算符:
>、<、<=、>=、、=
==:是比较两个数据是否相等,在js中,==可以比较两个不同类型的数据,只要打印出来的内容一致,就是true
如:
alert("123"==123)//结果true
===:叫做全等,会先去判断两边的数据类型是否一致,如果不一致就是false,如果一致,就会判断值是否相等,如果再相等就是true
>,<:
在Java中>和<如果对字符串进行判断的话,会直接编译错误。
在js中,就可以直接对字符串进行比较,那根据字典顺序判断大小
逻辑运算符:
&&、||、!
和Java中一样,但是js中没有&、|。
三元运算符:
条件表达式?表达式1:表达式2;
和Java中一样
流程控制语句
if…else
和Java中大部分一样,但有一些区别
if(任意类型数据){
}else{
}
任意类型数据中只有6个数据的结果为false
false,0,NaN,null,undefined,""
其余全为true
switch:
和Java一样,但是有一点区别
Java中:switch各种中的表达式只能为int,char,枚举,String,byte,short这几种
在js中,switch中的表达式,可以为任意类型
while
for
do…while
这些循环和Java相同
函数(方法):
在js中方法的意义比较独特,它既是方法,也是对象,也是构造方法。
注意:对于我们来说,我们只重点学习它当作方法来使用
1、当做方法
定义格式:
function 方法的名字(参数......){
XXXXX
//return 返回值
}
方法的调用
方法名(实参);
注意事项
1、方法的返回值类型不写,参数的数据类型也不写
2、方法可以定义多个,如果一旦方法的名字相同的话,后面的同名方法会覆盖前面的同名方法
3、js中的方法调用,只关注方法的名字,不关注方法的参数类型和个数
4、当参数没有传值时,实参会为undefined
5、当方法调用时,方法内置一个数组(arguments)所有传的参数都会存入数组中,然后根据方法的参数顺序取出,如果多出的就依然还在数组中放在
另外一种格式:(匿名函数)
标签对象.事件属性=function(){
}
2、当做对象
function person() {
}
person.username = "tom"; //给person对象动态绑定一个name属性,值为tom
person.age = 18; //给person对象动态绑定一个age属性,值为18
alert(person.username);
alert(person.age);
/*
Java中的写法
public void setName(String name) {
this.name = name;
}
*/
person.setUsername = function(username) {//给person对象动态绑定一个方法,名字叫做setUseranme
this.username = username;
}
person.setAge = function(age) {//给person对象动态绑定一个方法,名字叫做setAge
this.age = age;
}
person.getUsername = function() {
return this.username;
}
person.getAge = function() {
return this.age;
}
person.setUsername("张三");
person.setAge(19);
alert(person.getUsername());
alert(person.getAge())
3、当做构造方法
function Person() {
}
Person.prototype.name = "tom";
Person.prototype.age = 18;
var person = new Person();
alert(person.name);
alert(person.age);
数组:
在js中的数组和Java中的数组有些区别,甚至更像Java中的集合
js中定义数组有3种格式:
格式一:
var arr =[1,2,3,4,5];
格式二:
var arr=new Array(数据列表)
格式三:
var arr=new Array(数组的长度)
遍历:
var arr=[1,2,2,3,4]
//普通循环
for(var i=0;i<arr.length;i++){
alert(arr[i]);
}
//for...in...循环
for(var i in arr){
alert(i);//i对应的是索引,如果中间有 没有值的数据会直接跳过
}
数组中的方法:
push():向数组末尾追加元素,一个可以,多个也可以
join();把数组中的所有元素放入一个字符串
数组中的属性:
length:获取数组的长度
其他的对象:
Date:
创建对象:
var date = new Date();
常用方法:
getTime();//获取当前时间的时间戳
toLocaleString();//获取当前时间的年月日时分秒
Math:
cell(x):天花板:向上取整
floor(x):地板:向下取整
random():获取随机0-1之间的数
获取1-100之间的随机数?
Math.random() [0, 1)
Math.random() * 100 [0, 100)
Math.floor(Math.random() * 100) [0, 99]
Math.floor(Math.random() * 100) + 1 [1, 100]
获取7-56之间的随机数?
round(x):四舍五入
RegExp:正则表达式的类
正则表达式规则:
\\ 反斜线字符
[abc] 代表a,b,c三个字符中的任意一个
[^abc] 任何字符,除了 a、b 或 c(否定)
[a-zA-Z] a 到 z 或 A 到 Z,两头的字母包括在内(范围)
. 任何字符(与行结束符可能匹配也可能不匹配)
\d 数字:[0-9]
\w 单词字符:[a-zA-Z_0-9]
^ 行的开头
$ 行的结尾
X? X,一次或一次也没有
X* X,零次或多次
X+ X,一次或多次
X{n} X,恰好 n 次
X{n,} X,至少 n 次
X{n,m} X,至少 n 次,但是不超过 m 次
正则表达式对象的创建?
方式一:
var regExp = new RegExp("正则表达式");
方式二:
var regExp = /正则表达式/;
校验方法:
test("校验的字符串");
针对手机号的校验:
1.手机号一定是11位
2.第一位肯定1
3.第二位也是固定范围(3,5,8,7,9)
4.其余的无所谓
var regExp = new RegExp("^1[35789]\\d{9}$");
var regExp = /^1[35789]\d{9}$/;
Global对象:
以下方法可以直接使用,不需要创建对象
var str=encodeURI("字符串"); 将字符串转为URI编码格式
var str=decodeURI("编码后的字符串");解析某个编码的URI
var str1=encodeURIComponent():把字符串编码为URI组件:区别能够转换一些更为特殊的字符,如/、:等
var str1=decodeURIComponent("编码后的字符串")解析某个编码的URI
eval();计算js字符串,并作为代码进行运行
parseInt():解析一个字符串并返回一个整数
isNaN();检查某个值是否是NaN
简单的DOM:
文档对象模型
1、获取标签对象
标签对象=document.getEIementById(“id属性值”)
方式一:如果script标签在目标标签(我们要获得标签对象的那个)对象的下面的时候
结果为目标标签对象
<div id="a">
你好
</div>
<script>
var div=document.getElementById("a");
alert(div);
</script>
方式二:如果script标签在目标标签(我们要获得标签对象的那个)对象的下面的时候
结果为null;由于html文件是自上而下进行解析,所以在上面的时候无法解析到下面的标签属性
解决办法:通过设置页面加载完后再执行此段js代码
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
window.onload=function (){
var div=document.getElementById("a");
alert(div);
}
</script>
</head>
<body>
<div id="a">
你好
</div>
</body>
</html>
window.οnlοad=function(){
}:意思当整个页面加载完后才会执行这里面的内容,这样就不会出现null的情况了
2、获取标签中的数据
1)获取标签对象
var div = document.getElementById(“id属性值”)
2)获取标签中的文本内容
var text=div.innerHTML;
3)修改标签中的文本内容
div.innerHTML=“约吗”;
4)追加标签中的文本内容:
div.innerHTML+=“约吗”;
5)修改标签对象的属性值:
div.style=“color:red font-size:20px;”;
3、事件处理(单机):
方式一:在标签的里面添加一个单机事件的属性
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
function aaa(){
alert("哈哈");
}
</script>
</head>
<body>
<input type="button" value="点击我" onclick="aaa()" />
</body>
</html>
方式二:使用js原生代码,先获取标签对象
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript">
window.onload=function(){
var bt=document.getElementById("a");
bt.onclick=function (){
alert("哈哈")
}
}
</script>
</head>
<body>
<input type="button" value="点击我" id="a" />
</body>
</html>
BOM:
浏览器对象模型
就是将浏览器进行拆分,分成了各个组件
BOM:Navigator:浏览器对象
Screen:显示器对象
*Window:窗口对象
*History:历史栏对象
*Location:地址栏对象
DOM:文档对象
*Window对象:窗口对象
注意:WIndow对象不需要写,可以直接使用里面的方法
弹出框的相关方法:
alert()显示带有一段消息和一个确认按钮的警告框
confirm() 显示带有一段消息以及确认按钮和取消按钮的对话框
prompt() 显示可提示用户输入的对话框
打开和关闭浏览器窗口的相关方法:
open(想要打开窗口的地址);打开一个新的浏览器窗口或查找一个已命名的窗口
窗口对象.close():关闭浏览器窗口
计时器相关方法
setInterval():按照指定的周期(以毫秒计)来调用函数或计算表达式
第一个参数:事件到了要执行的方法
第二个参数:过多长事件执行该方法(毫秒)
clearInterval():取消由setInterval()设置的计时器
参数:需要关闭的计时器对象
window.onload=function(){
var rename;
var bt=document.getElementById("bt1");
bt.onclick=function(){
rename=setInterval("hh()",3000);
}
var bt1=document.getElementById("bt2");
bt1.onclick=function(){
clearInterval(rename);
}
}
function hh(){
alert("你好");
}
setTimeout():在指定的毫秒数后调用函数或计算表达式
:过多长事件执行一次,执行完一次之后,就不会执行了
clearTimeout():取消由setTimeout()方法设置的计时器
属性:document
history
location
Location对象:
地址栏对象:
href:设置或返回完整的URL
reload();重新加载当前页面
HIstory对象
历史栏对象
length 返回浏览器历史列表中的URL数量
back() 加载history列表中的前一个URL
forward() 加载history列表中的下一个URL
go() 加载history列表中的某个具体页面
go方法中可以传递参数,如果是正数,就是下某页,如果是负数就是上某页
DOM
文档对象模型
就是指将文档给拆了,将各个组成部分,都封装成对象,使用这些对象,对文档实现增删查改等操作
DOM:
Document:树对象,文档对象
Element:元素对象,标签对象
Attribute:属性对象
Text:文本对象
Comment:注释对象
Node:节点对象
DOM分为两个部分:
HTML DOM
XML DOM
注意:这两个DOM既有共性,也有不同
Document对象:
获取标签对象的相关方法:
getElementById():根据标签id值获取标签对象
一般id属性是用来获取标签对象的
getElementsByName():赶回带有指定名称的对象集合
一般name属性是用来给后台获取数据用的
getElementsByTagName():返回带有指定标签名的对象集合
getElementsByClassName():根据标签的class属性来获取标签对象们
一般class属性是用来进行样式设置的,使用类选择器
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script>
window.onload=function(){
var tex=document.getElementsByClassName("aa");
for(var i=0;i<tex.length;i++){
alert(tex[i].innerHTML);
}
}
window.onload=function(){
var tex=document.getElementsByName("b");
for(var i=0;i<tex.length;i++){
alert(tex[i].innerHTML);
}
}
window.onload=function(){
var tex=document.getElementsByTagName("a");
for(var i=0;i<tex.length;i++){
alert(tex[i].innerHTML);
}
}
</script>
</head>
<body>
<a name="b" class="aa">哈哈</a>
<a name="b" class="aa">嘿嘿</a>
<a name="b" class="aa">喜喜</a>
</body>
</html>
和创建相关的方法:
createElement()创建元素节点
createAttribute()创建拥有指定名称的属性节点,并返回新的
createTextNode()创建文本节点
createComment()创建注释节点
注意:每创建出一个对象,都是处于游离状态
Element对象:
和属性相关的方法:
getAttribute():传递属性值,获取属性值
setAttribute():设置属性名与值
removeAttribute():移除属性
Node对象:
方法 :
appendChild():向节点的子节点列表结尾添加新的子节点
removeChild():删除(并返回)当前节点的指定子节点
属性:parentNode:获取父级的节点对象
超链接标签实现页面不刷新
<a href="#" onclick="a()">点击</a>
<a href="javascript:void(0)" onclick="a()">点击</a>
<a href="javascript:a()">点击</a>
this:谁调用就是谁的对象
js针对样式的设置:
方式一:
function aaa(){
var div=document.getElementById("a");
div.style="border:1px solid red";
div.style.backgroundColor="yellow"
}
方式二:
<style>
.aaa{
border:1px solid red;
}
</style>
function aaa(){
var div=document.getElementById("a");
div.setAttribute("class","aaa")
}
方式三:
<style>
.aaa{
border:1px solid red;
}
</style>
<script>
function aaa(){
var div=document.getElementById("a");
div.className="aaa";
}
</script>
js中的事件:
点击事件:
onclick();鼠标点击某个对象
ondbclick(); 鼠标双击某个对象
焦点事件:
属性:onblur 元素失去焦点属性
onfocus 元素获得焦点属性
方法:
onblur():一调用,获取到焦点
onfocus():一调用失去焦点
键盘相关事件:
onkeydown:某个键盘的建被按下
onkeypress:某个键盘的按键被按下并松开
onkeyup:某个键盘的键被松开
加载事件:
onload:在页面或图片加载完后执行
鼠标相关事件:
onmousedown:某个鼠标按键被按下
onmousemove:鼠标被移动
onmouseout:鼠标从某元素移开
onmouseover:鼠标移动到某元素上
onmouseup:某个鼠标按键被松开
表单相关事件:
onchange:用户改变域的内容
注意支持该事件的标签有:
onselect:文本被选定
注意支持该事件的标签有:
onsubmit:提交按钮被点击
提交表单的几种方式:
第一种:
<form action="demo1.html" method="get">
<input type="submit" value="提交"/>
</form>
第二种:可以对表单进行前端校验
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script>
function aa(){
return true;
}
</script>
</head>
<body>
<form action="demo1.html" method="get" onsubmit="return aa()">
<input type="submit" value="提交"/>
</form>
</body>
</html>
第三种
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script>
window.onload=function(){
var form=document.getElementById("a");
form.onsubmit=function (){
return true;
}
}
</script>
</head>
<body>
<form action="demo1.html" method="get" id="a">
<input type="submit" value="提交"/>
</form>
</body>
</html>
第四种
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script>
function a(){
var fo= document.getElementById("aa");
fo.submit();
}
</script>
</head>
<body>
<form action="demo1.html" method="get" id="aa">
<input type="button" value="提交" onclick="a()"/>
</form>
</body>
</html>
bootstrap
定义:一个前端框架
优点:
1、具有丰富的css样式和js插件,可以使我们做的页面更加丰富
2、支持响应式布局
Bootstrap入门案例
1、下载好bootstrap
2、创建项目时将官方提供的三个文件拷贝到i项目下
3、创建html页面时引入指定的css和js的第三方文件
响应式开发:
我们只需做一份页面,这个页面根据不同分辨率的,来自行修改布局
bootstrap的响应式是依赖于栅格系统
栅格系统:
栅格:就是指由很多个水平线和垂直线相交,会产生出很多个小格子,就称为栅格。而bootstrap的栅格系统规定,每行中,存在的格子只能是12个
栅格系统的使用:
1、定义容器,相当于之前的table标签
1)container(固定宽度):两边有留白
2)container-fluid(100%宽度)
2、定义行
样式设置:row
3、定义列
样式设置:col-尺寸-格子数
web核心
web服务器:
定义:安装了服务器软件的计算机
服务器软件:就是软件,下载安装即可。
作用:
1)接收用户请求,对用户请求进行处理,然后进行响应
2)可以部署web项目,可以通过浏览器访问的方式去访问web项目中的资源
网络编程三要素:IP(对方的计算机),端口(对方计算机中的应用程序),协议
常见的web服务器软件:
Weblogic:是Oracle公司的,是一个大型的JavaEE服务器,收费
Websphere:是IBM公司的,是一个大型的JavaEE服务器,收费
JBOSS:是一个大型的服务器,免费的
Apache:Apache软件基金会,是一个大型的服务器,免费的
*Tomcat:Apache软件基金会,免费,且是市面比较流行的web应用服务器
容器:weblogic和websphere都是支持JavaEE技术的服务器,也就是JavaEE13种技术均支持在里面使用
Tomcat仅仅支持servlet和jsp,所有也称为jsp/servlet容器,只能使用这两种
tomcat服务器:
1、下载:https://tomcat.apache.org/
2、安装:解压即可
注意:安装的目录最好在没有中午没有空格的地方
3、卸载:把文件夹删除掉即可
4、启动:
找到D:\tomcat\apache-tomcat-9.0.12\bin目录下的startup.bat文件,双击即可
闪退:
第一种:jdk环境变量没有配置好
环境变量的目的:在任何目录下都能使用命令,因为配置的是直接在path配置全路径,而tomcat找的是JAVA_HOME,所以会闪退。
解决办法:在系统变量种新建一个JAVA_HOME系统变量值为jdk的路径,在path下配置%JAVA_HOME%\bin即可
第二种:端口冲突
解决办法:在dos窗口种输入netstat -ano,查看8080端口所对应的pid (进程id)。在任务管理器中找到pid所对应的应用程序,强制关闭即可
5、关闭
正常关闭:
shutdown.bat
ctrl+c
强制关闭:
点x直接关闭
web项目
静态web项目
项目中的东西都是静态资源
动态web项目
项目中的资源可动可静
web项目的部署
部署:
就是将web项目交给服务器来管理
部署方式:
方式一:
将web项目放入tomcat中的webapps目录下启动tomcat后,在浏览器端进行访问
注意:如果项目名字改成ROOT是不写项目名的
如果端口号改成80,那访问资源的时候就不需要写端口号
方式二:
在tomcat的conf的server.xml中的Service标签下Engine标签下的host标签下,配置以下内容
<Context docBase="项目所在路径" path="/aaa"/>
docBase属性:web项目的真实路径
path属性:web项目的虚拟目录
访问资源:localhost:8080/项目虚拟目录/文件夹/资源文件名字
方式三:
在tomcat的conf中的Catalina的中locahost目录中创建一个xml文件,xml文件中写
<Context docBase="项目所在路径"/>
docBase属性:web项目的真实路径
xml文件名:web项目的虚拟目录
访问资源:localhost:8080/项目虚拟目录/文件夹/资源文件名字
注意如果配置xml文件的名字叫做ROOT,访问时是不写虚拟目录的
自己的电脑tomcat配置域名
1)在C:\Windows\System32\drivers\etc路径下hosts文件中最后一行添加
127.0.0.1 www.aaa.com
2)在tomcat文件目录下的conf中的server.xml中修改配置文件
1、在Engine标签下再添加一个Host标签
<Host name="www.aaa.com" appBase="woniu_webapps"
unpackWARs="true" autoDeploy="true"/>
3)在tomcat目录下创建文件夹woniu_webapps
将项目拷贝到该目录下
4)重启项目,这样就可以通过域名的方式进行访问,如果不想写端口号时将server.xml中的端口号8080修改为80
Servlet
Servlet定义:
运行在服务器端的小程序
可以通过浏览器访问的类
说明:我们能否通过浏览器去访问web项目中的一个类?其实是访问不了的,但是这些访问不到的类只是普通类,而有一群不普通的累,其实是可以被浏览器访问到的,这些不普通的累就称为Servlet;
区分普通类和Servlet类
servlet只是一个泛指,即所有能够被浏览器访问的类
servlet这一系列的类其实拥有一个庞大的家族,这个家族有自身的体系结构
其中有一个最顶层的接口,叫做Servlet。我们创建的普通类不属于这个家族,想要让普通类成为Servlet大家族的一份子,就实现Servlet接口即可
Servlet的入门案例
第一步:创建一个类,实现Servlet接口,并且重写接口里面的五个方法
第二部:在类里面的service方法中写测试内容
第三步:在web.xml中进行配置
<servlet>
<servlet-name>随意起的名字</servlet-name>
<servlet-class>要访问的Servlet的全限定类名</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>随意起的名字</servlet-name>
<url-pattern>servlet的虚拟路径</url-pattern>
</servlet-mapping>
第四步:开启服务器,打开浏览器,访问servlet类
http://localhost:8080/项目虚拟目录/servlet的虚拟路径
Servlet的执行流程:
1、服务器接收到了客户端的请求,解析请求的url路径,获取到servlet的虚拟路径
2、通过获取到的servlet虚拟路径,去web.xml中进行查找,去中配置文件中所有的url-pattern一一对比,知道找到一样的。
3、如果找到了就会通过servlet-name的匹配,找到servlet-class标签获取全限定类名
4、通过全限定类名由反射创建出对象,再调用其他方法即可
Servlet生命周期:
由三个方法体现,称为生命周期方法:
inti():初始化方法
service():执行方法
destroy():销毁方法
生命周期:
1、当我们第一次访问servlet的时候,会创建servlet对象,调用servlet对象的init()方法,然后调用service()方法
2、当我们再一次访问servlet的时候,就不调用init(),会调用service()方法
3、当我们正常关闭服务器时,会调用destroy方法
将创建servlet对象时间提前到tomcat开启时
在web.xml中该servlet配置servlet标签中添加如下配置
<load-on-startup>1</load-on-startup>
注意:如果值为负数,servlet对象的创建就是在第一次访问时
如果值为正数,servlet对象的创建就在开启服务器的时候进行创建
数字的顺序来决定多个servlet初始化的先后顺序 数字为1的先执行依次往后
Servlet的参数配置:
三大框架整合的时候,才会使用
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>com.MyServlet</servlet-class>
<!--配置参数-->
<init-param>
<param-name>name</param-name>
<param-value>tom</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
注意:init-param需要在load-on-startup之前
然后可以通过servlet的init方法进行参数的获取,init方法只会执行一次,统一参数也只会获取一次
<!--
@Override
public void init(ServletConfig arg0) throws ServletException {
// TODO Auto-generated method stub
System.out.println("初始化方法");
String name=arg0.getInitParameter("name");
System.out.println(name);
}
-->
Servlet的注解方式的配置
在servlet类上直接加上注解
写法一:@WebServlet("/servletDemo1")
写法二:
@WebServlet(value="/servletDemo1")
写法三 @WebServlet(urlPatterns="/servletDemo1")
参数为该servlet的虚拟路径
这样就可以直接访问这个servlet对象了
Servlet的体系结构:
javax.servlet.Servlet 通用接口
----javax.servlet.GenericServlet 通用的抽象类
----javax.servlet.http.HttpServlet 通用的抽象类(针对http协议的)
*适配器设计模式:
就是有一个接口类,我们想不全部实现里面的方法,这样一来我们就需要一个适配器抽象类,我们用抽象类去实现接口类。然后再抽象类中可以实现方法。我们再去继承这个适配器抽象类。就可以只重写其中的部分方法了
Servlet的实现方式
方式一:定义一个类去实现Servlet接口
class ServletDemo1 implements Servlet {}
方式二:定义一个类继承GenericServlet抽象类
class ServletDemo2 extends GenericServlet {}
方式三:定义一个类继承HttpServlet抽象类
public class MyHttpServlet extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
System.out.println("成功");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
this.doGet(req, resp);
}
}
注意:需要再里面重写Servlet中的的方法如果不重写会报405
Servlet的url-pattern配置
Servlet的url-pattern配置?
@WebServlet(urlPatterns={"/servletDemo1", “/aaa/servletDemo1”,“servletDemo1.do”})
可以配置多个路径
路径定义规则:
1. /XXX 例子:/servletDemo1
2. /XXX/OOO 例子:/aaa/servletDemo1, /aaa/bbb/*
3. *.do 例子:*.do, *.aaa
以上的*都是通配符
注意:
1. /*.do
/和*.do不能放在一起
2. /*/servletDemo1
*不是通配符,就是*
Http协议
定义:超文本传输协议
超文本:超级文本,比一般文本强大
传输协议:定义了,客户端和服务器之间,数据的相互使用的传递数据的格式
http协议的特点:
1、是基于TCP/IP的高级协议,是建立再tcp协议基础之上的
2、http协议的默认端口号是80
3、http协议是基于请求和响应的模式的协议 ,一次请求对应一次响应
4、http协议是一种无状态协议,两次请求之间没有任何关系,互相不干涉
无状态:指上一次请求对于下一次请求是不影响的
http协议的历史版本
http/1.0版本:
一次连接中,只能进行一次请求和一次响应
断开时机:请求响应一次一次
http/1.1版本:复用连接,一次连接中多次请求响应。
断开的时机:
1、如果客户端和服务端任意一方端口,连接会自动断开
2、超时也会自动断开
http协议请求消息格式
请求行:
格式:请求方式 请求URI 协议/版本号
GET /day27/servletDemo1?username=tom HTTP/1.1
细节一:请求方式
请求方式一共有7个,但是我们重点关注2个
get:
1.请求参数是在请求行中的,也是在地址栏上显示的
2.相对不安全
3.传输数据的大小是有限制的
post:
1.请求参数是在请求体中的, 不会在地址栏上显示的
2.相对安全
3.传输数据的大小是没有限制的
细节二:HTTP协议1.0和1.1区别
细节三:URI和URL
什么是URL?
统一资源定位符
组成结构:协议 + IP + 端口 + URI
http + localhost + 8080 + /项目的虚拟目录 + /资源名称(servlet的虚拟路径) 刚果共和国 中华人民共和国
什么是URI?
统一资源标识符
组成结构:/项目的虚拟目录 + /资源名称(servlet的虚拟路径) 共和国
这两个哥们哪个范围大呢?
URI比URL范围大的
URL比URI更加精确,URL能直接根据地址定位到互联网中的某一个资源文件
请求头:
客户端和服务器之间说的悄悄话
客户端对服务器端说的悄悄话
格式:请求消息头:请求消息值
key :value
例子:
Accept:浏览器可接受的MIME类型。浏览器告诉服务器,我这边能接收的MIME类型都有哪些
MIME类型:响应回来的数据如果是文件的,那其实文件也是有类似于Java中的数据类型的
格式:大类型/小类型
例子:image/jpg
image/png
text/html
Cookie:这是最重要的请求头信息之一
Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
浏览器告诉服务器,我是从何而来
可以干什么?
防盗链
统计
User-Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。
浏览器告诉服务器,我是什么浏览器
请求空行:
它的用处只是用来区分请求头和请求体的
请求体(请求正文):
get:由于请求参数是在请求行的URI的后面跟着,所以并不会存储在请求体中,
一般我们也会说get请求的请求体是没有的。
post:请求参数是不在请求行中的uri后面的,是存储在请求体中的,所以post请求是有请求体的
多个请求参数之间使用&分隔的,连续的
Request
定义:request是一个请求对象,是由tomcat帮助我们创建的,我们直接拿来即可。里面封装了所有的请求相关的数据,里面会提供大量的方法,让我们调用获取请求消息中的数据
request体系结构
javax.servlet.ServletRequest 接口
-----javax.servlet.http.HttpServletRequest 接口
----org . apache . catalina . connector . RequestFacade实现类
request对象原理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cIE6XWJv-1615994809501)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201019095755648.png)]
1、当我们向服务器的资源发送请求,tomcat会创建request对象,会将所有数据都封装在request中
2、传递给service(ServletRequest request),里面向下转型传递给service(HttpServletRequest request)
3、service里面调用doGet/doPost(HttpServletRequest request)
request的常用方法
1、获取请求消息的方法
a、获取请求行的方法
String getMethod ():获取请求方式
String getContextPath ():获取项目的虚拟目录
String getServletPath ():获取Servlet的虚拟路径
String getQueryString ():获取get请求的请求参数
String getRequestURI ():获取请求的URI
StringBuffer getRequestURL():获取请求的URL
String getProtocol ():获取协议/版本号
String getRemoteAddr ():获取IP地址
b、获取请求头的方法
String getHeader (String name): 根据请求头的名称获取请求头的值
Enumeration getHeaderNames():获取所有的请求头的名称
Enumeration getHeaders (String name):根据请求头的名称获取多个请求头的值
key:value1
key:value2
int getIntHeader (String name) :根据请求头的名称获取请求头的值(请求值为int类型的时候)
c、获取请求体的方法
注意:只有post请求才有请求体
在学习文件的上传的时候会用到
ServletInputStream getInputStream ()
BufferedReader getReader ()
2、其他方法
a、获取请求参数的通用方法
*String getParameter (String name ):根据请求参数的名称获取值
Enumeration getParameterNames ():获取所有的请求参数的名称
String[] getParameterValues (String name):根据请求参数的名称获取值(多个)
*Map<String, String[]> getParameterMap ():将所有请求参数的名称和值都封装到了map对象
中文乱码问题:
get:tomcat8之后,已经不会出现中文乱码,tomcat8之前依然会出现中文乱码
post:中文都会出现乱码
因为tomcat默认编码是ISO-8858-1,所以乱码
解决方法:只要将两边的编码进行匹配,而我们开发环境是utf-8, 所以说我们只需要将tomcat的默认编码修改为utf-8即可。
request . setCharacterEncoding (“utf-8” )
BeanUtils(封装获取的数据成对象)的使用
1、导入相关jar包
2、调用相关方法
BeanUtils.populate(user, map);
b、请求转发相关方法
请求转发:可以帮助我们实现页面的跳转
Request:
public request.getRequestDispatcher (String path) ;
RequestDispatcher :
public void forward (ServletRequest request, ServletResponse response )
特点:
1、转发只支持服务器内部跳转
2、转发的地址栏是没有发生改变的
3、转发中,只有一次请求一次响应
c、数据共享相关方法
request对象不是一般的对象,它是一个域对象。
域对象:域,就是指一个区域,一个范围,在我们web中有四大域对象, 这四个域对象每个掌握一个域,而request域对象所掌握的域是一次请求范围的(一次请求和一次响应之间)
setAttribute(String key,String value);
String getAttribute(String key);
removeAttribute(String key);
注意:只要是域对象都有这三个方法
只有转发可以实现数据的共享
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x8xpRjq2-1615994809502)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201020175806530.png)]
路径问题
相对路径:
./:当前目录,./可以省略不写
格式一:
./资源名称
./servletDemo
格式二:
资源名称
servletDemo
…/:代表上一级目录
注意:在转发中写相对路径是访问地址中,
一般为:./html/demo.html
或者:html/demo.html
绝对路径:
以前: 以盘符开始的路径就是绝对路径
C://xxx//ooo
D://xxx//ooo
现在:格式一:以http开头
http://localhost:8080/web01/demo1.html
格式二:以/开头的
/项目虚拟目录/资源名称
/web01/demo1.html
注意:如果是转发的话,不能写项目的虚拟目录,之间写资源路径,因为tomcat会自动带上项目的虚拟目录
http的响应
http响应格式:
响应行:
格式:协议/版本号 状态码
状态码:描述此次请求和响应的状态的
常见的状态码都有:
1、消息:1XX
服务器接收到了客户端的请求,但是还没有接收完成,等待了一下,然后服务器给客户端发的状态码
2、成功:2XX
表示请求已经被服务器接收
200:代表没问题,成功
3、重定向:3XX
表示需要客户端采取进一步操作才能完成请求。
302:重定向
当我们访问服务器端某个资源时,如果响应状态码为302,我们就知道是服务器通过重定向返回的。
304:缓存
缓存出现的时机:
当我们第一次访问资源时,状态码是200.当我们再次访问时,状态码就是304
当我们清空缓存之后,再去访问服务器资源,状态码就会变回200,当我们再次访问时又会变回304
当我们访问的资源被更新后,那我们再去访问该资源的话,响应状态码会变成200
资源被更新:看资源文件最后的修改时间
4、请求错误:4XX
这类的状态码代表了客户端可能发生了错误,妨碍服务器的处理
404:资源没找到
一般是路径写错了
405:没有重写doGet() 或者doPost()方法
5、服务器错误:5XX
这类状态码代表服务器再处理请求的过程中有错误或者异常状态发生
500:出现了500,说明服务器有错误,而且产生这个问题的原因有多种情况
响应头:服务器对浏览器说的悄悄话
Content-Type:text/html;charset=utf-8
服务器告诉浏览器,响应的数据的类型是什么,以及用什么编码解析该资源
text/html 这是一个MIME类型
location:实现重定向的响应头
Content-Disposition:服务器告诉浏览器,你要用什么类型来解析我响应回去的数据
文件的下载,需要设置该响应头
响应空行:区分响应头和响应体
响应体:响应的内容
response响应对象
设置响应状态码
setStatus(int sc)
设置响应头和值
setHeader(String name,String value)
重定向:
方式一:
满足重定向,需要两个条件
1、响应状态码为302
response.setStatus(302)
2、响应头为location
response.setHeader(“location”,"/web02/ServletDemo01")
注意:重定向,路径写法,一定要加入项目的虚拟目录,而转发不需要
方式二:
response.sendRedirect("/web02/ServletDemo01")
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HQoQ4s9u-1615994809503)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201020175729199.png)]
重定向和转发的区别:
1、转发是一次请求一次响应,重定向是两次请求,两次响应
2、转发的地址栏是没有变化的,重定向的地址栏是改变的
3、转发不写项目的虚拟目录(tomcat会自动添加)。重定向是要写项目的虚拟目录
4、转发是内部跳转,只能跳转到项目内的一些其他资源。重定向不不限于项目内,其他服务器也可以
设置响应体
PrintWriter getWriter()
ServletOutputStream getOutputStream()
注意:如果是响应数据是有中文的话,会出现乱码,原因是两边的编码是不匹配的,解决办法是,将tomcat写出的编码改为utf-8
response.setCharacterEncoding(“utf-8”)
但是如果浏览器的编码不是utf-8依然会出现乱码
服务器还需告诉浏览器用什么编码来解析资源
response.setHeader(“Content-Type”,“text/html;charset=utf-8”)
其实上面两句可以用一句来表示:
优化1:
response.setHeader(“Content-Type”,“text/html;charset=utf-8”)
优化2:
response.setContentType(“text/html;charset=utf-8”)
访问到WEB-INF里面的资源
只能通过转发来实现,由于转发是服务器内部实现所以可以
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ix10Z8bP-1615994809504)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201020175621000.png)]
ServletContext
定义: ServletContext对象代表当前的应用程序,当前的项目
每一个项目中有且仅有一个ServletContext对象
ServletContext对象的生命周期:
当我们开启服务器的时候,服务器所部署的项目,都会自动的生成ServletContext对象,由tomcat所创建的。
当服务器被关闭,或者应用程序被卸载的时候ServletContext对象就消失了。
ServletContext对象的获取方式
方式一:
request.getServletContext();
方式二:
this.getServletContext();
注意:两种方式获取的对象是同一个,只是获取方式不同而已
ServletContext的常用方法
1、获取MIME类型
大的类型/小的类型
image/jpg
…
String mimeType = servletContext.getMimeType(“1.html”);
2、作为域对象使用
ServletContext也是一个域对象,是四大域对象之一。
它所管辖的区域是整个项目
可以实现数据的共享
setAttribute(String key,Object value);
String getAttribute(String key);
removeAttribute(String key);
3、可以获取项目下的资源的真实路径—是资源真实在电脑中的位置
String getRealPath(资源名);
可以使用这种方式去加载配置文件
加载配置文件方式:
方式一:使用类加载器(相对于classpath路径下)
ServletCon1.class.getClassLoader().getResourceAsStream("aaa.properties");
方式二:使用ServletContext.getRealPath(相对于当前项目下)
String realPath = servletContext.getRealPath("html/aaa.properties");
FileInputStream is2 = new FileInputStream(new File(realPath));
注意:如果加载的是classpath路径下的资源,推荐使用类加载器
如果是加载classpath外的资源推荐使用ServletContext的方式
jsp
jsp里面既可以写Java代码,也可以写HTML标签
jsp=java+HTML
jsp的本质:
jsp的本质是一个Servlet
jsp的脚本
格式一:<%代码%>
里面写的是Java代码,在这个里面写的Java代码,会被翻译成在_jspService内部
格式二:<%!代码%>
里面写的是Java代码,在这个里面写的Java代码,会被翻译成在成员位置
格式三:<%=代码%>
里面写的是Java代码,在这个里面写的Java代码,会输出到页面上
jsp的指令
对jsp进行配置的,导入一些资源的
格式:<%@指令名称 属性名1=属性值1 属性名2=属性值2%>
page指令:主要是用来对jsp进行配置的
contentType:是jsp翻译成servlet之后,所设置的响应头,该响应头是服务器告诉浏览器响应的相应的数据类型和编码
pageEncoding:是指jsp翻译成servlet所使用的编码
import:导入包
errorPage:如果该jsp页面出现了错误,会跳转到指定的错误页面
isErrorPage:默认为false,false时,该jsp翻译成的servlet就没有exception对象,如果为true就会翻译成自动生成exception对象
include指令:可以用来包含其他的页面
静态包含:<%@include file="其他的页面"%>
会将两个合并成一个生成servlet
动态包含:<jsp:include page="其他的页面">
每一个页面会生成各自的servlet
推荐使用静态包含
taglib指令
jsp的注释:
方式一html原生的:
方式二jsp特有的:<%–你好–%>
注意:方式一在jsp翻译成的jsp中是存在的
方式二是不存在的
推荐使用方式二
jsp中的九大内置对象
内置对象:不能创建,直接使用
显示的名称 | 真实的类型 | 作用 |
---|---|---|
pageContext | PageContext | 四大域对象之一,代表范围是当前jsp页面,可以实现数据的共享 |
session | HttpSession | 四大域对象之一,代表的范围是一次会话,可以实现数据的共享 |
request | HttpServletRequest | 四大域对象之一,代表的范围是一次请求,可以实现数据的共享 |
application | ServletContext | 四大域对象之一,代表的范围是整个项目,可以实现数据的共享 |
response | HttpServletResponse | 响应对象,可以用来设置响应头、响应状态码,实现重定向等 |
config | ServletConfig | servlet的配置对象,可以配置一些信息 |
out | JspWriter | 输出流,向页面写东西,和PrintWrite功能差不多 |
page | Object | 它其实是指jsp翻译成servlet的servlet对象 |
exception | Throwable | 异常对象,可以打印一些异常信息 |
EL表达式
表达式语言
目的:替换Java代码,使得jsp写起来更加简单
EL表达式的格式:
${表达式}
注意:如果我们想在浏览器上显示这种格式时:
方案一:
\${表达式}
方案二:
我们在page指令中写一个属性:isELIgnored=“true”,表示忽略该页面的EI表达式
表达式的写法:
1、运算符
${运算符}
a、算术运算符:+、-、*、/
b、逻辑运算符:&&、||、!
c、比较运算符:>、<、>=、<=、==
d、空运算符:empty 为空就是true
not empty不为空就是true
用于判断数组,字符串,或者集合,内容是否为空或者是否为null
2、获取值
注意:想要使用el表达式获取数据的话,只能从域对象中获取数据
a、${域名称.名字}
requestScope.名字
pageScope.名字
sessionScope.名字
applicationScope.名字
<%
String name="tom";
request.setAttribute("name", name);
session.setAttribute("name", name);
application.setAttribute("name", name);
pageContext.setAttribute("name", name);
ArrayList<String> list=new ArrayList<>();
list.add("jeck");
list.add("rose");
request.setAttribute("list", list);
%>
${requestScope.name }<br>
${sessionScope.name }<br>
${applicationScope.name }<br>
${pageScope.name }<br>
${requestScope.list }<br>
b、${键名}
根据域的范围大小,由小到大进行查找,直到每一个域中都不存在,就不会展示了,如果找到了就直接展示
范围: pageScope <requestScope<sessionScope<applicationScope
<%
String name="tom";
request.setAttribute("name", name);
session.setAttribute("age", 11);
application.setAttribute("a", "17级");
pageContext.setAttribute("sex", "男");
ArrayList<String> list=new ArrayList<>();
list.add("jeck");
list.add("rose");
request.setAttribute("list", list);
%>
${name }<br>
${age }<br>
${sex }<br>
${a }<br>
${list }<br>
3、获取对象中的数据
a、普通类对象
${键名.对象属性名}
User user=new User();
user.setName("张三");
user.setAge(11);
pageContext.setAttribute("user", user);
${user.name }<br>
${user.age }<br>
b、集合对象
1、单列集合
${键名[索引]}
List<String> list=new ArrayList<>();
list.add("aaa");
list.add("bbb");
pageContext.setAttribute("list", list);
${list[0] }<br>
${list[1] }<br>
2、双列集合
${键名.map的键名}
${键名[‘键名’]}
Map map=new HashMap();
map.put("name", "李四");
map.put("sex","man");
pageContext.setAttribute("map", map);
方式一
${map.name }<br>
${map.sex }<br>
方式二
${map['name'] }<br>
${map['sex'] }<br>
4、获取当前项目的虚拟目录
el表达式中有很多隐式对象,但是我们只需要知道pageContext即可
pageContext隐式对象可以获取到其他8个内置对象
格式:
${pageContext.request.contextPath}
案例:
<form action="${pageContext.request.contextPath }/ServletDemo" method="get">
姓名:<input type="text" name="name"/>
<input type="submit" vlaue="提交"/>
</form>
JSTL
jsp标准标签库,用于取代jsp页面上的Java代码
如何引入JSTL标签库
1、导入相关jar包
2、导入taglib指令
<%@taglib prefix=“c” uri=“http://java. sun.com/ jisp/ jstl/core” %>
常见的标签:
if:用来判断的
<c:if test=""></c:if>
案例:
<%
String str="aaa";
request.setAttribute("str", str);
%>
<c:if test="${empty str }">
str是空的
</c:if>
<c:if test="${not empty str }">
str不是空的
</c:if>
foreach:用来循环的
<c:forEach begin="1" end="5" step="1" var="a" varStatus="s" item=""></c:forEach>
begin:起始索引
end:结束索引
step:步长
items:要遍历的容器对象
var:容器中的元素
varStatus:
index:索引
count:计数
注意items在不使用的时候不可以写否则就不会进行循环了
遍历数组:
<%
int arr[]={11,12,13,14}
request.setAttribute("arr",arr)
%>
<c:forEach items="${arr}" var="a">
${a}<br>
</c:forEach>
遍历集合
单列集合:
List<String> list=new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
request.setAttribute("list", list);
<c:forEach items="${list }" var="a">
${a }<br>
</c:forEach>
双列集合:
Map<String,String> map=new HashMap<>();
map.put("张三","11");
map.put("李四","12");
map.put("王五","13");
request.setAttribute("map", map);
<c:forEach items="${map }" var="a">
${a.key }...${a.value}<br>
</c:forEach>
MVC设计思想
M:Model 用来进行数据的封装 bean
V:View 用来进行页面的展示 jsp
C:Controller 用来进行逻辑代码的编写,操作数据库,等 servlet
软件设计架构
以前:jsp+bean
这种方式不好,一个jsp里面东西太多,太乱,在后期难以维护以及修改
后来:jsp/html+servlet+bean
jsp/html:用来进行页面展示的
servlet:用来写一些Java代码
bean:用来对数据进行封装的
分工明确
三层架构
设计思想
表示层:web层,这一层主要用来和前台页面进行数据的交互
业务逻辑层:service层,这一层主要是处理业务逻辑的代码
数据访问层:dao层,这一层主要是操作数据库的代码
会话技术
说明:
cookie:客户端会话技术
session:服务器端会话技术
会话:
在一次对话中,浏览器和服务器之间有多次请求和多次响应
结束时间:双方只要有一方断开,会话就结束
会话作用:可以实现多次请求和多次响应之间的数据的共享
cookie:
客户端会话技术
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NJWRs7yq-1615994809505)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201026094759742.png)]
入门案例:
ServletDemo1:
方式一:支持设置多个值
//创建cookie对象
Cookie cookie=new Cookie("name","tom");
//添加cookie对象
response.addCookie(cookie);
方式二:只能设置一个值
//response.setHeader("set-cookie", "name=jeery");
ServletDemo2:
//获取cookie对象数组
Cookie[] cookies = request.getCookies();
if(cookies!=null) {
//遍历cookie对象数组
for (Cookie cookie : cookies) {
//获取cookie对象的对应值 System.out.println(cookie.getName()+"....."+cookie.getValue());
}
}
注意:
1、可以写多个cookie
如果cookie的名字相同的话,虽然可以写到浏览器,但是到了浏览器旧值会被覆盖
2、中文问题
cookie可以存储中文,但是使用的setHeader方式的话,中文会乱码,如果实在想用这一方式,我们可以先对中文进行URL编码,然后获取时进行解码
但是使用new Cookie对象就不会乱码
3、生命周期:
默认存活时长:浏览器被关闭,cookie就消失了
如果我们想让它存活更久一点,可以进行设置 cookie.setMaxAge(int value)
value的设置有三种情况:
负数:默认,如果是负数,浏览器关闭cookie就消失了,cookie是存储在浏览器的缓存中,所以浏览器关了缓存就消失了
正数:以秒为单位,不管浏览器关不关,只要时间到就会消失,cookie存储在了本地磁盘,时间一到就自动清理
0:当cookie从服务器写到浏览器的时候,马上消失
4、cookie的携带范围:
默认情况下,访问当前项目下的任何资源,都会携带cookie
可以设置它的携带范围
setPath(String path);
setPath("/web-cookie-session"):默认,我们访问项目下的任何资源,同样都会携带。
setPath("/web-cookie-session/ServletDemo2");相当于只有访问这一资源的时候才会携带cookie
setPath("/");只要是该服务器下所部署的项目,访问资源时都会携带cookie
Session
服务器端的会话技术
session是一个域对象
域对象的范围:一次会话的范围
request.setAttribute(String key,Object value);
session.setAttribute(String key,Object value);
application.setAttribute(String key,Object value);
注意:
1、如果浏览器关闭了,服务器没有关闭,会发生什么事
如果浏览器关闭了之后,在浏览器缓存中存储的JSESSIONID的数据,就会消失。下次再去访问服务器的,就不会携带原来的JSESSIONID去请求
但是通过设置可以实现关闭浏览器再打开还是可以访问
Cookie cookie=new Cookie()
2、如果浏览器不关闭,服务器关闭关闭的话,会发生什么事:
因为浏览器没有关闭,所以JSESSIONID的cookie没有消失,一直存在,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rxQX7OJY-1615994809507)(C:\Users\18146\AppData\Roaming\Typora\typora-user-images\image-20201026094720604.png)]
a、然后我们如果非正常关闭服务器,session就消失了,当我们再次开启服务器通过浏览器访问,cookie会携带,JSESSIONID也有,但是匹配不到session对象,,所有再服务器端会重写创建一个session对象,并且重新生成一个session的id值
b、然后我们如果正常关闭服务器,session也会消失,但是它会进行钝化(将session的数据存储到本地服务器)当我们再次开启服务器,session会被活化(激活,将本地磁盘的数据还原成Java中的对象)通过浏览器访问,cookie所携带的JSESSIONID会于服务器端session的ID值匹配,数据自然就获取到了,不会创建新的session对象
序列化和反序列化
序列化:将对象以流的形式存储到本地
反序列化:将本地磁盘中的文件以流的形式还原成对象
想要序列化和反序列化,前提条件是类需要实现序列化接口
3、session对象被销毁的方式
a、关闭服务器
b、它可以自杀
session.invalidate();
c、通过配置session的生存时长
session默认存活30分钟
可以在tomcat的web.xml文件中配置
<session-config>
<session-timeout>30</session-timeout>
</session-config>
session和cookie的区别
1、cookie是存储在客户端的,session是存储在服务器端的
2、cookie存储数据是有大小限制的,4kb,而session是没有大小限制的
3、cookie用来存储数据,相对不安全,session相对安全
Filter
过滤器,Javaweb三大组件之一
作用:可以将编码处理统一放在过滤器中,减少代码的 编写
可以实现用户的登陆校验
过滤一些敏感词汇
入门案例
1、创建一个类,实现Filter接口,并且重写三大方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
System.out.println("1");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
System.out.println("FilterDemo1");
//表示交给下一个过滤器或者servlet处理
chain.doFilter(request, response);
}
@Override
public void destroy() {
// TODO Auto-generated method stub
System.out.println("2");
}
2、在web.xml中对filter进行配置
<filter>
<filter-name>FilterDemo1</filter-name>
<filter-class>com.filter.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/ServletDemo1</url-pattern>
</filter-mapping>
过滤器的生命周期
1、执行流程:当服务器被开启的时候,filter对象会被创建,并且会调用init方法
2、当我们通过浏览器访问servlet的时候servlet对象会被创建,并且调用init方法,然后通过过滤器doFilter中的放行前代码,当我们响应的时候会执行doFilter中的放行后代码
3、当我们多次访问servlet的时候,只会重复的调用filter的doFilter方法和servlet的service方法
4、当我们正常的关闭服务器的时候,会先执行Servlet的销毁方法,然后再执行过滤器的销毁方法。
url-patten的配置(配置拦截路径)
1、具体的路径:/XXX
2、根据路径进行拦截:/XXX/*
3、针对后缀名进行配置:*.jsp
4、拦截所有的的配置:/*
过滤器的配置
方式一:基于web.xml配置文件的配置
方式二:基于注解的配置
在filter类上面加上注解:@WebFilter(“拦截路径”)
过滤器链
可以配置多个过滤器
执行流程
执行的先后顺序:
1、配置文件
谁在配置文件的上面谁先执行
2、注解
根据类名的字符串的字典顺序进行排序
Listener
定义:监听器,是Javaweb三大组件之一
常见的两个监听器
ServletContextListener:用来监听ServletContext对象的
ServletRequestListener:用来监听request对象的
1、创建一个类,实现ServletContextListener接口
public class MyServletContextListener implements ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("servletContext对象被销毁了...");
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("servletContext对象被创建了...");
}
}
2.重写两大方法
contextDestroyed()
contextInitialized()
3.进行配置
<listener>
<listener-class>com.listener.MyServletContextListener</listener-class>
</listener>
Listener配置方式?
方式一:基于配置文件的方式
<listener>
<listener-class>com.listener.MyServletContextListener</listener-class>
</listener>
方式二:基于注解方式
在类的上面加上@WebListener
分页查询
Jquery
js的一个框架
jQuery的版本说明:
入门案例
1、创建html或jsp文件
2、导入相应的js文件
3、写jQuery代码
<script type="text/javascript">
window.onload=function(){
$("#div").css("border","red 1px solid")
}
</script>
<body>
<div id="div">你好</div>
</body>
Jquery和js对象的相互转换?
为什么要学会他们之间的相互转换呢?
因为有些功能使用jquery是非常方便的,那如果我们有js的对象,可以通过转换,变成jquery对 象。有些功能jquery对象不具备该功能,又要将jquery对象转换js对象再去使用。
jquery对象的格式?
$(“选择器”)
js对象如何转换成jquery对象?
1.获取js对象
var div = document .getElementById(“div”);
2.转换
var $div = $(div) ;
jquery对象如何转换成js对象?
1.获取jquery对象
var $div = $("#div") ;
2.转换
方式一: var div = $div.get (索引)
方式二: var div = $div[索引]
注意:我们可以把jQuery看成数组,里面的元素就是js原生对象
jQuery的入口函数,样式设置,事件处理
入口函数
js:
windown.οnlοad=function(){
}
注意:js的入口函数可以写多个,但是后者会覆盖前者
jQuery:
$(function(){
})
注意:和js不一样,可以写多个,而且不会被覆盖
样式设置
js:
div. style.border = “1px solid red”;
div.style = “border:1px solid red”;
jQuery:
$(“选择器”).css(“属性”,“样式”)
$(".div").css("border","1px solid red");
$("div").css("background-color","green")
事件处理:
js:
对象.οnclick=function(){
}
jQuery:
对象.click(function(){
})
jQuery中的选择器
选择器:能够帮助我们快速定位到元素
基本选择器:
id选择器:
$("#id属性值")
元素选择器:
$(“元素名”)
类选择器
$(".calss属性值")
通配符选择器
$("*")
组合选择器
$(“选择器,选择器”)
层级选择器:
包含选择器:
$(“a b”):找的是a下面所有的b
子元素选择器
$(“a>b”):找的是a下直属的b
属性选择器:
$(“标签名[属性名]”)
$(“标签名[属性名=‘属性值’]”)
基本过滤选择器:
获取第一个:
$(“选择器:first”)
获取最后一个
$(“选择器:last”)
获取奇数下标的个:
$(“选择器:even”)
注意:实际上展示出的是奇数
获取偶数下标的个
$(“选择器:odd”)
注意:实际上展示出的是偶数
表单过滤选择器
获取复选框或者单选框中被选中的元素
$(“对象:checked”)
获取所有选中的option元素
$(“对象:selected”)
jQuery关于dom的操作
1、关于文本的操作
html(); 获取文本内容
html(val); 设置文本内容
text(); 获取文本内容
text(val); 设置文本内容
注意:html获取的是该对象下的所有内容,包括标签等,而text获取的的纯文本,不包含标签,同理设置内容也是这样的
val():获取输入框中的值
val(val):设置输入框中的值
2、关于属性的操作
通用属性
attr(“属性名”,“属性值”);//添加属性
removeAttr(“属性名”);//删除属性
prop(“属性名”,“属性值”)://添加属性
removeProp(“属性名”);//删除属性
注意:prop()主要是设置的固有属性
removeProp(“属性名”)是将name变为undefined的,removeAttr(“属性名”)是直接删除
class属性:
addClass(“class的值”);//添加class属性的值
removeClass(“class的值”);//删除指定class的值
3、关于节点的操作
append()
对象A.append(对象B):将对象B添加到对象A中的所有内容后面
appendTo();
对象A.appendTo(对象B):将对象A添加到B中的所有内容后面
prepend()
对象A.prepend(对象B):将对象B添加到对象A中的所有内容前面
prependTo()
对象A.prependTo(对象B):将对象A添加到B中的所有内容前面
after():
对象A.after(对象B):将对象B放入到对象A外的后面
insertAfter()
对象A.insertAfter(对象B):将对象B放入到对象A外的前面
before()
对象A.before(对象B):将对象B放入到对象A外的前面
insertBefore()
对象A.insertBefore(对象B):将对象B放入到对象A外的后面
jQuery对象的遍历:
1、普通for循环
for (var i=0;i<divs.length;i++) {
alert(divs[i].innerHTML);
}
2、容器.each(function(i,index){
其中i是索引,index是当前所遍历到的元素对象
});
divs.each(function(i,index){
alert(index.innerHTML)
//this指当前遍历到的对象
//i指当前索引
//index指当前遍历到的对象
})
3、$.each(容器,function(i,index){
其中i是索引,index是当前所遍历到的元素对象
});
$.each(divs, function(i,index) {
alert(index.innerHTML);
});
4、for(var i of 容器){
其中i代表当前遍历到的元素对象
注意这一方式只支持3.X以上的版本
}
for(var i of divs){
alert(i.innerHTML);
}
Ajax
ajax
异步交互,无刷新(局部刷新)
异步:在同一个时间点双方可以同时执行
同步:在同一个时间点,一方正在执行,另一方只能等待
Ajax的优势:无刷新(局部刷新)
ajax的实现方式:
方式一:传统js原生代码实现,不推荐,因为特别麻烦
方式一:传统js原生代码来实现,不推荐,因为特别麻烦
function aaa() {
//获取xmlHttpRequest对象
var xmlHttp = ajaxFunction();
//给xmlHttpRequest对象动态绑定事件,监听服务器那边的动态
xmlHttp.onreadystatechange = function() {
//判断状态,结果为4说明响应完成
if(xmlHttp.readyState == 4) {
//获取响应数据
alert(xmlHttp.responseText);
}
}
//发送ajax请求
//第一个参数:请求方式;第二个参数:请求路径;第三个参数:如果true,说明是ajax请求
xmlHttp.open("get","${pageContext.request.contextPath}/servletDemo1", true);
//参数是post请求的请求参数
xmlHttp.send(null);
}
方式二:jQuery方式来实现(get和post通用)
$.ajax({
type:"post",//请求方式,可以是post,也可以是get
url:"${pageContext.request.contextPath}/servletDemo1",//请求资源路径
data:"name=tom&age=18",//请求参数
success:function(data){//回调函数,当响应成功来回之后,会执行该方法,data参数:响应回来的数据
alert(data);
}
});
方式三:jQuery方式来实现(具体到get或post的方式)
*方式三:jquery方式来实现(具体到get或者post的方式)
//第一个参数:请求资源路径
//第二个参数:请求参数
//第三个参数:回调函数,响应成功之后调用该方法,data是响应的数据
//第四个参数:响应回来的数据的格式
$.post("${pageContext.request.contextPath}/servletDemo1","name=tom&age=18",function(data) {
alert(data);
},"json")
$.get("${pageContext.request.contextPath}/servletDemo1","name=tom&age=18",function(data) {
alert(data);
},"json")
Json
定义:json就是一个js的对象,它有自己的格式
作用:用来传输输入
json语法:
基本格式:var json={key:value,key:value,…}
注意:key是字符串,可以用引号(单双都可以)引起来也可以不引
value可以是数值,true/false, 字符串,对象,方法,都可以
获取数据:
json对象.键名
json对象[‘键名’]
遍历:
for(var i in json对象){
i是key
json[i]是value
}
json格式的字符串和对象的相互转换
常见的json解析器
Jackson,fastjson
使用步骤:
1、导入jar包
json格式字符串->对象
String json = "{\"name\":\"tom\",\"age\":18}";
//创建对象
ObjectMapper om = new ObjectMapper();
User user = om.readValue(json, User.class);
System.out.println(user);//User [name=tom, age=18]
对象 ->json格式字符串
User -> json
User -> json格式字符串
//创建User对象
User user = new User();
user.setName(“tom”);
user.setAge(18);
//创建ObjectMapper对象
ObjectMapper om = new ObjectMapper();
//调用方法,将user对象转换成json格式的字符串
String user_json = om.writeValueAsString(user);
System.out.println(user_json);//{"name":"tom","age":18}
或者
om.writeValue(response.getWriter(), pageBean);
List -> json
//创建User对象
User user1 = new User();
user1.setName(“tom”);
user1.setAge(18);
User user2 = new User();
user2.setName("张三");
user2.setAge(20);
//创建一个list集合容器
ArrayList<User> list = new ArrayList<>();
//添加元素
list.add(user1);
list.add(user2);
//创建ObjectMapper对象
ObjectMapper om = new ObjectMapper();
//调用方法,将user对象转换成json格式的字符串
String list_json = om.writeValueAsString(list);
System.out.println(list_json);//[{"name":"tom","age":18},{"name":"张三","age":20}]
Map -> json
//创建map
HashMap<String, String> map = new HashMap<>();
map.put("name", "jack");
map.put("age", "18");
//创建ObjectMapper对象
ObjectMapper om = new ObjectMapper();
//调用方法,将user对象转换成json格式的字符串
String list_json = om.writeValueAsString(map);
System.out.println(list_json);//{"name":"jack","age":"18"}
ontextListener implements ServletContextListener {
@Override
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("servletContext对象被销毁了...");
}
@Override
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("servletContext对象被创建了...");
}
}
2.重写两大方法
contextDestroyed()
contextInitialized()
3.进行配置
<listener>
<listener-class>com.listener.MyServletContextListener</listener-class>
</listener>
Listener配置方式?
方式一:基于配置文件的方式
com.listener.MyServletContextListener
方式二:基于注解方式
在类的上面加上@WebListener
分页查询
### Jquery
js的一个框架
jQuery的版本说明:
#### 入门案例
1、创建html或jsp文件
2、导入相应的js文件
3、写jQuery代码
<script type="text/javascript">
window.onload=function(){
$("#div").css("border","red 1px solid")
}
</script>
<body>
<div id="div">你好</div>
</body>
#### Jquery和js对象的相互转换?
为什么要学会他们之间的相互转换呢?
因为有些功能使用jquery是非常方便的,那如果我们有js的对象,可以通过转换,变成jquery对 象。有些功能jquery对象不具备该功能,又要将jquery对象转换js对象再去使用。
#### jquery对象的格式?
$("选择器")
#### js对象如何转换成jquery对象?
1.获取js对象
var div = document .getElementById("div");
2.转换
var $div = $(div) ;
#### jquery对象如何转换成js对象?
1.获取jquery对象
var $div = $("#div") ;
2.转换
方式一: var div = $div.get (索引)
方式二: var div = $div[索引]
注意:我们可以把jQuery看成数组,里面的元素就是js原生对象
#### jQuery的入口函数,样式设置,事件处理
入口函数
js:
windown.onload=function(){
}
注意:js的入口函数可以写多个,但是后者会覆盖前者
jQuery:
$(function(){
})
注意:和js不一样,可以写多个,而且不会被覆盖
样式设置
js:
div. style.border = "1px solid red";
div.style = "border:1px solid red";
jQuery:
$("选择器").css("属性",“样式”)
```js
$(".div").css("border","1px solid red");
$("div").css("background-color","green")
事件处理:
js:
对象.οnclick=function(){
}
jQuery:
对象.click(function(){
})
jQuery中的选择器
选择器:能够帮助我们快速定位到元素
基本选择器:
id选择器:
$("#id属性值")
元素选择器:
$(“元素名”)
类选择器
$(".calss属性值")
通配符选择器
$("*")
组合选择器
$(“选择器,选择器”)
层级选择器:
包含选择器:
$(“a b”):找的是a下面所有的b
子元素选择器
$(“a>b”):找的是a下直属的b
属性选择器:
$(“标签名[属性名]”)
$(“标签名[属性名=‘属性值’]”)
基本过滤选择器:
获取第一个:
$(“选择器:first”)
获取最后一个
$(“选择器:last”)
获取奇数下标的个:
$(“选择器:even”)
注意:实际上展示出的是奇数
获取偶数下标的个
$(“选择器:odd”)
注意:实际上展示出的是偶数
表单过滤选择器
获取复选框或者单选框中被选中的元素
$(“对象:checked”)
获取所有选中的option元素
$(“对象:selected”)
jQuery关于dom的操作
1、关于文本的操作
html(); 获取文本内容
html(val); 设置文本内容
text(); 获取文本内容
text(val); 设置文本内容
注意:html获取的是该对象下的所有内容,包括标签等,而text获取的的纯文本,不包含标签,同理设置内容也是这样的
val():获取输入框中的值
val(val):设置输入框中的值
2、关于属性的操作
通用属性
attr(“属性名”,“属性值”);//添加属性
removeAttr(“属性名”);//删除属性
prop(“属性名”,“属性值”)://添加属性
removeProp(“属性名”);//删除属性
注意:prop()主要是设置的固有属性
removeProp(“属性名”)是将name变为undefined的,removeAttr(“属性名”)是直接删除
class属性:
addClass(“class的值”);//添加class属性的值
removeClass(“class的值”);//删除指定class的值
3、关于节点的操作
append()
对象A.append(对象B):将对象B添加到对象A中的所有内容后面
appendTo();
对象A.appendTo(对象B):将对象A添加到B中的所有内容后面
prepend()
对象A.prepend(对象B):将对象B添加到对象A中的所有内容前面
prependTo()
对象A.prependTo(对象B):将对象A添加到B中的所有内容前面
after():
对象A.after(对象B):将对象B放入到对象A外的后面
insertAfter()
对象A.insertAfter(对象B):将对象B放入到对象A外的前面
before()
对象A.before(对象B):将对象B放入到对象A外的前面
insertBefore()
对象A.insertBefore(对象B):将对象B放入到对象A外的后面
jQuery对象的遍历:
1、普通for循环
for (var i=0;i<divs.length;i++) {
alert(divs[i].innerHTML);
}
2、容器.each(function(i,index){
其中i是索引,index是当前所遍历到的元素对象
});
divs.each(function(i,index){
alert(index.innerHTML)
//this指当前遍历到的对象
//i指当前索引
//index指当前遍历到的对象
})
3、$.each(容器,function(i,index){
其中i是索引,index是当前所遍历到的元素对象
});
$.each(divs, function(i,index) {
alert(index.innerHTML);
});
4、for(var i of 容器){
其中i代表当前遍历到的元素对象
注意这一方式只支持3.X以上的版本
}
for(var i of divs){
alert(i.innerHTML);
}
Ajax
ajax
异步交互,无刷新(局部刷新)
异步:在同一个时间点双方可以同时执行
同步:在同一个时间点,一方正在执行,另一方只能等待
Ajax的优势:无刷新(局部刷新)
ajax的实现方式:
方式一:传统js原生代码实现,不推荐,因为特别麻烦
方式一:传统js原生代码来实现,不推荐,因为特别麻烦
function aaa() {
//获取xmlHttpRequest对象
var xmlHttp = ajaxFunction();
//给xmlHttpRequest对象动态绑定事件,监听服务器那边的动态
xmlHttp.onreadystatechange = function() {
//判断状态,结果为4说明响应完成
if(xmlHttp.readyState == 4) {
//获取响应数据
alert(xmlHttp.responseText);
}
}
//发送ajax请求
//第一个参数:请求方式;第二个参数:请求路径;第三个参数:如果true,说明是ajax请求
xmlHttp.open("get","${pageContext.request.contextPath}/servletDemo1", true);
//参数是post请求的请求参数
xmlHttp.send(null);
}
方式二:jQuery方式来实现(get和post通用)
$.ajax({
type:"post",//请求方式,可以是post,也可以是get
url:"${pageContext.request.contextPath}/servletDemo1",//请求资源路径
data:"name=tom&age=18",//请求参数
success:function(data){//回调函数,当响应成功来回之后,会执行该方法,data参数:响应回来的数据
alert(data);
}
});
方式三:jQuery方式来实现(具体到get或post的方式)
*方式三:jquery方式来实现(具体到get或者post的方式)
//第一个参数:请求资源路径
//第二个参数:请求参数
//第三个参数:回调函数,响应成功之后调用该方法,data是响应的数据
//第四个参数:响应回来的数据的格式
$.post("${pageContext.request.contextPath}/servletDemo1","name=tom&age=18",function(data) {
alert(data);
},"json")
$.get("${pageContext.request.contextPath}/servletDemo1","name=tom&age=18",function(data) {
alert(data);
},"json")
Json
定义:json就是一个js的对象,它有自己的格式
作用:用来传输输入
json语法:
基本格式:var json={key:value,key:value,…}
注意:key是字符串,可以用引号(单双都可以)引起来也可以不引
value可以是数值,true/false, 字符串,对象,方法,都可以
获取数据:
json对象.键名
json对象[‘键名’]
遍历:
for(var i in json对象){
i是key
json[i]是value
}
json格式的字符串和对象的相互转换
常见的json解析器
Jackson,fastjson
使用步骤:
1、导入jar包
json格式字符串->对象
String json = "{\"name\":\"tom\",\"age\":18}";
//创建对象
ObjectMapper om = new ObjectMapper();
User user = om.readValue(json, User.class);
System.out.println(user);//User [name=tom, age=18]
对象 ->json格式字符串
User -> json
User -> json格式字符串
//创建User对象
User user = new User();
user.setName(“tom”);
user.setAge(18);
//创建ObjectMapper对象
ObjectMapper om = new ObjectMapper();
//调用方法,将user对象转换成json格式的字符串
String user_json = om.writeValueAsString(user);
System.out.println(user_json);//{"name":"tom","age":18}
或者
om.writeValue(response.getWriter(), pageBean);
List -> json
//创建User对象
User user1 = new User();
user1.setName(“tom”);
user1.setAge(18);
User user2 = new User();
user2.setName("张三");
user2.setAge(20);
//创建一个list集合容器
ArrayList<User> list = new ArrayList<>();
//添加元素
list.add(user1);
list.add(user2);
//创建ObjectMapper对象
ObjectMapper om = new ObjectMapper();
//调用方法,将user对象转换成json格式的字符串
String list_json = om.writeValueAsString(list);
System.out.println(list_json);//[{"name":"tom","age":18},{"name":"张三","age":20}]
Map -> json
//创建map
HashMap<String, String> map = new HashMap<>();
map.put("name", "jack");
map.put("age", "18");
//创建ObjectMapper对象
ObjectMapper om = new ObjectMapper();
//调用方法,将user对象转换成json格式的字符串
String list_json = om.writeValueAsString(map);
System.out.println(list_json);//{"name":"jack","age":"18"}
文件上传