一、SQL

1 数据库概述

1.1 什么是数据库

数据是一个专业的存储和管理数据的仓库

数据库的分类
早期:层次式数据库,网络型数据库
现在:关系型数据库,非关系型数据库
现在市面上大部分用的还是关系型数据库

1.2 什么是关系型数据库

底层以二维表的形式保存数据的库,就是关系型数据库

常见的关系型数据库有哪些?
  • Oracle: 甲骨文公司提供的一款数据库产品,收费的,之前在Java中的市场份额超过50%。
    主要适用于一些大型或者超大型应用系统。

  • SQL Server:微软提供的一款数据库产品,收费,主要适用于一些中型或者大型的应用系统

  • MySQL:瑞典的一个公司(MySQLAB)提供的一款数据库产品,特点是小巧轻量,简单易用,适用于一些小型或中型的应用系统,如果做mysql集群,也可以用于一些大型或者超大型系统。免费!

    mysql被甲骨文收购了!

  • DB2:IBM公司提供的一款数据库产品, 用于金融/银行等系统较多, 收费!

  • SQLite:迷你数据库, 用于嵌入式设备(手机/智能家居等产品)

1.3 数据库相关名词解释

数据库服务器:其实就是你安装的哪个mysql软件,将mysql安装在计算机上,那么这台计算机就可以作为数据库服务器使用,可以实现数据的存和取。一个数据库服务器中可以包含多个数据库。

比如:装好的mysql服务器中自带了四个数据库
±-------------------+
| Database |
±-------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
±-------------------+

  • 数据库

    数据库就是存储数据的仓库,通常情况下一个网站(系统)中的所有数据回存放在一个数据库中
    京东网站的所有数据 db_jd
    淘宝网站的所有数据 db_taobao
    百度网站的所有数据 db_baidu

  • 数据库中的数据是安装类型存放的, 一类数据往往存储在一张表中, 一个数据库中可以创建多张表!
    京东网站的用户数据 tb_user
    京东网站的商品数据 tb_product
    京东网站的订单数据 tb_order

  • 表记录

    一张表中可以包含多行表记录, 一张表中用于存储一类信息, 一行表记录就用于存储某一个具体的数据
    数据库中的表 java中的类(student)
    表记录 对象

1.4 SQL语言

SQL语言是一门操作关系型数据库的通用的语言(学会了SQL可以操作所有的关系型数据库)

SQL语言可以操作的有:

  • 查看库、创建库、删除库、修改库(了解)
  • 创建表、删除表、修改表(扩展内容中)、查看表
  • 新增表记录、删除表记录、修改表记录、查询表记录(数据)
  • 存储过程/视图/索引等也可以操作

SQL语言是一门通用的操作关系型数据库的语言(普通话),但每个数据库厂商为了增强自己数据库的功能,都提供了少量的"方言"(独有的SQL语句),SQL语言通用,但方言不通用!

1.5 相关cmd命令

  • mysql -uroot -proot # u用户名 p密码
    

    mysql中默认有一个超级管理员(具有所有权限),用户名就是root

  • mysql -uroot -p
    

    回车后在下一行键入密码(会有**的效果)

  • mysql -uroot -proot -h127.0.0.1 -P3306
    # h主机名或IP地址 P端口
    

    h: 后面跟的是主机名或ip地址,如果不写-h,默认连接localhost(127.0.0.1)
    P: 后面跟的是端口, 如果不写-P,默认端口是3306

  • 退出连接mysql服务器:exit、quit或者直接关闭窗口

  • \c # 取消当前sql语句的执行(执行前)
    
  • SQL注释:

    -- 单行注释(注意--后面的空格不可省)
    #单行注释
    /* 多行注释 */
    
  • 复制,选中后鼠标右键,自动复制到剪切板

  • 按上下箭头可以找到之前输入过的sql语句

  • Ctrl+C 退出mysql(其他cmd应用同样适用)

  • cls # 清屏(进入sql命令后不能使用,仅cmd可用)
    

2 SQL语句

mysql(MariaDB)安装目录下的data目录存放的就是所有sql的数据库,包名就是数据库名字

针对多个单词拼接的名字,建议使用下划线拼接,比如user_nickname

注意mysql中不支持-,所以UTF-8要写成utf8

2.1 DDL数据定义语言

select database(); -- 查看已进入的库
drop database if exists mydb1; -- 删除mydb1库,如果存在则删除mydb1,如果不存在则不执行删除操作,也不会报错
create database if not exists mydb1 charset utf8; -- 如果不存在则创建mydb1,如果已存在,则不执行创建操作,也就不会报错了!
show create database mydb1; -- 查看建库,验证使用的编码
drop table if exists stu;
create table stu(name verchar(50), -- 最多50个字符,中英文一致birthday date, score double
);

2.2 DML数据操作语言

  • 如果是在cmd中执行插入记录的语句,先set names gbk;

    这是用来通知数据库服务器,当前cmd窗口发送给服务器的数据的GBK的,那么服务器就会按照GBK编码来接收cmd窗口发送过来的数据,再将GBK的数据转换成utf8编码的数据存入数据库中。
    这个命令只能用在cmd窗口,而且是每次新开一个cmd窗口都需要重新设置一次!!!像Navicat/SQLYog等工具不需要设置该命令,因为这些工具底层已经设置过编码了。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9NApIwnV-1619579904889)(01mysql\其他文件\中文乱码问题.png)]

  • mysql中推荐使用单引号包裹字符串or日期值(有些版本的数据库使用双引号会报错)

    insert into stu(birthday) value('1908-10-2'); -- 日期写法
    update stu set score=score+10; -- mysql不支持复合运算符 score+=10 错!
    

2.3 DQL数据查询语言

2.3.1 普通查询
  • ifnull(a, 0) 对a列中的null进行处理,将null值用第二个参数0进行替换

    -- 在查询时将null值看做0来处理(不会修改表中数据),注意as位置
    select name,ifnull(sal,0)+ifnull(bonus,0) as 总工资 from emp;
    
  • distinct 去重(若有两个及以上数据,去重是去组合的重,不是单个列的重)

    -- 相同dept,job组合的记录只保留一行
    select distinct dept, job from emp;
    
  • 不为null

    select * from emp where not(dept is null);
    select * from emp where dept is not null;
    
2.3.2 聚合函数

多行函数也叫做聚合函数(聚集函数),多行函数会默认过滤null值,即不统计null值。
常见的多行函数有:

  • count(列名 | *):
    count(列名): 表示统计当前列的值有多少个(不统计null值)
    count(*): 以行为单位,统计查询结果中有多少行记录
  • max/min(列名): 表示统计当前这一列中所有值中的最大值/最小值
  • sum(列名): 表示统计当前这一列中所有值的和(也就是说会将这一列中所有的值加在一起返回)
  • avg(列名): 表示统计当前这一列中所有值的平均值
    这一列中所有值的和 / 不是null值的个数
2.3.3 其他函数
  • curdate() 获取当前日期:年月日

  • curtime() 获取当前时间:时分秒

  • sysdate()/now() 获取当前日期+时间,年月日 时分秒

  • year(‘2020-8-10’) 返回日期中的年份2020
    month和day同理

  • hour(‘2020-8-10 12:34:56’) 返回时间中的小时12
    minute和second同理

  • concat(s1,s2,…sn) 将 s1,s2,…sn 拼接在一起返回

    -- 结果是'王海涛2450',是数据库不存在的数据
    select concat('王海涛',2450);
    -- 结果是王海涛1800,是数据库的查询结果
    select concat(name,sal) from emp where name='王海涛';
    -- concat更改数据的显示方式,eg:2000元
    select concat(sal,'(元)') from emp where id=1;
    

    concat_ws(x,s1,s2,…sn) 将 s1,s2,…sn 拼接在一起,并且每两个拼接时会通过x作为分隔符进行拼接,再返回

    -- 结果为'王海涛,1995-03-25,2450'
    select concat_ws(',' ,'王海涛', '1995-03-25', 2450);
    
2.3.4 案例

查询emp表中所有1993-1995出生的员工,显示姓名和出生日期

select name, birthday from emp where birthday between 1993 and 1995; -- 错误,由于birthday是日期类型(年月日格式),而1993和1995都是数值,没法比较
-- 方式一:
select name, birthday from emp where birthday between '1993-1-1' and '1995-12-31';
-- 方式二:
select name, birthday from empwhere year(birthday) between 1993 and 1995;

查询emp表中本月过生日的所有员工

select * from emp where month(now()) = month(birthday);

统计emp表员工的平均薪资(包括奖金)

select avg(sal+bonus) from emp; -- 错!,因为韩少云的薪资(5000)加上奖金(null),结果是null,会被avg直接过滤
select avg(sal)+avg(bonus) from emp; -- 错!,avg(sal)由于sal中没有null值,是总薪资/12, 而avg(bonus)由于bonus中有一个null值,是总奖金/11
-- 正确方法:
select avg(sal+ifnull(bonus,0)) from emp;

3 mysql的数据类型

具体看mysql-notes.html

3.1 数值类型

mysql中提供了多种数值类型,其中包括:

tinyint、smallint、int、bigint、float、double、decimal等

其中较为常用的就是 int、double

3.2 字符串类型

  • char类型: 定长字符串, char(n), n的范围是: 0~255个字符
    char类型之所以叫做定长字符串,是因为一旦确定了n的最大字符数,不管存的数据是多少,该数据占用的空间就是n个字符。例如:name char(10), 存入’张三丰’, 存入了3个字符,剩余的空间会用空格补全。
    因此char类型可能会浪费空间!所以char类型适合存储长度固定的数据, 比如:
    student_id char(11), 用这个列存储所有学生的编号.
    idcard char(18), 用这个列存储所有人的身份证号.
    char类型相比varchar类型速度要快一些,因为char类型只需要判断一个数据是否能存入该列中,而不需要将剩余的空间留给别的数据使用!
  • varchar类型: 变长字符串, varchar(n), n的范围是: 0~?个字符
    varchar类型之所以叫变长字符串,是因为n只是限制该列中最多能存的字符数, 如果你实际存的数据量小于n,剩余的空间还可以留给别的数据使用。例如:name char(10), 存入’张三丰’, 存入了3个字符,剩余的7个空间会留给别的数据使用!
    因此varchar类型不会浪费空间!所以varchar类型适合存储长度不固定的数据(长度固定的数据我们会使用char类型)
  • varchar类型最大能存的数据量是 0~65535个字节
    • latin1编码中,1个字符对应1个字节, n的最大值约是 65535/1 (max = 65532,另外三个字节用来统计)
    • gbk编码中,1个字符对应2个字节, n的最大值约是 65535/2 (max = 32766)
    • utf8编码中,1个字符对应3个字节, n的最大值约是 65535/3 (max = 21844)
  • 面试题: char和varchar有什么区别?
    • char和varchar存的数量是不同的, char类型最多能存255个字符, varchar类型最多能存65535个字节
    • char类型如果存的数据量小于最大长度, 剩余的空间会使用空格填充, 因此可能会浪费空间。所以char类型适合存储长度固定的数据, 这样既不会浪费空间, 效率还比varchar略高。
    • varchar类型如果存的数据量小于最大长度, 剩余的空间会留给别的数据使用,所以varchar类型适合存储长度不固定的数据, 这样虽然没有char存储效率高, 但至少不会浪费空间。

3.3 日期类型

date: 日期类型, 格式是: 年月日

time: 时间类型, 格式是: 时分秒

datetime: 日期+时间,格式是: 年月日 时分秒

timestamp: 时间戳, 格式和datetime相同, 也是: 年月日 时分秒,

timestamp和datetime不同的是:

  • 范围上: datetime范围是: 1000~9999(年份)
    timestamp范围是: 1970到2038年
  • 实际存的数据: datetime实际存的就是一个“年月日 时分秒”格式的日期+时间,而timestamp实际存储的是这个从1970年1月1日到这个日期+时间的时间毫秒值
    create_time timestamp, 2018-2-3 14:45:56, 实际存储的是 1970年1月1日到2018-2-3 14:45:56时间的时间毫秒值
  • 在使用上: timestamp可以设置自动获取当前时间作为值插入到表中, 而datetime不可以.
    2018-2-3 14:45:56

4 mysql的字段约束

4.1 主键约束

如果一个列添加了主键约束,那么这个列的值就必须是非空的且不能重复。

主键通常用于唯一的表示一行表记录(就像人的身份证号一样),一张表中通常都会有且只有一个主键,如果设置了两个主键,那么比如“1,1”和“1,2”不算重复主键,所以一般还是设置为一个。

create table stu(id int primary key,...);

如果id是主键并且是数值类型,为了方便维护,可以设置主键自增策略:

create table stu(id int primary key auto_increment,...);

在设置完主键自增之后,表中会维护一个AUTO_INCREMENT的值,这个值从1开始。如果插入主键时没有给主键赋值,就会从AUTO_INCREMENT这里获取一个值再作为主键插入到表中。再用完之后,会自动将AUTO_INCREMENT的值加1。

4.2 非空约束

如果一个列添加了非空约束, 那么这个列的值就不能为空(null), 但可以重复

create table stu(...,gender varchar(10) not null,...);

4.3 唯一约束

如果一个列添加了唯一约束, 那么这个列的值就不能重复, 但可以为空(null)

比如: 网站绑定的邮箱, 前期可以不绑定, 即可以为null, 但一旦绑定, 这个邮箱是不能和其他账号的邮箱重复的。

create table stu(...,email varchar(50) unique,...);
create table stu(...,username varchar(50) unique not null -- 用户名既不能重复,也不能为空...
);

4.4 外键约束

-- 建表时添加外键约束
CREATE TABLE emp(...,dept_id INT,   foreign key(dept_id) references dept(id)    on delete cascade -- 级联删除(如果要删除部门,且部门下有员工,数据库会在保证数据完整性的情况下,先把对应部门的员工删了,再删部门)
);
-- 另外添加外键约束(关联字段要用括号括起来)
ALTER TABLE emp ADD CONSTRAINT 约束名 FOREIGN KEY (dept_id) references dept(id);
-- 删除外键
ALTER TABLE emp DROP FOREIGN KEY 约束名;
-- 查询是否有外键
SHOW CREATE TABLE emp;
4.4.1 添加外键和不添加外键有什么区别?
  • 如果不添加外键:对于数据库来说,dept_id这个列就是一个普通的列,数据库也不会知道dept和emp两张表存在任何关系,自然也不会帮我们去维护这层关系。
    假如现在要删除dept表中的某一个部门(4号部门),删除后就会造成emp表中的赵六和刘能找不到部门,而赵六和刘能后面对应的dept_id值为4,这个数据也变成了冗余数据。这样会破坏数据库中数据的完整性和一致性
  • 如果将dept_id添加为外键:将dept_id添加为外键就等同于通知数据库 dept 和 emp 两张表存在对应关系,而且emp表中的dept_id列要参考dept表中的id列。
    这样数据库就会帮我们维护这两张表的对应关系,如果此时删除dept表中的某一个部门(4号部门),数据库会首先检查这个4号部门在emp表中还有没有对应的员工,如果有,数据库就会阻止我们删除!这样就保证了数据的完整性和一致性。
    如果非要删除,可以将4号部门里面的员工记录转移到其他部门或者删除,只要保证所删除部门中没有对应的员工即可!
4.4.2 补充:表关系
  • 1对多(多对1): 在这种关系中,往往会在多的一方添加列,保存一的一方的主键(可以设置外键,当然也可以不设置,看需求),比如:
    部门表(1) 员工表(), 在员工表()中添加列(dept_id)保存部门的编号
    班级表(1) 学生表(), 在学生表()中添加列(class_id)保存班级的编号

  • 1对1: 在这种关系中,可以在任意一方添加列保存另一方的主键(可以设置外键,当然也可以不设置,看需求), 比如:
    班级表(1) 教室表(1), 在班级表(1)添加列(room_id)来保存教室的编号
    班级表(1) 教室表(1), 在教室表(1)添加列(class_id)来保存班级的编号

  • 多对多: 在这种关系中,在任何一方添加列保存另一方的主键都不合适
    此时可以再创建一张表,在这种表中分别添加两个列(stuid,teaid), 分别用于保存学生表的主键和教师表的主键,以此来保存学生和教师的对应关系!

    create table stu_tea(stu_id int, -- 学生编号    tea_id int, -- 教师编号    primary key(stu_id,tea_id), -- 设置联合主键    -- 添加外键    foreign key(stu_id) references stu(stu_id),    foreign key(tea_id) references tea(tea_id)
    );
    

4.5 区别

  • 主键约束 和 非空+唯一 特点是相同的, 都是不能为空且不能重复
  • 主键约束除了非空且不能重复之外, 还可以表示唯一一行表记录, 即作为表记录的唯一标识
  • 外键约束不同于主键、非空、唯一约束,外键约束是用于表示两张表的对应关系

5 多表查询

5.0 笛卡尔积查询

笛卡尔积查询:同时查询两张表,其中一张表有m条记录,另外一张表有n条记录,查询的结果是m*n条。

select * from dept,emp;

但这种查询结果中包含大量错误的数据,所以我们一般不会直接使用这种查询。在笛卡尔积查询的基础上,通过where子句将错误的数据剔除,只保留正确的数据,这就是连接查询。

5.1 内连接查询

在使用内连接查询的时候,如果没有对应关系,例如:第四个部门没有员工,则不会显示,如果想要显示,应使用外连接。

select * from dept inner join emp on emp.dept_id=dept.id;
-- 以上内连接查询和下述查询结果一致
select * from dept,emp where emp.dept_id=dept.id;

5.2 外连接查询

左外连接查询:可以将左边表中的所有记录都查询出来,右边表只显示和左边相对应的数据,如果左边表中某些记录在右边没有对应的数据,右边显示为null即可。右外连接查询同理。

select * from dept left join emp on emp.dept_id=dept.id;

查询所有部门以及所有员工, 如果部门没有对应员工,可以对应null,如果员工没有对应部门,也可以对应null,这种情况应使用全外连接查询

union关键词模拟全外连接

mysql不支持全外连接查询,但可以通过union来模拟这种查询。

  • union关键字是用于将两个查询结果上下合并在一起显示,并且会去除重复记录。

    select * from dept left join emp on emp.dept_id=dept.id
    union
    select * from dept right join emp on emp.dept_id=dept.id;
    
  • union all关键字是用于将两个查询结果上下合并在一起显示,不会去除重复记录

    select * from dept left join emp on emp.dept_id=dept.id
    union all
    select * from dept right join emp on emp.dept_id=dept.id;
    
  • 能够使用union和union all合并结果的查询语句,必须符合:

    1. 两条SQL语句查询的结果列数必须相同
    2. 两条SQL语句查询的结果列名必须相同(低版本mysql要求)

5.3 案例

  1. 列出所有员工及其直接上级,显示员工姓名、上级编号、上级姓名

    解决方案:由于员工表有且仅有一个,所以可以通过别名解决

    查询的表:emp e(员工表) emp s(上级表)

    SELECT e.name,e.topid,s.name
    FROM emp e,emp s
    WHERE e.topid=s.id
    
  2. 列出受雇日期早于直接上级的所有员工,显示员工编号、员工姓名、部门名称、上级编号、上级姓名

    连接条件:WHERE e.topid=s.id AND e.dept_id=d.id

    SELECT e.id 员工编号,e.name 员工姓名,d.name 部门名,s.id 上级编号,s.name 上级姓名
    FROM emp e, emp s, dept d
    WHERE e.topid=s.idAND e.dept_id=d.id
    AND e.hdate<s.hdate; -- 条件也使用and连接
    

6 数据库的备份和恢复

6.1 备份数据库

6.1.1 备份单个数据库

在cmd窗口(未登录、未连接到mysql服务器的界面)中,可以通过如下命令对指定的数据库进行备份:

mysqldump -u用户名 -p密码 库名 > 备份文件的位置

注意!没有分号!

案例: 把db40库中的数据(表,表记录)备份到 d:/db40.sql 文件中

mysqldump -uroot -proot db40 > d:/db40.sql

注意:

  • 如果输入命令回车之后没有提示错误,就说明备份成功了!
  • 备份单个数据库,其实只会备份这个库中的表和表记录,并不会备份库本身!
6.1.2 备份多个数据库

在cmd窗口(未登录、未连接到mysql服务器的界面)中

mysqldump -u用户名 -p密码 --databases 库名1 库名2 ... > 备份文件的位置

案例:对db20 和 db40 库中的数据进行备份,备份到 d:/db2040.sql 文件中

mysqldump -uroot -proot --databases db20 db40 > d:/db2040.sql

注意:

  • 如果输入命令回车之后没有提示错误,就说明备份成功了!
  • 备份多个数据库,不仅会备份这个库中的表和表记录,同时会备份库本身!

如果想备份mysql服务器中的所有的库以及库中的表和表记录:

mysqldump -uroot -proot -A > d:/dball.sql

输入完后回车如果没有提示错误(警告可以忽略),表示备份成功。

6.2 恢复数据库

6.2.1 恢复单个数据库

在cmd窗口中(未登录的状态下),可以通过如下命令对指定的数据库进行恢复

mysql -u用户名 -p密码 库名 < 备份文件的位置

案例:将 d:/db40.sql 文件中的数据恢复到 db60 库中

先在cmd窗口中(已登录的状态下)创建db60库

create database db60 charset utf8;

再回到cmd窗口中(未登录的状态下), 执行下面恢复的命令

mysql -uroot -proot db60 < d:/db40.sql
6.2.2 恢复多个数据库

在cmd窗口中(已登录的状态下),可以通过source命令来执行指定位置的sql文件中的sql语句

source sql文件的位置

案例:将 d:/db40.sql 文件中的数据恢复到 db80 库中

先创建db80库, 并进入到db80库

create database db80 charset utf8;
use db80;

再通过source命令执行 d:/db40.sql 文件中的sql语句

source d:/db40.sql

案例:将 d:/db2040.sql 文件中的数据恢复回来

将db20,db40库删除(模拟数据丢失)

drop database db20;
drop database db40;

再通过source命令执行 d:/db2040.sql 文件中的sql语句

source d:/db2040.sql

6.3 cmd权限

7 其它

7.1 where和having的区别?

  1. where和having都是用于对表中的记录进行筛选过滤

  2. where用于在分组之前对记录进行筛选过滤,而having用于对分组之后的记录进行筛选过滤

  3. where子句中不能使用多行函数和列别名,但可以使用表别名

  4. having子句中可以使用多行函数、列别名,以及表别名

    select name as 姓名, sal as 薪资 from emp e; -- 姓名和薪资是列别名,e是表别名
    

7.2 navicat软件的使用

  • 下发的navicat软件是绿色解压版,解压之后进入到目录,找到navicat.exe双击即可启动程序
  • 点击左上角的连接图标,输入连接名(就是个自定义的名字),输入主机或ip地址,以及端口,再输入用户名和密码,点击左下角的连接测试,如果连接成功,点击确定即可。
  • "新建查询"支持所有sql语句,Ctrl+加号,调大sql语句的字体

二、JDBC

JDBC(Java DataBase Connectivity) Java数据库连接,是利用Java语言(Java程序)连接并访问数据库的一门技术。
像Mybatis/hibernate/DBUtils/Spring JdbcTemplate等框架底层也是通过JDBC来连接数据库。

1 通过JDBC连接数据库

1.1 概述

  1. 早期,不同的数据库厂商提供的数据库驱动包(jar)是各不相同的,开发人员在操作不同的数据库时需要学习该数据库对应的数据库驱动(jar)包,这对于开发人员的学习成本太高了。
  2. 后来SUN公司就规定了JDBC这套规范(其实其中包含了大量的接口),要求不同的数据库厂商提供的驱动都来实现这套接口,这样一来,开发人员只需要学会这套接口,所有的数据库就都会操作了!
  3. JDBC中主要包含两个包(java.sql和javax.sql),并且java中已经包含这两个包了。但由于JDBC只有接口,所以除了JDBC的包之外,我们在操作数据库时还需要导入该数据库对应的驱动包(jar包)。

1.2 JDBC使用案例

  • 导入mysql驱动包(mysql-connector-java-8.0.11.jar)入lib文件夹,引入文件
    eclipse:右键Build Path->Add To Build Path)
    idea:Project Structure->Libraries

  • 具体流程

    1. 注册数据库驱动(可以省略,但建议加上),交给驱动管理器来管理
    2. 获取数据库连接
    3. 获取传输器
    4. 发送sql到数据库执行,并返回执行结果
    5. 处理结果(打印到控制台)
    6. 释放资源
  • Java代码

    //1.注册数据库驱动
    Class.forName("com.mysql.cj.jdbc.Driver");
    //2.获取数据库连接
    /*   jdbc: 后面接使用的数据库的软件名localhost:3306 IP和端口(此种情况的IP和端口也可以省略)   jt_db 使用的库   characterEncoding 编码设置(建议设置)   & 连接两个参数   serverTimezone 设置时区(mysql版本低的话不可省略)   UTC 世界协调时间,比北京时间早八个小时  Asia/Shanghai (没有北京)   useSSL=false 建议不使用SSL协议,不写此句可能会有警告 */
    //Connection conn = DriverManager.getConnection(
    //  "jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false",
    //  "root","root");
    Connection conn = DriverManager.getConnection( "jdbc:mysql:///jt_db","root","root");
    //3.获取传输器
    Statement stat = conn.createStatement();
    //4.发送sql到数据库执行,并返回执行结果
    ResultSet rs = stat.executeQuery("select * from account");
    //5.处理结果
    while(rs.next()){   int id=rs.getInt("id");  String name=rs.getNString("name");   double money=rs.getDouble("money");  System.out.println(id+","+name+","+money);
    }
    //6.释放资源(越晚获取的越先关闭)
    rs.close();
    stat.close();
    conn.close();
    

2 JDBC连接数据库的优点和缺点

  • 优点:使用JDBC连接并访问数据库相比使用第三方的框架连接访问数据库速度要快一些。因为这是最为传统,最为底层的方法。
  • 缺点:
    a.JDBC中包含大量重复的代码(比如每次连接数据库都需要注册驱动、获取连接、获取传输器、处理结果、释放资源等),后期难以维护。
    b.JDBC自身没有连接池,而框架(mybatis自带连接池)自带连接池,当需要连接直接从连接池中获取,用完连接不用关闭,再还回连接池中,这样可以提高执行效率
    c.JDBC中执行select查询语句的结果需要开发人员自己手动处理,如果是非常复杂的数据(比如查询的结果中列数非常多或者查询的数据来自多张表)处理起来是非常麻烦的,但框架可以帮我们处理。

3 连接池概念

3.1 如果不使用连接池

用户每次需要连接访问数据库时,都需要创建一个连接(Connection),基于这个连接去访问数据库中的数据,用完连接(Connection)后会将连接关闭(close),其实每次创建连接和关闭连接(相比使用连接)需要消耗大量的时间和资源,导致程序的执行效率低下(特别是高并发的时候,比如京东618)

3.2 如果使用连接池

可以在程序一启动之后,就创建一批连接放在一个池中(容器), 当用户需要连接时, 不用自己创建, 而是从连接池中获取一个连接对象, 再基于这个连接对象去访问数据库, 用完连接后, 不用将连接关闭, 而是还回连接池中。这样一来,用户使用的都是池中的这一批连接,可以减少连接创建和关闭的次数,提高程序的执行效率!

三、事务

1 概述

1.1 DataBase Transaction

数据库事务(Database Transaction)是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。

简单来说:事务就是将一堆的SQL语句绑定在一起执行,必须所有SQL都执行成功了才算成功,但凡有一条失败,就按全失败来处理(即使执行成功的语句也会进行回滚,就是撤销当前的执行)

1.2 案例

A、B账户各有1000元,A给B转账100元

update account set money=money-100 where name='A';
-- A账户减去100元update account set money=money+100 where name='B'; -- B账户加上100元

如果上面两条语句执行时没有事务,就可能发生:第一条成功,但第二条失败了;或第一条失败,但第二条成功了。所以如果想保证上面的两条SQL语句同时成功或者同时失败,可以将这两条SQL添加到一个事务中。

2 事务的四大特征(面试题)

2.1 原子性

原子曾被认为是最小单位,不能被分割, 这里的原子性其实是指:事务中的所有SQL是一个整体,不能被分割。不存在一部分SQL执行成功,而另一部分SQL执行失败,都执行成功才算成功,有一条失败就算失败。

2.2 一致性

在事务执行前后(不管事务最后是提交还是回滚)的业务数据之和是保持一致的。以转账为例,事务执行前后的A、B金额之和是一致的。

2.3 隔离性

在事务并发时,一个事务理论上看不到另外一个事务的状态,也就是说事务之间是相互隔离开来的。一个事务只能看到另外一个事务没开始之前的数据状态,或者只能看到另外一个事务已经结束之后的数据状态。

这是因为,事务从开启后到结束之前是不会对数据做任何改变的,只会在做完之后才一起改变,所以在事务进行过程中是无法知道事务的状态的。

2.4 持久性

一旦事务提交之后,事务中对数据的更新操作会持久的保存到数据库中(最终是更新到硬盘的数据文件里)。反过来说,在事务提交之前, 对数据的更新操作只是一个临时的数据,没有真正的去修改数据库。

PS:非持久存储eg:Redis存到内存中,关闭就没有了。

3 mysql中的事务操作

在默认情况下,mysql中每次执行一条SQL语句,其实都是一个单独的事务。即使你不开启事务,每执行一条SQL之前,自动开启事务,执行完这条立即提交事务。如果需要在一个事务中包含多条SQL语句,那么就需要手动开启事务,和手动结束事务。

开启事务:begin; | start transaction;
结束事务:提交(commit),回滚(rollback)

-- 开启事务
begin;
-- 或者使用start transaction;
-- 进行事务...
-- 结束事务
-- rollback; 回滚事务(撤销所有事务,并结束事务)
-- 提交事务(真正提交数据,持久更新到数据库,并结束事务)
commit;

注意!!!回滚之后,如果有自增id,这个自增的auto_increment是不会回滚的!!!下一个id从auto_increment+1开始

四、Maven

1 简介

Maven:翻译为"专家",“内行”,是apache下的一个纯Java开发的开源项目。Maven是一个项目管理工具,可以简化项目配置,统一项目结构,使得开发者的工作变得更简单。

我们在课程中主要通过Maven创建Maven项目,通过Maven帮我们下载和管理项目中所需要的依赖。

依赖: 比如我们前面创建的JDBC项目中引用了一个mysql驱动包,如果将这个jar包从项目中移除,就会导致整个项目都运行不了,此时我们可以说这个项目依赖于mysql驱动包(也就是依赖于这个jar包),因此我们将项目中所需要引入的jar包称之为"依赖"。

2 下载、安装、配置

2.1 下载,安装maven

官方下载地址: http://maven.apache.org/download.cgi

Linux系统下载tar.gz文件,windows系统下载zip文件。

maven是一个绿色软件,解压之后就可以使用。安装时建议安装的路径中不要包含中文和空格。

2.2 配置maven的本地仓库

2.2.1 什么是本地仓库

本地仓库就是本地硬盘上的一个目录, 这个目录用于存放Maven帮我们下载的所有jar文件(包括一些插件等)。

如果不配置本地仓库,maven会使用一个默认的路径(以windows系统为例)作为本地仓库:c:/uses/{当前用户}/.m2/repository
如果你没有这个目录(.m2/repository),maven会在第一次使用时自动创建以上目录。可以保持默认,也可以将本地仓库配置到其他路径。

2.2.2 如何配置本地仓库

找到maven的安装目录:/conf/settings.xml,在这个settings文件的55行,添加如下内容:

<localRepository>d:/JaveDev/localRepo3</localRepository>

修改完后, 以后通过maven下载的所有jar文件都会保存到D:/JavaDev/localRepo3/这个路径中。

2.3 配置maven的远程仓库

2.3.1 什么是远程仓库

如果不配置远程仓库,maven默认会从中央仓库下载所需要的jar包和插件。中央仓库是由maven团队维护的jar包仓库,其中包含了全世界几乎所有的jar包,全世界所有的人都可以通过网络连接中央仓库去下载依赖(jar包)。中央仓库面向的是全球用户,所以下载速度比较慢。

总结:如果不配置远程仓库,默认连接中央仓库下载所需要的jar包和插件,保存到本地仓库中。

2.3.2 如何配置远程仓库

可以配置连接阿里云的远程仓库。

配置方法:找到maven的安装目录:/conf/settings.xml 文件,在settings文件的标签内部添加一个标签:

<mirror><id>nexus-aliyun</id>  <name>Nexus aliyun</name>   <mirrorOf>central</mirrorOf>    <url>https://maven.aliyun.com/repository/public</url>
</mirror>

2.4 配置JDK版本

因为Maven和eclipse整合之后,通过eclipse创建maven项目默认的JDK版本是1.5版本(太旧了)。可以在maven的安装目录:/conf/settings.xml文件中配置JDK版本。配置后,再通过maven创建项目其中的JDK版本就是我们配置的版本。

配置方法是: 在settings文件的标签内部添加如下配置:

<profile><id>development</id>  <activation>        <jdk>1.8</jdk>        <activeByDefault>true</activeByDefault>    </activation>    <properties>        <maven.compiler.source>1.8</maven.compiler.source>        <maven.compiler.target>1.8</maven.compiler.target>        <maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion></properties>
</profile>

注意:以上的本地仓库配置、远程仓库配置,以及JDK版本配置都是事先添加上上去的,如果你自己到maven官网下载一个新的maven软件,这个新的maven软件中是没有以上配置的,这些配置需要你自己来添加。

2.5 将maven整合到eclipse中

将maven整合到eclipse中就可以通过eclipse创建maven项目(其实eclipse中也自带了maven插件,但是不完整,后期在使用时很可能会出现问题,所以不建议使用)。

具体配置方法参考讲义<1.3.4.将Maven配置到Eclipse中>章节。如果在将maven和eclipse整合之后修改settings.xml文件,需要重启eclipse才能生效。



查看maven设置


3 Maven项目创建

3.1 通过Maven创建Java项目

注意:阿里云仓库不支持手机热点,不要使用手机热点连接!!!

new->others->maven project


其中packaging:jar表示Java项目,war表示web项目

3.2 通过maven创建Web项目

创建maven的web项目后,项目上会有一个叉号,这是因为项目中缺少文件导致的,解决方法是:在Web项目上右键 ->JavaEE Tools->Generate Deployment…,webapp目录下就会生成WEB-INF目录和web.xml文件

如果建好Web项目后,项目中没有web.xml文件并且还不报错,而且在项目上右键选项中没有"JavaEE Tools"这个选项,很可能是你的eclipse版本不对(eclipse是java版的, 正确的应该是eclipse企业版本)
企业版本的标识:Eclipse IDE for Enterprise Java Developers

3.3 maven项目的目录结构

  • src/main/java(源码目录):主要用于存放主程序/项目里的Java源文件
  • src/main/resources(源码目录):主要用于存放项目所需要的配置文件
  • src/test/java(源码目录):主要用于存放测试相关的java源文件
  • src/test/resources(源码目录):主要用于存放测试程序所需要的配置文件
  • src/main/webapp:相当于web应用的目录,将来这个目录中可以存放web资源文件(html/css/js/jsp/png…)
  • src/main/webapp/WEB-INF/web.xml文件:Web应用的核心配置文件
    其中可以做很多配置,比如:配置Servlet、配置session、配置监听器、配置过滤器…
  • target/classes:源码目录中的文件经过编译后会输出到classes目录
  • pom.xml:Maven项目的配置文件。如果当前项目需要引入jar包(mysql驱动包),就可以在pom文件中通过添加配置信息,让maven帮我们去下载这个jar文件,并引入到项目中。

4 Maven的依赖管理

4.1 Maven依赖引入

引入到maven项目中的jar包,存放在本地仓库中。这个jar存放的具体位置就是:本地仓库位置+当前jar的坐标

比如: 在项目的pom文件中引入了mysql8.0.11的驱动包

<dependency><groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>    <version>8.0.11</version>
</dependency>

而我们的本地仓库的位置是:D:\JavaDev\localRepo3
那么这个mysql8.0.11的jar文件的位置:
D:\JavaDev\localRepo3\mysql\mysql-connector-java\8.0.11

4.2 Maven三种仓库之间的联系

  • 本地仓库:就是本地硬盘上的一个目录,用于存放从远程仓库或者中央仓库下载下来的jar文件
  • 远程仓库:通常是由公司或团队搭建,服务于公司或团队的内部远程仓库。远程仓库刚搭建完毕时,内部几乎是没有jar包的,其中的jar包是从中央仓库下载下来并保存到远程仓库中。
  • 中央仓库:由maven团队维护的jar包仓库,其中包含了全世界几乎所有的jar包,全世界所有的人都可以通过网络连接中央仓库去下载依赖(jar包)。中央仓库面向的是全球用户,所以下载速度比较慢。

假如现在要做一个项目A,项目A中需要引入SSM三大框架的jar包

  • 在项目A的pom文件中添加三大框架的坐标之后,maven首先会根据坐标到本地仓库中去寻找这三大框架的jar包。
    如果本地仓库中有这些jar包,直接引入到当前项目中即可。
  • 如果本地仓库中没有这些jar包,maven会去连接远程仓库(前提是配置远程仓库),远程仓库中有的话,直接从远程仓库中下载到本地仓库中,在本地仓库中保留一份,再从本地仓库引入到项目A中。
  • 如果本地仓库要从远程仓库下载jar包,而远程仓库也没有这个jar包,远程仓库会自动连接中央仓库,从中央仓库中下载到远程仓库,在远程仓库中保留一份,再下载到本地仓库中,再从本地仓库引入到项目A中
  • 如果本地仓库中没有这些jar包,并且现在没有配置远程仓库,那么maven会直接连接中央仓库,从中央仓库下载jar包到本地仓库,在本地仓库中保留一份,再从本地仓库中引入到项目A中。

4.3 依赖的坐标

如果不知道一个jar包的坐标信息,怎么去在项目的pom文件中引入这个jar包?

  • 添加方式一:如果添加一个jar包不知道坐标是什么,可以从网上搜索这个jar包的坐标
    搜索jar包坐标的网址:http://mvnrepository.com http://maven.ityuan.com
  • 添加方式二:如果添加的jar包在本地仓库中,添加方法是: 打开当前项目的pom.xml文件, 在pom文件中右键 --> Maven --> Add Dependency, 在弹出的窗口中输入要添加的依赖包。
    如果本地仓库中有这个jar包也搜索不到,解决方法是:打开 Maven repositories 窗口,找到 Local Repository,在这个选项上右键->Rebuild Index(重建索引)

5 Maven常见问题

如果通过maven创建的项目结构不完整, 或者后期在引入jar包时, jar包引入失败, 可以参考如下解决方法:

  • 确保当前网络环境是否能连接上所配置的远程仓库,接着进行第2步。(若不在达内教室,是无法连接达内的远程仓库;又或者使用手机热点网络将无法连接阿里云的远程仓库等)
  • 在项目的pom文件中敲一个空白行,再保存文件。(目的是让maven检测到pom文件发生了变化,再根据pom文件中的配置,到本地仓库中寻找对应的jar包,如果没有相应的jar包,maven会重新下载)
  • 接着在项目上,右键—> Maven —> Update Project…,在弹出的窗口中勾选下方的 “Force Update…”,即强制更新项目,此时maven也会检查pom文件,下载没有引入的相关依赖。
  • 如果以上操作还是不行,到本地仓库的目录下,将本地仓库中所有的目录都删除,删除时,由于eclipse正在使用本地仓库中的资源文件,所以会阻止删除,此时将eclipse关闭,再将本地仓库中的所有目录删除,重启eclipse。
  • 启动eclipse后,再将上面的第(2)步和第(3)步再做一遍
  • 如果还是不行,就使用老师下发的本地仓库替换你的本地仓库

Maven的本地库支持复制

如果因为网络问题导致无法通过maven下载所需要的依赖,可以将别的电脑上、已经下载好的本地库,复制到我们的本地库中。在复制之前, 先将自己本地库目录中的所有内容全部删除(如果删除不了就关闭eclipse再删除), 将老师下发的本地库解压, 打开本地库目录, 选中里面所有的内容复制, 粘贴到自己的本地库中。

6 导入已有的Maven项目

6.1 导入数据库(yonghedb)

打开"yonghe-ssm项目"目录中的"SQL脚本.txt",复制其中的SQL语句,到Navicat中执行。执行后会创建yonghedb库,并创建tb_door表(门店表)和tb_order(订单表)。

6.2 导入Java项目

方式一:通过import导入(只要是eclipse创建的项目都可以这样导入)

  1. 点击eclipse左上角的"File" --> “Import…”,在弹出的窗口中找 “General” --> “Existing Project into Workspace”
  2. 接着会切换到下一个窗口,可以看到一个 "Select root directory"选项(选择根目录),这行的后面有 “Browse…” 选项,点击 “Browse…” 可以找到你要导入的项目,点击你要导入的项目,点击下方的 “选择文件夹”。
  3. 接着在窗口中间的位置找到 “Copy Pojects into workspace” 选项并选中该选项,选中该选项意味着:导入的项目会复制到工作空间中一份(这样的好处是,即使导入位置的项目删除了也不影响,因为已经把整个项目复制到工作空间中了),最后点击 “Finish” 完成即可。

方式二:创建新的项目, 将原项目中的代码复制到新项目中

  1. 创建一个同类型的新的项目
    如果要导入的项目是Maven的Web项目,也就需要你创建一个Maven的Web项目。这里新项目的名字可以和原项目保持一致,也可以不一致。
  2. 将原项目中的src目录复制到新项目中,将新项目中的src目录覆盖即可
  3. 如果是maven项目,那么其中还有pom.xml文件,将原项目中的 pom.xml 文件打开,将其中需要的配置复制,粘贴到新项目的pom文件中的对应位置即可。

6.3 eclipse运行Tomcat

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tKeGztMY-1619579904907)(02maven/tomcat/eclipse-Tomcat.png)]

6.4 可能出现的问题

  1. 导入项目后如果是xxx.xml文件报错,这里的错误可以忽略,是eclipse误报的。
  2. 如果在导入项目后有叹号(!)都是因为maven没有将所有的环境/依赖下载下来。
    可以通过如下三个步骤去解决:

    • 打开yonghe项目,找到其中的pom.xml文件,在文件中敲一个空白行并保存,让maven重新扫描pom文件,并根据其中的配置下载所需要的依赖。
    • 在报错的maven项目上右键–> Maven–> Update Project…,在弹出的窗口中勾选下方的[]Force Update…
      即勾选强制更新,如果还没有解决,再看第3步。
    • 下载老师下发的本地仓库(localRepo(maven的本地库).zip)
      用老师下发的本地仓库,替换自己的本地仓库。

运行导入的yonghe项目,在项目上右键–> Run As --> Run On Server,在打开的浏览器地址栏后面补全路径:http://localhost:8080/yonghe/index
/index对应的是controller中的一个方法,最后会跳转到index.jsp

五、Tomcat&Servlet

1 Tomcat

1.1 服务器

1.1.1 服务器
  • 服务器:分为软件服务器和硬件服务器
  • 硬件服务器:运行在互联网上的、具有静态IP的一台计算机(通常配置比较高)
  • 软件服务器:运行在互联网上的计算机程序(软件),将服务器软件安装在硬件服务器上,才可以对外提供服务。
    服务器软件分为很多种:数据库服务器(MySQL,Oracle,SQL Server等),Web服务器(tomcat,jetty,jboss等),邮件服务器,FTP服务器…
1.1.2 Web服务器

Web服务器:运行在互联网上的计算机程序,专门用于接收客户端(主要指浏览器)的请求,根据请求进行处理,最后给出回应。

比如:打开浏览器,输入"http://www.baidu.com"回车,其实访问的就是百度的服务器,此时会向百度服务器发送一个请求,请求百度的首页,百度服务器会接收并处理这个请求,根据请求给出回应(将百度首页响应给客户端浏览器)。

Tomcat就是一个Web服务器,特点是小巧灵活、简单易用、学习成本非常低。

1.2 使用Tomcat

1.2.1 下载Tomcat

下载地址: http://tomcat.apache.org

Tomcat分为很多版本:有windows版本(解压版和安装版)、linux版本,推荐使用解压版(需要用的时候解压一份,不需要用了直接删除解压的目录即可)。

1.2.2 安装启动Tomcat

由于Tomcat服务器是由Java语言开发的,所以运行Tomcat需要JDK的支持。在启动Tomcat之前,需要配置(检查)一个JAVA_HOME环境变量,该变量需要指向JDK的安装根目录:

变量名:JAVA_HOME
变量值:D:\Java\jdk1.8.0_161 (安装的JDK的根目录)

  • bin/startup.bat启动Tomcat服务器
  • bin/shutdown.bat文件关闭Tomcat服务器

启动Tomcat之后,可以打开浏览器访问:http://localhost:8080 http://127.0.0.1:8080 如果可以访问,则成功!

1.2.3 修改tomcat服务器默认端口

如果不修改端口,每次在访问tomcat服务器时都需要在[主机名/ip地址]的后面加上8080。如果想在访问时省略端口,可以将端口修改为80(这个端口特殊,可以省略不写)

修改端口的方法是:

找到conf/server.xml并用文本编辑工具打开这个文件:找到文件的69行,将Connector标签上的port属性值改为80,保存文件并重启服务器即可生效。

重启服务器后就可以通过如下路径访问:http://localhost:80 http://localhost http://127.0.0.1:80 http://127.0.0.1

1.3 Tomcat服务器的目录结构(了解)

  • bin:存放批处理文件的目录(startup.bat、shutdown.bat文件)
  • conf:存放tomcat配置文件的目录(server.xml是tomcat核心配置文件)
  • lib:存放tomcat服务器在运行时所依赖的jar包的目录
  • logs:存在tomcat服务器在运行时产生的日志文件的目录
  • temp:存放tomcat服务器在运行时产生的临时文件的目录
  • work:存放tomcat服务器在运行期间产生的一些工作文件(JSP在第一次被访问时翻译后的Servlet文件、session对象序列化后产生的文件等都会放在这个目录下)
  • webapps:是Web应用的存放目录,放在这个目录中的Web应用程序,可以通过localhost虚拟主机进行访问。
    webapps目录是localhost主机默认存放Web应用的目录。把Web应用放在webapps目录下,就相当于发布到了localhost主机中。

1.4 Web应用和虚拟主机

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ryZCZmqf-1619579904908)(02maven/web应用和虚拟主机.png)]

Web应用其实就是一个目录,其中可以包含很多资源文件(html/css/js/图片/jsp/servlet…)。虚拟主机中不能直接管理Web资源文件,需要将Web资源文件组织成一个Web应用(目录),将Web应用发布到虚拟主机中运行才可以被虚拟主机所管理。

虚拟主机就是在tomcat服务器中配置的一个站点,在访问时就好像在访问一台真实独立的主机一样。我们将这个站点称之为:运行在tomcat服务器中的一台虚拟主机。

tomcat服务器中可以配置多个站点,每一个站点都是一台虚拟主机。下面是tomcat默认提供的localhost主机的配置:

<Host name="localhost" appBase="webapps"...></Host>
<Host name="www.baidu123.com" appBase="baidu"...></Host>
  1. 在服务器硬件上安装了一个tomcat服务器软件
  2. 在tomcat服务器软件内部可以配置多个站点(虚拟主机),其中tomcat默认自带了一个localhost虚拟主机
  3. localhost虚拟主机默认管理Web应用的目录–webapps,发布到webapps目录下的web应用,也就都发布到了localhost主机中
  4. 往webapps中发布了一个 jt web应用,其中包含一些Web资源文件
  5. web资源文件可以是html/css/js/图片/servlet/jsp…

1.5 Web应用

Web应用的目录结构:

  • news(目录,Web应用)

    • 其它目录(放在其它目录中的资源文件可以被浏览器直接访问到)
    • WEB-INF目录(隐私目录,放在这里面的资源文件,不能被浏览器直接访问)
      • classes目录(Java程序编译后的class文件会放在这个目录下)
      • lib目录(Web应用所依赖的jar包会放在这个目录下)
      • web.xml文件(当前Web应用的核心配置文件)
  • 也可以将Web资源文件放在Web应用的根目录下

如果想要发布一个Web应用到虚拟主机中,直接将Web应用的目录复制到虚拟主机所管理的目录下即可。

例如:将news复制到webapps目录下。由于webapps是localhost主机发布web应用的目录,所以相当于将news发布到了localhost主机中,可以通过localhost主机进行访问。

2 Servlet

2.1 概述

Servlet是由SUN公司提供的一门Web资源开发技术(规范,接口)。Servlet本质上是一个Java程序,但和我们之前接触的Java程序不同的是:Servlet无法独立运行(Servlet中没有main函数),需要将Servlet程序放在服务器中,由服务器调用才可以执行。运行在服务器中的Servlet程序作用是:对服务器接收的请求进行处理(处理请求)。

jar包

<dependency><groupId>javax.servlet</groupId>    <artifactId>javax.servlet-api</artifactId>    <version>3.1.0</version>    <scope>provided</scope>
</dependency>
<!-- 或者 -->
<dependency><groupId>javax.servlet</groupId>    <artifactId>servlet-api</artifactId>    <version>2.5</version>    <scope>provided</scope>
</dependency>

2.2 开发Servlet程序

2.2.1 步骤
  1. 写一个类,需要实现一个Servlet接口或者继承Servlet接口的子类:
    Servlet 父接口->GenericServlet 实现了Servlet接口,并实现了其中的方法->HttpServlet 继承了GenericServlet,也实现了其中的方法

    在开发时,我们只需要继承HttpServlet,并继承其中的方法即可。

  2. 在web.xml文件中配置Servlet对外访问的路径,再将Web应用发布到服务器即可。
    如果是Servlet3.0及以上版本,可以使用注解方式配置Servlet访问路径。

2.2.2 Eclipse创建Servlet
  1. Ctrl+N,会弹出创建窗口,在输入框中输入 “servlet” 进行搜索,选中servlet,点击 “Next”。接着进入下一界面(其实这里和我们之前创建类几乎一样了,区别就是这里默认会继承HttpServlet)。
  2. 填写包名和类名点击完成即可。
  3. 创建好的类中有很多不需要的注释和构造方法实现,删除即可。
    创建的Servlet类的内部保留doGet和doPost方法即可。
2.2.3 运行Servlet程序

第一种运行方式:直接在要运行的文件(html/jsp/servlet等)上–>右键–>Run as–>Run On Server。

eclipse会帮我们做如下几件事:

  1. 将Servlet所在的Web项目(CGB-Servlet-01)发布到服务器中
  2. 再启动tomcat服务器
  3. 最后打开浏览器,在地址栏输入路径去访问这个Servlet

Eclipse默认用内置浏览器去访问Servlet,但是这个内置浏览器有bug,最好是使用本地的浏览器去测试。

第二种运行方式:可以自己手动将项目发布到服务器。启动服务器,打开浏览器输入地址进行访问即可。

2.2.4 Servlet在web.xml文件中的配置
<servlet><servlet-name>HelloWorld</servlet-name>    <servlet-class>cn.tedu.HelloWorld</servlet-class>
</servlet>
<servlet-mapping><servlet-name>HelloWorld</servlet-name><url-pattern>/HelloWorld</url-pattern><!-- 访问路径为:/HelloWorld -->
</servlet-mapping>
  1. 每创建一个Servlet,eclipse会帮我们生成至少8行配置信息,这8行配置信息由一个Servlet标签和一个servlet-mapping标签组成。这两个标签中的标签中的内容一致,决定了它俩是一组配置。
  2. 标签中配置的当前Servlet类的全类名(包名.类名)。将来服务器根据访问路径找到这个全类名,再利用反射+全类名可以获取当前Servlet类的实例。
  3. 标签中配置了外界该通过什么路径来访问当前Servlet。
    也就是说,这里配置什么路径,外界就得通过什么路径来访问这个Servlet。

注意事项:

  1. 如果不知道什么原因,tomcat服务器启动失败了,可以将Eclipse创建的Server删除,再重新创建一份(删除Server的同时,也将左侧的Servers项目从工作空间中删除)
  2. 在将tomcat和Eclipse整合之后,tomcat默认开启了热部署功能:在修改了代码后,不用重新发布,也不需要重启服务器,就可以运行最新的效果。(如果是创建了新的Servlet类,或者修改了web.xml文件,则需要重启服务器,才会生效)
2.2.5 创建Servlet3.0及以上版本的项目

在Servlet3.0的项目中,可以通过注解方式配置Servlet相关信息。

@WebServlet("/HelloWorld")
public class HelloWorld extends HttpServlet{...}
  • 在@WebServlet这个注解内部所配置的内容就是(xml方式)url-pattern中配置的访问路径。
  • 服务器通过扫描注解定位到当前这个Servlet,获取该类的全路径。
  • 通过全路径从硬盘上加载这个类到内存中,获取该类的字节码对象,再利用反射+字节码对象创建该类的实例,然后通过HelloWorld类的对象实例再调用其中的方法。

案例:

@WebServlet("/HelloWorld")
public class HelloWorld extends HttpServlet {private static final long serialVersionUID = 1L;  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {response.setContentType( "text/html;charset=utf-8" );response.getWriter().write( "世界, 你好!" ); }protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}
}

六、Mybatis

1 概述

1.1 简介

MyBatis 本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。

Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。

总之,Mybatis对JDBC访问数据库的过程进行了封装,简化了JDBC代码,解决JDBC将结果集封装为Java对象的麻烦。

1.2 架构

  • mybatis-config.xml是Mybatis的核心配置文件,通过其中的配置可以生成SqlSessionFactory,也就是SqlSession工厂
  • 基于SqlSessionFactory可以生成SqlSession对象
  • SqlSession是一个既可以发送SQL去执行,并返回结果,类似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。
  • Executor是SqlSession底层的对象,用于执行SQL语句
  • MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数),以及做输出映射(即将SQL查询的结果映射成相应的结果)

1.3 优点

  • Mybatis对JDBC对了封装,可以简化JDBC代码;
  • Mybatis自身支持连接池(也可以配置其他的连接池),因此可以提高程序的效率;
  • Mybatis是将SQL配置在mapper文件中,修改SQL只是修改配置文件,类不需要重新编译。
  • 对查询SQL执行后返回的ResultSet对象,Mybatis会帮我们处理,转换成Java对象。

总之,JDBC中所有的问题(代码繁琐、有太多重复代码、需要操作太多对象、释放资源、对结果的处理太麻烦等),在Mybatis框架中几乎都得到了解决。

1.4 运行原理

2 Mybatis简单使用案例

2.1 添加依赖

<dependencies>
<!-- junit单元测试 --><dependency><groupId>junit</groupId>      <artifactId>junit</artifactId>      <version>4.9</version>  </dependency> <!-- mysql驱动 -->  <dependency>        <groupId>mysql</groupId>        <artifactId>mysql-connector-java</artifactId>        <version>8.0.11</version>    </dependency>    <!-- mybatis -->    <dependency>        <groupId>org.mybatis</groupId>        <artifactId>mybatis</artifactId>        <version>3.2.8</version>    </dependency>    <!-- 整合log4j -->    <dependency>        <groupId>org.slf4j</groupId>        <artifactId>slf4j-log4j12</artifactId>      <version>1.6.4</version>    </dependency>
</dependencies>

2.2 xml文件配置

resource下创建mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- MyBatis的全局配置文件 -->
<configuration><!-- 1.配置开发环境 -->   <!-- default中写的是environment的id,写哪个使用哪个 --><environments default="dev"><environment id="dev"><!--1.1.配置事务管理方式Mybatis会自动开启事务,但需手动提交,否则会回滚JDBC:将事务交给JDBC管理(每条SQL都作为一个事务)MANAGED:自己手动管理事务--><transactionManager type="JDBC"></transactionManager><!--1.2.配置连接池信息, type的取值:JNDI:已过时UNPOOLED:不使用连接池POOLED:使用连接池(可以减少连接创建次数,提高执行效率)--><dataSource type="POOLED"><!--此处的name是固定值,不能随便写,spring-boot整合后会改变这里所有的引号内部不能换行,否则会读入换行符和空格xml文件中的&是转义字符,所以必须使用&amp;--><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql:///yonghedb?characterEncoding=utf-8&amp;serverTimezone=Asia/Shanghai"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment></environments><!--2.导入XxxMapper.xml文件(如果mapper文件有多个,可以通过多个mapper标签导入)resource属性会直接到类目录(classes)下去找指定位置的文件--><mappers><!-- </> 自闭标签 --><mapper resource="EmpMapper.xml"/></mappers>
</configuration>

EmpMapper

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace: 用于标识当前这个mapper文件(就是一个名字) 在mybatis程序中需要通过这个名字来定位当前这个mapper文件通过namespace值+id值可以定位要执行的是哪条SQL语句--><mapper namespace="EmpMapper"><!--通过select,insert,update,delete标签来存放要执行的SQLid属性:要求当前这个文件中的id值必须是独一无二的(不能重复)resultType属性: 指定查询的结果要存放在哪个类型的对象中--><select id="findAll" resultType="cn.tedu.pojo.Emp">select * from emp</select>
</mapper>

2.3 实体类

为了封装员工信息而提供的Emp类,我们称之为实体类。

如果要查询所有的员工信息,员工信息查询出来后需要封装到Java对象中,因此这里需要提供的Emp(员工)类,这个类用于封装所有的员工信息。

pojo(plain old/ordinary java object): 简单java对象

如果一个类只是用来封装数据的(比如为了封装员工信息而提供的Emp类),我们称之为POJO类。通过该类生成的对象称之为POJO对象。

建议:

  • 实体类中的数据建议使用包装类。因为本类型的默认值(0,0.0,’’,false),而包装类的默认值是null,可以避免一些误会产生。
  • 在编写实体类时,如果要添加有参构造函数,建议将无参构造函数也带上。
emp表的列名和Emp对象的属性名必须相同!!!

mybatis通过反射根据emp表中的列名(empname) 生成一个set方法:setEmpname,去Emp对象中寻找这个setEmpname。

  • 如果有就调用这个方法将empname列中的值封装到Emp对象。
  • 如果Emp类中没有set方法,Mybatis就会根据列名(empname)到Emp对象中寻找是否有同名的属性。如果有,会通过暴力反射将emp表中empname列的值赋值给同名的属性。

或者也可以通过在sql查询语句中起别名,使得别名=Emp类的属性名即可。

2.4 运行Mybatis的Java类

1.读取Mybatis核心配置文件中的配置信息
mybatis-config.xml
2.基于上面读取的配置信息获取SqlSessionFactory对象(工厂)
3.打开与数据库的连接(即通过工厂对象获取SqlSession对象)
Connection
4.通过namespace+id找到并执行SQL语句,返回处理后的结果
EmpMapper.xml,DeptMapper.xml,UserMapper.xml…
处理后的结果:List
5.输出结果

//1.读取配置信息
InputStream in=Resources.getResourceAsStream("mybatis-config.xml");
//2.获取工厂对象(SqlSessionFactoryBuilder 工厂构造器)
SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(in);
//3.与数据库连接,获取SqlSession对象(true为自动提交事务)
SqlSession session=factory.openSession(true);
//4.通过namespace+id执行sql语句,返回处理后的结果
//EmpMapper.xml,List<Emp>
List<Emp> list=session.selectList("EmpMapper.findAll");
//5.输出结果
list.forEach(emp->System.out.println(emp));

2.5 mybatis程序中常见的问题

  1. Emp类中添加了有参构造函数,但没有添加无参构造函数。
    Mybatis底层会基于反射创建Emp类的对象实例,创建时使用的就是Emp类的无参构造函数,如果没有无参构造函数,就会报错:java.lang.NoSuchMethodException: cn.tedu.pojo.Emp.()
    经验总结:在写实体类时如果要添加有参构造函数,尽量将无参构造也加上。
  2. 每条SQL语句都有对应的 namespace+id,而这些信息是存放在一个map中:EmpMapper.findAll[key] : select * from emp[value]
    执行SQL语句时,namespace或id写错了,找不到要执行的SQL语句就会报错:Mapped Statements collection does not contain value for EmpMapper.finaAll
  3. 如果mapper文件中标签的id值重复了,将会报如下错误:Mapped Statements collection already contains value for EmpMapper.update
  4. 连接数据库的基本信息书写错误, 会导致连接不上数据库:DataSourceException: Unknown DataSource property: usename
    – 报了一个未知的数据源(连接池)属性: usename

3 Mybatis的占位符

3.1 启用log4j日志框架

Log4j是专门为Java语言提供的一个日志框架, 可以通过log4j打印程序中的日志信息。
Mybatis默认已经支持了Log4j框架,我们在Mybatis中使用Log4j时,只需要两步:a.导入log4j的jar包,b.导入log4j的配置文件(log4j.properties)即可,配置文件内容如下:

# 文件名必须叫log4j.properties
# Global logging configuration
# debug log4j的日志输出级别
# stdout=Console 输出到控制台/LOGFILE 输出到日志文件
log4j.rootLogger=DEBUG, Console
# Console output...
# log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# %5p输出的日志级别 $t输出当前线程的名字 %m日志的详细信息 %n换行
# log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%5p [%t] %d{yyyy-MM-dd hh:mm:ss} - %m%n
# log4j.appender.LOGFILE=org.apache.log4j.FileAppender
# log4j.appender.LOGFILE.file=./mylog.log
# log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
# log4j.appender.LOGFILE.layout.ConversionPattern=%5p [%t] %d{yyyy-MM-dd hh:mm:ss} - %m%n

3.2 Mybatis的占位符

Mybatis中有两种占位符,分别是:#{},${},其中最常用的就是#{}占位符

3.2.1 #{}占位符

#{}占位符其实就是JDBC中的问号(?)占位符,在mybatis底层会将 #{}占位符翻译成问号(?)占位符。
a.如果在SQL语句中占位符只有一个#{}占位符,{}中名称没有要求,但不能是空的;参数可以直接传递,不用封装。
b.如果在SQL语句中的#{}占位符不止一个,参数值需要通过Map或者POJO对象进行封装

  • 如果通过Map集合来封装SQL参数值,#{}占位符中的名称要和Map中的key保持一致。因为在mybatis底层是通过:把#{}占位符中的名称作为key,到map中获取对应的value。
  • 如果通过POJO对象来封装SQL参数值,#{}占位符中的名称要在POJO对象中有对应的getXxx方法,或者有对应的变量。例如:#{job}占位符中的名称为job,那么就意味着:在Emp中要有getJob()方法或者有job变量。如果两者都有,会优先通过getXxx方法来获取POJO对象中存储的属性值,如果没有getXxx方法,会通过暴力反射直接获取Emp中job变量的值。

总结:在Mybatis框架中,大部分情况都是用#{}占位符。#{}其实就是JDBC中的问号(?)占位符,是为SQL语句中的参数值进行占位。例如:

select * from emp where job=参数值 and salary>参数值
insert into emp value(null,参数值,参数值,参数值)
update emp set 列=参数值,列=参数值,... where 列=参数值...
delete from emp where 列=参数值...
3.2.2 ${}占位符

${}占位符是为SQL语句中的某一个SQL片段进行占位,在参数传递过来时,直接将参数拼接在占位符所在的位置。因为是直接拼接,所以可能会引发SQL注入攻击,因此不推荐大量使用。

  • 如果SQL语句中只有一个#{}占位符,参数可以不用封装,直接传递即可。
  • 但如果SQL语句中哪怕只有一个${}占位符,参数也必须得先封装到Map或者POJO对象中,再把Map或者POJO对象传递过去。

Mybatis底层在执行SQL语句时,使用的就是PreparedStatement对象来传输SQL语句。

3.3 案例

3.3.1 #{}
select #{colStr} from emp

colStr传入id,name,job,会自动封装为:

select `id,name,job` from emp

可以查到8行’id,name,job’,但无法封装到Emp中,所以最后出来8个null

3.3.2 ${}
select ${colStr} from emp

colStr传入id,name,job,会自动封装为:

select id,name,job from emp

可以查到8行数据,并封装入Emp中。

4 Mybatis的动态SQL

if标签、where标签、foreach标签

动态SQL指的是:SQL语句可以根据参数值的不同,来生成不同的SQL语句

4.1 if标签

如果test属性中的布尔表达式为true,那if标签中的SQL片段就会参与整个SQL语句的执行。反之,如果test属性中的布尔表达式为false,那么if标签的中内容将不会执行。

格式:

<if test="布尔表达式"> SQL片段 </if>

案例:

<!-- 由于 < 在xml是特殊符号,所以需要转义为&lt;如果两个if都没有成立,则where就空了,所以要加一个1=1 -->
<select id="findBySal" resultType="cn.tedu.pojo.Emp">select * from empwhere 1=1 <if test="minSal!=null">and salary > #{minSal}</if><if test="maxSal!=null">and salary &lt; #{maxSal}</if>
</select>

4.2 where标签

将if标签以及其中的条件都包裹在where标签内部,只要有任何一个条件成立,where标签就会生成where关键字,并且如果条件前面有多余的连接词(比如or,and),where标签会将多余的连接词去掉。(替代上述案例中where 1=1的作用)

<select id="findBySal2" resultType="cn.tedu.pojo.Emp">select * from emp<where><if test="minSal != null">salary > #{minSal}</if><if test="maxSal != null">and salary &lt; #{maxSal}</if></where>
</select>

4.3 foreach标签

foreach标签可以对传过来的数组或集合进行遍历,生成我们所需要的SQL片段。

  • collection属性:

  • 如果传过来的只有一个数组(或List集合)->array(或list)

  • 如果传过来的是一个map集合(将数组或集合作为value封装到map中)->map中的key

  • open属性:指定所生成SQL片段的起始符号,通常是左圆括号“(”

  • close属性:指定所生成SQL片段的结束符号,通常是右圆括号“)”

  • item属性:指定占位符中的名称

  • separator属性:指定占位符中间的分隔符,通常是逗号“,”

<delete id="deleteByIds">delete from emp where id in    <foreach collection="array" open="(" item="id" separator="," close=")">    #{id}    </foreach>
</delete>
<!-- collection使用的是map中数组ids对应的key -->
<update id="updateByIds">update emp set salary=salary+#{money}    where id in    <foreach collection="arrayIds" open="(" item="id" separator="," close=")">#{id}</foreach>
</update>

5 Mybatis的Mapper接口开发

5.1 四个规则

mapper接口开发要满足以下四个规则:

  1. 写一个接口,要求接口的全类名(包名.接口名) 要等于 XxxMapper文件的namespace值
    namespace = 接口的全类名
  2. mapper文件中要执行的SQL语句,在接口中得有对应的接口方法,并且,SQL语句的id值要等于接口的这个方法名
    SQL标签的id值 = 方法名
  3. 如果是查询SQL, resultType指定的类型要和接口方法的返回值类型对应
    • 如果接口返回的是某一个实体对象(Emp),此时resultType指定的是该对象的类型(cn.tedu.pojo.Emp)
    • 如果接口返回的是集合(List),此时resultType指定的是集合中的泛型(cn.tedu.pojo.Emp)
  4. (可以忽略) SQL标签上的参数类型(paramterType属性)要和接口方法的参数类型保持一致

5.2 使用方法

5.2.1 EmpMapper接口
//接口的全类名: cn.tedu.dao.EmpMapperpublic interface
EmpMapper {public List<Emp> findAll(); //由于查询的emp信息结果可能不止一条,所以返回的结果需要使用List集合进行封装
}
5.2.2 EmpMapper.xml
  1. 修改namespace的值为EmpMapper的全类名

    <mapper namespace="cn.tedu.dao.EmpMapper">
    
  2. 添加要执行的SQL语句的id值 要和接口中的方法名保持一致

    <select id="findAll" resultType="cn.tedu.pojo.Emp">select * from emp
    </select>
    
  3. resultType指定的类型要和接口方法的返回值类型对应

  4. SQL语句中没有占位符就不需要传参数,因此接口方法也就不需要参数;如果有则都必须得有。

5.2.3 测试
@Testpublic void findAll(){//1.获取EmpMapper接口的子类的对象实例(Mybatis提供的代理类)    EmpMapper mapper=session.getMapper(EmpMapper.class);    //2.执行sql    List<Emp> list = mapper.findAll();    list.forEach(emp -> System.out.println(emp));
}
5.2.4 模拟实现类
public class EmpMapperImpl implements EmpMapper{private SqlSession session;//将session对象通过构造方法保存到类的内部    public EmpMapperImpl(SqlSession session) { this.session = session; }    // 查询所有的员工信息    public List<Emp> findAll() {        //1.获取当前这个类的父接口的全类名(=namespace)        String interName=this.getClass().getInterfaces()[0].getName();        //2.获取存放的方法调用栈信息(也就是所有方法的调用信息)        StackTraceElement[] st=Thread.currentThread().getStackTrace();        //3.获取当前调用的方法的名字(=SQL标签的id值)        String methodName=st[1].getMethodName();        //4.调用findAll方法        List<Emp> list=session.selectList(interName+"."+methodName);        return list;    }
}
5.2.5 测试实现类
@Testpublic void findAll2(){    //1.获取EmpMapperImpl的对象实例    EmpMapper mapper=new EmpMapperImpl(session);    //2.执行sql    List<Emp> list = mapper.findAll();    list.forEach(emp -> System.out.println(emp));}

5.3 原理

6 mybatis的注解开发

6.1 使用xml方式和注解开发的区别

  • 使用xml方式配置SQL语句,写起来相比注解要麻烦一些;而注解方式不用写配置文件,直接在接口的方法上面添加一个注解(@Select,@Insert,@Update,@Delete…),将SQL语句直接写在注解的括号里即可。
  • 使用注解方式配置SQL语句又回到将SQL语句写在Java程序中。如果将来SQL语句一旦发生变化,就意味着要修改java源文件(.java),改完后要重新编译整个项目,再打包,部署到服务器上,相比来说较为麻烦。

6.2 使用方法

6.2.1 mybatis-config.xml

由于使用注解开发,要将SQL语句配置在注解中,因此EmpMapper.xml文件也就不需要了,在mybatis-config.xml文件中也就不需要引入EmpMapper.xml文件了。在mybatis-config.xml文件中引入SQL语句所在的mapper接口(因为SQL语句存放在接口中):

<mappers><!-- 原方法: --><!-- <mapper resource="EmpMapper.xml"/> --><!-- 1.可以将每一个Java文件中的XxxMapper接口通过一个mapper标签的class属性,用[全类名]引入;如果有多个XxxMapper接口,添加多个mapper标签引入即可 -->    <mapper class="cn.tedu.dao.EmpMapper"/>    <!-- 2.或者是直接引入XxxMapper接口所在的包, 这样mybais会直接扫描这个包下的所有接口中的每个方法, 那么这些方法上所配置的SQL语句也会被读取到mybatis中 --><package name="cn.tedu.dao"/>
</mappers>
6.2.2 EmpMapper.java接口
public interface EmpMapper { @Select("select id,name,job from emp")   public List<Emp> findAll();   @Insert("insert into emp value(null,'赵云云','高级Java工程师',35000)")   public void insert();   @Insert("insert into emp value(null,#{name},#{job},#{salary})")  public void insert2(Map map);   @Update("update emp set job=#{job},salary=#{salary} where name=#{name}")  public void update2(Emp emp);
}
6.2.3 测试

由于这里只是将SQL语句从XxxMapper.xml文件中移动到了XxxMapper接口中,所以测试代码直接使用之前的即可。

7 Junit

单元测试框架(junit):这个框架可以在不提供main函数,并且不创建对象的情况下,直接运行一个非静态的方法。

a.@Test注解可以标记一个非静态的方法,在不添加main函数并且不创建对象的情况下直接运行这个方法,但被@Test标记的方法必须符合以下要求:

  1. 方法必须是公共的(pubilc), 不能是私有的(private)

  2. 方法必须是非静态的(no static)

  3. 方法必须是无返回值的(no return)

  4. 方法必须是无参数的(no param)

如果违反以上任何一个规则, 就会报如下异常:
java.lang.Exception: No tests found matching [{ExactMatcher:…

b.@Before:在每个@Test方法之前执行。

七、date

1 日期类Date

1.1 概述

Java提供了Date类来处理日期、时间,Date对象既包含日期,也包含时间。该类从JDK 1.0起就开始存在了,但正因为它历史悠久,所以它的大部分构造器、方法都已经过时,不再推荐使用了。

过时(Deprecated)是指:Java不再推荐使用,如果非要使用不再推荐的构造器或方法,编译器会提出警告信息,并可能会导致程序性能、安全性等方面的问题 。

1.2 创建对象

Date类提供了6个构造器,其中4个已经过时,剩下两个:

Date() //无参构造,使用当前日期和时间来初始化Date对象,即Date对象包含的是当前日期时间.底层调用System.currentTimeMillis()获得long整数作为日期参数.
Date(long milliSec) //接收从1970年1月1日0:0:0 GMT起的毫秒数来初始化Date对象.即Date包含的是:1970年1月1日0:0:0加上毫秒数后的日期时间.//其他构造方法已过时.

开发时使用Date类要导包,注意是java.util.Date,不是java.sql.Date。

创建日期对象示例:

Date d=new Date(); //创建包含当前日期时间的Date对象
Date d2=new Date(1000*60*60); //创建包含指定毫秒值的Date对象

1.3 常用方法

  • long getTime() 返回从1970年1月1日 00:00:00 GMT 到该Date对象之间的时间差,以毫秒作为单位
  • void setTime(long milliSec) 设置此Date对象从1970年1月1日00:00:00 GMT开始的的毫秒值。
  • int compareTo(Date anotherDate) 比较当前日期时间和参数中表示的日期时间的先后顺序
    • 如果 [当前日期] 比 [参数中的日期] 早,返回 -1
    • 如果 [当前日期] 比 [参数中的日期] 晚,返回 1
    • 如果 [当前日期] 和 [参数中的日期] 相同,返回 0
  • int getYear() 获取此Date对象中包含的年份,已过时,不建议使用
  • int getMonth() 获取此Date对象中包含的月份,已过时,不建议使用
  • int getHours() 获取此Date对象中包含的月份,已过时,不建议使用
//测试setTime和getTime方法
Date d3=new Date();System.out.println(d3); //Thu Mar 18 16:43:50 CST 2021
System.out.println(d3.getTime()); //1616057030304d3.setTime(1000*60*60);
System.out.println(d3); //Thu Jan 01 09:00:00 CST 1970
System.out.println(d3.getTime()); //3600000
//测试compareTo方法
Date d4 = new Date(1000*60*60);
Date d5 = new Date(2000*60*60);
System.out.println(d4.compareTo(d5)); //-1
System.out.println(d5.compareTo(d4)); //1
System.out.println(d5.compareTo(d5)); //0
//下面过时的方法直接忽略即可,不需要掌握
System.out.println(d4.getYear()); //70,年份后两位
System.out.println(d4.getMonth()); //0,月份从1开始
System.out.println(d4.getHours()); //10

2 SimpleDateFormat

SimpleDateFormat是一个用于日期解析与格式化的类。它可以将Date对象格式化成字符串,也可以将日期字符串解析成Date对象。

2.1 创建对象

SimpleDateFormat() 用默认的模式和默认语言环境的日期格式符号构造SimpleDateFormat
SimpleDateFormat(String pattern) 用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat

//创建默认格式和符号的SimpleDateFormat实例SimpleDateFormat sdf=new SimpleDateFormat();//创建指定格式和符号的SimpleDateFormat实例SimpleDateFormat sdf2=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

其中 “yyyy-MM-dd HH:mm:ss” 是自定义的日期时间转换格式:

y: 表示年份,yyyy则表示年份为4位,如: 2020年
M: 表示月份,MM则表示月份为两位,如: 03月
d: 一个月中的日期,dd则表示天数为两位,如:21号
H: 一天中的小时, H是24小时制, h是12小时制
m: 分钟数
s: 秒数
S: 毫秒数
E: 星期几

2.2 常用方法

  • String format(Date date) 按照一定的模式,将一个Date对象格式化为日期/时间字符串
  • Date parse(String source) 按照一定的模式,将一个字符串解析成一个Date对象
//1.将当前日期时间按下面的格式, 转成一个字符串
SimpleDateFormat sdf3 =new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date d2 = new Date();
System.out.println(sdf3.format(d2)); //2021/03/18 23:48:40
//2.将下面的日期时间字符串, 解析成一个Date对象
String dateStr="2020年12月24日 13时25分39秒";
SimpleDateFormat sdf4=new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
System.out.println(sdf4.parse(dateStr)); //Thu Dec 24 13:25:39 CST 2020

2.3 案例:计算存活时间

根据用户输入的出生日期,计算用户的存活时间,实现思路:

  1. 定义日期格式化对象(比如时间格式为:yyyy/MM/dd)
  2. 提示用户按照指定的格式输入出生年月日,并接收
  3. 将该用户输入的年月日字符串 转成Date对象
  4. 获取该用户从 1970年1月1日 到 用户出生时 的时间毫秒值
  5. 获取该用户从 1970年1月1日 到 此时此刻 的时间毫秒值
  6. 计算该用户一共所存活的时间毫秒值(begin-end)
  7. 计算用户存活的时间
//1.定义日期格式化对象
String pattern="yyyy/MM/dd";SimpleDateFormat sdf=new SimpleDateFormat(pattern);
//2.提示用户按照指定的格式输入出生年月日,并接收
System.out.println("请输入您的出生年月日,以"+pattern+"隔开:");
String birthStr=new Scanner(System.in).nextLine();
//3.将该用户输入的年月日字符串 转成Date对象Date
birth=sdf.parse(birthStr);
//4.获取该用户从 1970年1月1日 到 用户出生时 的时间毫秒值
long begin=birth.getTime();
//5.获取该用户从 1970年1月1日 到 此时此刻 的时间毫秒值
long now=System.currentTimeMillis();
//6.计算该用户一共所存活的时间毫秒值
long total=now-begin;
//7.1 计算该用户一共活了多少年
int year=(int)(total/(1000L*60*60*24*365));
//7.2 计算该用户一共活了多少天
int day=(int)(total/(1000L*60*60*24));
//7.3 计算该用户一共活了多少小时
int hour=(int)(total/(1000*60*60));
//7.4 计算该用户一共活了多少分钟
int minute=(int)(total/(1000*60));

CGB2102Web总结相关推荐

最新文章

  1. Oracle事务和常用数据库对象
  2. ffmpeg连接超时与解码超时
  3. Java程序员面试宝典--this
  4. python的unicode_python的unicode及其编码解码
  5. tomcat开启SSL8443端口的方法
  6. mac版本查看日志命令
  7. AtCoder Regular Contest 059
  8. 太形象了!什么是边缘计算?最有趣的解释没有之一!
  9. Linux系统编程:fork函数的使用【循环创建N个子线程】
  10. 数据写入磁盘的过程,咔咔的!
  11. linux批量筛选序列变异位点,使用bedtools获取指定坐标上下游的序列
  12. 接口可以继承多个接口总结
  13. java初学者必看的学习路线
  14. Ubuntu 14.04/16.04 与 Windows 10 周年版 Ubuntu Bash 性能对比
  15. OA审批流程是什么?如何提升OA审批流程效率?
  16. 谁的用户在世界上是#160;#160;明基决心保时捷设计标准
  17. redhat linux光盘4,技巧:把3张Redhat Linux 9的安装光盘刻录到一张DVD光盘中
  18. 2020-07-03:有1亿个数字,其中有2个是重复的,快速找到它,时间和空间要最优
  19. 学习笔记之——汉明码(Hamming Code)
  20. 关于Could not find QtWebEngineProcess.exe 进程已结束,退出代码 -1073740791 (0xC0000409) 问题

热门文章

  1. endnote插入文献后没有生成域代码也没有生成参考文献列表
  2. 公司想申请软著投标加分,没有源代码也可以申请
  3. Introduction into ISO 27145 WWH-OBD
  4. Kotlin学习与实践 (一)WWH (what?why?how)
  5. javascript爬虫
  6. 阿里云被工信部暂停合作,惹事的Log4j2漏洞该如何解决?
  7. 在农村创业有哪些优势和机会?
  8. 使用iText导出word 可自由设置为横页和纵页
  9. Python模拟手机微博登录
  10. nvme固态必须uefi启动吗_听说这招能在3秒内启动Windows10,很多高手都这么做,你知道吗?...