MYSQL
一. DDL语句:数据库定义语句
库/表的增删查改
二. 查询当前mysql下所有的数据库
mysql8.0 自带的 跟5.5自带的不一样
mysql > show databases;
+--------------------+
| Database |
+--------------------+
| information_schema | mysql默认的一些配置
| mysql | mysql库里面包含user表用户表 (权限用户)
| performance_schema | mysql性能相关的库
| sakila | 提供其他三个库 sakila sys world 提供了一些例库 练习sql语句
| sys |
| world |
| zy | 自定义的库(开发者自定义)
+--------------------+
7 rows in set (0.00 sec)
1.查看创建数据库的默认的字符集(了解)
show create database 库名;
mysql> show create database mydb_01;
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| Database | Create Database |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
| mydb_01 | CREATE DATABASE `mydb_01` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */ /*!80016 DEFAULT ENCRYPTION='N' */ |
+----------+-----------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
2. 修改数据库的字符集(了解)
alter database 库名 (default)可以省略 character set=字符集;
mysql> alter database mydb_01 default character set gbk ;
Query OK, 1 row affected (0.01 sec)
mysql> show create database mydb_01;
+----------+----------------------------------------------------------------------------------------------------+
| Database | Create Database |
+----------+----------------------------------------------------------------------------------------------------+
| mydb_01 | CREATE DATABASE `mydb_01` /*!40100 DEFAULT CHARACTER SET gbk */ /*!80016 DEFAULT ENCRYPTION='N' */ |
+----------+----------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql>
三. 创建数据库
create database 库名 ;
create database if not exists 库名;
方式一 :
create database 库名 ;
mysql> create database mydb_01;
Query OK, 1 row affected (0.02 sec)
方式二 :
create database if not exists 库名;
mysql> create database if not exists mydb_02;
Query OK, 1 row affected (0.02 sec)
四. 删除数据库
drop database 库名;
drop database if exists 库名;
mysql> drop database mydb_02;
Query OK, 0 rows affected (0.02 sec)
mysql> drop database if exists mydb_03;
Query OK, 0 rows affected (0.01 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb_01 |
| mysql |
| performance_schema |
| sakila |
| sys |
| world |
| zy |
+--------------------+
五. 模糊查询 mysql 服务中所有的带character字符集的全局变量
show variables like ‘%character%’ ;
mysql> show variables like '%character%' ;
+--------------------------+---------------------------------------------------------+
| Variable_name | Value |
+--------------------------+---------------------------------------------------------+
| character_set_client | gbk |
| character_set_connection | gbk |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | gbk |
| character_set_server | utf8mb4 |
| character_set_system | utf8mb3 |
| character_sets_dir | C:\Program Files\MySQL\MySQL Server 8.0\share\charsets\ |
+--------------------------+---------------------------------------------------------+
8 rows in set, 1 warning (0.01 sec)
如果出现中文乱码:
character_set_client 客户端 utf8
character_set_results 结果集 utf8
改动字符集:临时解决中文乱码
mysql> set character_set_client=utf8;
Query OK, 0 rows affected, 1 warning (0.01 sec)
mysql> set character_set_results=utf8;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> show variables like '%character%' ;
+--------------------------+---------------------------------------------------------+
| Variable_name | Value |
+--------------------------+---------------------------------------------------------+
| character_set_client | utf8mb3 |
| character_set_connection | gbk |
| character_set_database | ut
f8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8mb3 |
| character_set_server | utf8mb4 |
| character_set_system | utf8mb3 |
| character_sets_dir | C:\Program Files\MySQL\MySQL Server 8.0\share\charsets\ |
+--------------------------+---------------------------------------------------------+
8 rows in set, 1 warning (0.00 sec)
六. 创建表
create table 表明(
列名1 字段类型1,
列名2 字段类型2,
...
列名n 字段类型n
) ;
数据库常用的字段类型
int------>默认int(11) 取的是当前实际长度 (推荐)
id int,---编号 1
int(长度):指定长度
id int(3) 1---->001
varchar(字符长度):字符串类型数据 '' "" 不写 引号 ,习惯去使用''或者双引号
double(值1,值2):小数类型
举例
double(3,2) ----小数类型,3位数,小数点后保留2位
123.56
日期类型
date 仅仅表示日期,不表示时分秒
"2021-8-12"
datatime 不仅表示日期具体的时间
"2021-8-12 17:31:00"
timestap:时间戳
给表中插入数据, 显示当前插入/修改/删除/查询数据那一刻时间 (具体到秒"2021-8-12 18:00:01")
注意事项:就是给那一个库中创建表 使用哪个库(选择数据库名)
use 库名;
mysql> use mydb_01;
Database changed
mysql>
mysql> create table student(
-> id int,
-> name varchar(20),
-> gender varchar(10),
-> address varchar(50),
-> age int
-> )
-> ;
Query OK, 0 rows affected (0.05 sec)
七. 查询当前数据库中有哪些表
show tables;
mysql> show tables;
+-------------------+
| Tables_in_mydb_01 |
+-------------------+
| student |
+-------------------+
1 row in set (0.01 sec)
八. 查询当前表的结构
desc student;
mysql> desc student;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| gender | varchar(10) | YES | | NULL | |
| address | varchar(50) | YES | | NULL | |
| age | int | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
5 rows in set (0.01 sec)
九. 修改表
1. 给表添加一列 :
alter table 表名 add 字段名称 字段类型;
mysql> alter table student add email varchar(50) ;
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| gender | varchar(10) | YES | | NULL | |
| address | varchar(50) | YES | | NULL | |
| age | int | YES | | NULL | |
| email | varchar(50) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
6 rows in set (0.01 sec)
2. 修改表中的字段类型
alter table 表名 modify 字段名称 更改后的字段类型;
mysql> alter table student modify address varchar(30) ;
Query OK, 0 rows affected (0.07 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| gender | varchar(10) | YES | | NULL | |
| address | varchar(30) | YES | | NULL | |
| age | int | YES | | NULL | |
| email | varchar(50) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
3. 修改表中的字段名称
alter table 表名 change 旧列名 新列名 字段类型;
mysql> alter table student change gender sex varchar(10) ;
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| sex | varchar(10) | YES | | NULL | |
| address | varchar(30) | YES | | NULL | |
| age | int | YES | | NULL | |
| email | varchar(50) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
6 rows in set (0.01 sec)
4. 删除某一列字段
alter table 表名 drop 字段名称 ;
mysql> alter table student drop email;
Query OK, 0 rows affected (0.06 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc student;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| sex | varchar(10) | YES | | NULL | |
| address | varchar(30) | YES | | NULL | |
| age | int | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
5 rows in set (0.01 sec)
5. 修改表的表名
alter table 表名 rename to 新表名 ;
mysql> alter table student rename to stu;
Query OK, 0 rows affected (0.03 sec)
mysql> show tables;
+-------------------+
| Tables_in_mydb_01 |
+-------------------+
| stu |
| teacher |
+-------------------+
2 rows in set (0.01 sec)
6. 复制一张表,结构和以前表的结构一样
create table 新表名 like 旧表名 ;
mysql> create table teacher like student; //字段复制
Query OK, 0 rows affected (0.03 sec)
mysql> show tables;
+-------------------+
| Tables_in_mydb_01 |
+-------------------+
| student |
| teacher |
+-------------------+
2 rows in set (0.01 sec)
7 . 删除表
**drop table 表名; **
drop table if exists 表名;
mysql> drop table if exists teacher;
Query OK, 0 rows affected (0.03 sec)
mysql> show tables;
+-------------------+
| Tables_in_mydb_01 |
+-------------------+
| stu |
+-------------------+
1 row in set (0.00 sec)
十. DML 语句: 数据库操作语句,操作表记录
1. 插入语句
insert into 表名 values(值1,值2,值3…) ;
插入全表数据 :那么每一个参数值必须和字段类型匹配! (不会报错,警告)
mysql> insert into stu values(1,'高圆圆','女','西安市',30) ;
Query OK, 1 row affected (0.01 sec)
插入部分字段 :
INSERT INTO stu(NAME,sex,address) VALUES('王宝强','男','咸阳市') ;
-- 注意:插入部分字段,必须字段数量和插入值的数量必须匹配
-- insert into stu(id,name,sex) values(6,'姚笛') ;
-- 不管插入部分字段还全部字段,那么必须要和当前类型一致
INSERT INTO
stu
VALUES(6,'男','马嘉钰','西安市',20) ;
2. 查询全表数据
select * from 表名 ;
mysql> insert into stu values(1,'高圆圆','女','西安市',30) ;
Query OK, 1 row affected (0.01 sec)
mysql> select * from stu ;
+------+--------+------+---------+------+
| id | name | sex | address | age |
+------+--------+------+---------+------+
| 1 | 高圆圆 | 女 | 西安市 | 30 |
+------+--------+------+---------+------+
1 row in set (0.01 sec)
3. 修改表的数据 update
语法:按条件修改 update 表名 set 字段名称 = 值 where 字段名称= 值; 修改一个字段
-- 修改name是王宝强的将他的id设置为7
UPDATE stu SET id = 7 WHERE NAME = '王宝强' ;
-- 修改id为7的age设置为37
UPDATE stu SET age = 37 WHERE id = 7 ;
4. 一次修改多个字段
-- update 表名 set 字段名称1 =值1 ,字段名称2 =值2 where 条件;
-- 将id为的6的姓名修改为 '张三丰' ,性别修改为'男'
UPDATE
stu
SET
NAME = '张三丰',
sex = '男'
WHERE
id = 6 ;
-- 如果不带条件修改:就是批量修改
-- 将修改name为 '高圆圆'
UPDATE stu SET NAME = '高圆圆' ; -- 一般都是带条件进行修改!
5. 删除
-- 带条件来删除
-- delete from 表名 where 字段名称 = 值; //删除指定的记录
-- 删除id = 6的这条记录
DELETE FROM stu WHERE id = 6 ;
-- 删除全表数据
-- delete from 表名;
DELETE FROM stu ;
-- truncate table 表名;
TRUNCATE TABLE stu ;
/*
删除全表的语法
delete from 表名
truncate table 表名; 两个区别
1)delete from 表名:只是删除全表数据;表的结构还存在,
如果表中 存在主键并且自增长约束,那么不会受影响,下一次在插入数据
继续在之前的基础上继续自增长!
2)truncate table 表名 ;
将表中数据删除的同时删除了表,然后在创建一张一模一样空表,
肯定影响自增长主键的值,再次插入数据,自增长从1开始...
等价于
drop table my_use;
创建一个当前一模一样的表结构
*/
-- 创建一张新的表 my_user表
CREATE TABLE my_user(
id INT PRIMARY KEY AUTO_INCREMENT, -- 用户编号 主键加增长
NAME VARCHAR(20),
age INT ,
gender VARCHAR(5)
) ;
INSERT INTO my_user(NAME ,age ,gender)
VALUES('张三',20,'男'),('李四',22,'女'),('文章',25,'男') ;
INSERT INTO my_user(NAME,age,gender) VALUES('王五',18,'男') ;
-- delete from my_user where id = 8 ;
-- 删除全表数据
DELETE FROM my_user ;
-- 删除全部数据
TRUNCATE TABLE my_user ;
SELECT * FROM my_user;
十一. DQL语句: 查询语句
1.查询全表数据
SELECT * FROM stu ;
-- 实际开发中,* (通配符),一般查询全表数据,需要带全部字段
SELECT -- 指定全部字段
id ,
NAME,
sex,
address,
age
FROM stu ;
2. 查询指定的字段
select 字段名称列表 from 表名;
SELECT
id ,
NAME ,
address,
age
FROM stu ;
-- 查询指定字段时可以通过 as 关键字指定别名, as可以省略
SELECT
id AS '学生编号',
NAME AS '姓名',
address AS '地址',
age AS '年龄'
FROM stu ;
-- as 可以省略
SELECT
id '学生编号',
NAME '姓名',
address '地址',
age '年龄'
FROM stu ;
3. 字段去重
delete distinct
4. 数据库常用的字段类型
int------>默认int(11) 取的是当前实际长度 (推荐)
id int,---编号 1
int(长度):指定长度
id int(3) 1---->001
varchar:字符串类型数据 '' "" 不写 引号 ,习惯去使用''或者双引号
double(值1,值2):-- 小数类型
举例
double(3,2) ----小数类型,3位数,小数点后保留2位
123.56
-- 日期类型
date 仅仅表示日期,不表示时分秒
"2021-8-12"
datatime 不仅表示日期具体的时间
"2021-8-12 17:31:00"
timestap:时间戳
给表中插入数据, 显示当前插入/修改/删除/查询数据那一刻时间 (具体到秒"2021-8-12 18:00:01")
注意事项:就是给那一个库中创建表
5. DQL带条件查询
条件查询 (where)
可以基本运算符:比较运算符(<,>,<=,>=,!=)/逻辑运算符(|| && /and /or)/赋值运算符 =
-- where后面多个or in(集合数据) 在两个值之间 between 值1 and 值2
-- mysql 判断某个字段为null , is null /is not null
-- 模糊查询 like
-- 聚合函数 count(列名)/max(列名)/min(列名)/sum(列名)/avg(列名)
-- 排序查询 order by
-- 分组查询 group by
-- 筛选查询 having
-- 分页查询limit
-- 创建一个学生表
CREATE TABLE student (
id INT, -- 编号
NAME VARCHAR(20), -- 姓名
age INT, -- 年龄
sex VARCHAR(5), -- 性别
address VARCHAR(100), -- 地址
math INT, -- 数学
english INT -- 英语
);
DESC student ;
INSERT INTO student(id,NAME,age,sex,address,math,english) VALUES
(1,'马云',55,'男',' 杭州',66,78),
(2,'马化腾',45,'女','深圳',98,87),
(3,'马景涛',55,'男','香港',56,77),
(4,'柳岩 ',20,'女','湖南',76,65),
(5,'柳青',20,'男','湖南',86,NULL),
(6,'刘德华',57,'男','香港 ',99,99),
(7,'马德',22,'女','香港',99,99),
(8,'德玛西亚',18,'男','南京',56,65);
-- 查询年龄大于20岁的人的全部信息
SELECT
id '编号',
NAME '姓名',
age '年龄',
sex '性别',
address '地址',
math '数学成绩',
english '英语成绩'
FROM
student
WHERE
age > 20 ;
-- 查询年龄在20岁和30岁之间的学生信息
SELECT
*
FROM
student
WHERE
age >= 20 && age <=30 ; -- &&:Java的逻辑运算符
-- 另一种语法
SELECT
*
FROM
student
WHERE
age >= 20 AND age <=30 ;-- and mysql表示并列关系
-- 另一种语法:mysql 在两个值之间 between 值1 and 值2
SELECT -- 查询部分字段 并且年龄在20-30
NAME '姓名',
age '年龄',
address '地址',
math '数学成绩',
english '英语成绩'
FROM
student
WHERE
age
BETWEEN 20 AND 30 ;
-- 查询年龄是18或者20或者30岁的学生的编号,姓名,年龄以及地址 (|| 或者 or)
SELECT
id '编号',
NAME '姓名',
age '年龄',
address '地址'
FROM
student
WHERE
age = 18 OR age = 20 OR age = 30 ;
-- mysql另一种语法 where 字段名称 in(多个值)
SELECT
id '编号',
NAME '姓名',
age '年龄',
address '地址'
FROM
student
WHERE
age
IN(18,20,30) ;
-- 查询英语成绩为null的学号编号,姓名,性别,地址.数学成绩信息
/*
select
id ,
name,
sex,
address,
math
from
student
where
english == null ; -- java中的用法可以这样用 == = (都不行)
*/
SELECT
id ,
NAME,
sex,
address,
math,
english
FROM
student
WHERE
english IS NULL ; -- mysql用法
-- 查询英语成绩不为null的人 ,is not null 的用法
SELECT
id ,
NAME,
sex,
address,
math,
english
FROM
student
WHERE
english IS NOT NULL ;
-- 查询英语和数学成绩总分的学生信息
SELECT
id 编号 ,
NAME 姓名 ,
sex 性别 ,
address '地址',
-- (math+english) as '总分'
(math+IFNULL(english,0)) '总分'
FROM
student ;
-- mysql 内置函数ifnull(值1,值2) ; ifnull(english,0) :
-- 如果当前英语成绩为null,给默认值0
-- 查询地址
SELECT
address 地址
FROM student ;
-- 查询字段,对字段去重 (distinct)
SELECT
DISTINCT address 地址
FROM student ;
-- 查询年龄不是20岁的学生信息
SELECT
*
FROM student
WHERE age != 20 ; -- != Java中这种语法 !=
-- mysql中的不等于 <>
SELECT
*
FROM
student
WHERE
age <> 20 ;
6. 模糊查询(like)
mysql服务中带字符集相关的变量 show variables like '%character%' ;
-- 模糊查询 like
-- select 字段列表 from 表名 where 字段名称 like '%字符%' ;
/*
% :包含的指定的字符 使用'%字符值%' 模糊查询包含指定字符的信息
_ :代表单个字符(一个_下划线代表一个字符)
两个相结合使用: '_%字符值%_' 三个字符:中间字符包含指定的值进行模糊查询
*/
-- 查询当前学生表中姓名包含马的学生信息
SELECT
*
FROM
student
WHERE
NAME
LIKE
'%马%' ;
-- 查询第二个字符包含化的学生信息
SELECT
*
FROM
student
WHERE
NAME
LIKE
'_%化%' ;
-- 查询姓名是三个字符的人
SELECT
*
FROM
student
WHERE
NAME
LIKE
'___' ;
-- 应用场景: 搜索框中输入关键字查询---使用到模糊查询
7. 聚合函数查询(count,max,min,sum,avg)
查询结果:单行单列的数据
-- count(列名) :总记录数
-- max(列名): 最大值
-- min(列名字段):最小值
-- sum(字段名称):求和
-- avg(列名):平均分
-- select 聚合函数(列名) from 表名;
-- 查询当前student这个表的全部总记录数
-- 如果使用english字段查询总记录数
SELECT
COUNT(english) -- 使用业务字段查询(可能某个值为null,不会进行记录)
FROM
student ; -- 7条记录
-- 可以使用函数设置如果当前某个值null,给默认值
SELECT
COUNT(IFNULL(english,0)) 总记录数
FROM
student;
/*
count(列名)查询总记录数的时候,一般都使用非业务字段查询
student
id(非业务字段) name age gender (业务字段)
学校里面的学生表
某个学生刚开始还在学习, 后面转学了 (非业务字段id---主键+自增长的)
1 张三 20 男
select
count(id)
from 表名;
*/
-- 建议id来查询
SELECT
COUNT(id) 总条数
FROM
student ;
-- 查询数学的平均成绩 avg(列名)
SELECT
AVG(math) '数学平均成绩'
FROM
student ; -- 79.5000
-- 查询英语成绩---总成绩(求和 sum(列名))
SELECT
SUM(IFNULL(english,0)) 英语总分
FROM
student ;
-- 查询英语成绩的最高成绩
SELECT
MAX(IFNULL(english,0)) 英语最高分
FROM
student ;
-- 查询数学成绩最低分
SELECT
MIN(math) 数学最低分
FROM
student ;
-- 聚合函数使用最多的:count函数,avg函数
8. 排序查询(order by)
排序查询:order by 字段名称 asc/desc (升序/降序)
-- select 字段列表 from 表名 order by 字段名 排序规则; -- 单个字段进排序
-- 数学成绩安装升序排序
SELECT
*
FROM
student
ORDER BY math ; -- 如果字段名称后面没有带排序规则:则默认升序排序
-- 英语降序排序
SELECT
NAME ,
age,
sex,
address,
IFNULL(english,0) 英语成绩
FROM
student
ORDER BY english DESC ; -- 降序
-- 针对多个字段同时排序,当前第一字段值相等,则按照第二个字段的排序规则执行
-- select 字段列表 from 表名 order by 字段名称1 升序1 ,字段名称2 升序2;
-- 查询全表数据,数学成绩降序,英语成绩升序
SELECT
*
FROM
student
ORDER BY
math DESC,
english ASC ;
9. 分组查询(order by)
分组查询:group by
-- 分组group by 分组字段;
-- 查询的时候可以查询分组字段,
-- group by 后面不能使用聚合函数
-- 问题:如果group by 和where条件语句一块使用,先后顺序? where之后 才能使用group by
-- 现在需要按照性别分组-----分组之后查询出总人数
-- 性别-- 男/女
SELECT
-- 查询分组字段
sex '性别',
COUNT(id) '人数'
FROM
student
GROUP BY
sex ; -- 性别
-- 现在需要按照性别分组-----分组之后查询出总人数,数学的平均分
-- 条件:数学成绩不大于70分的人不参与分组;
/*
SELECT
-- 查询分组字段
sex '性别',
COUNT(id) '人数'
FROM
student
GROUP BY
sex -- 性别
where
math > 70 ;
*/
SELECT
sex '性别', -- 查询分组字段
COUNT(id) '总人数',
AVG(math) '数学平均成绩'
FROM
student
WHERE
math > 70 -- 条件:数学成绩大于70分人参与分组
GROUP BY
sex ; -- 性别分组
10. 筛选查询(having)
-- 现在需要按照性别分组-----分组之后查询出总人数,数学的平均分
-- 条件:数学成绩不大于70分的人不参与分组
-- 筛选条件:总人数大于2的一组
-- having 必须置于group by 之后,where 置于 group by 之前
-- group by不能聚合函数,但是having后面可以聚合函数
SELECT
sex '性别', -- 查询分组字段
COUNT(id) '总人数',
AVG(math) '数学平均成绩'
FROM
student
WHERE
math > 70 -- 条件:数学成绩大于70分人参与分组
GROUP BY
sex -- 性别分组
HAVING
COUNT(id) > 2 ;
-- 优化
SELECT
sex 性别, -- 查询分组字段
COUNT(id) 总人数,
AVG(math) 数学平均成绩
FROM
student
WHERE
math > 70 -- 条件:数学成绩大于70分人参与分组
GROUP BY
sex -- 性别分组
HAVING
总人数 > 2 ;
11. 分页查询(limit)
select 字段列表 from 表名 limit 起始行数,每页显示多少条;
-- 给student表在插入三条记录
INSERT INTO student VALUES(9,'德邦',19,'男','西安',78,58) ,
(10,'vn',20,'女','宝鸡',89,65),(11,'亚索',22,'男','西安',95,74);
-- 每页显示3条记录
-- 查询第一页的数据
-- limit 起始行数=(当前页码数-1)*每页显示的条数,每页显示条数;
SELECT * FROM student LIMIT 0,3 ;
-- 查询第二页的数据
SELECT * FROM student LIMIT 3 ,3 ;
-- 查询第三页数据
SELECT * FROM student LIMIT 6,3 ;
-- 第四页数据
SELECT * FROM student LIMIT 9,3 ;
十二.数据库约束
-- 约束用户操作表的一种行为
-- 创建一个新的表
CREATE TABLE test(
id INT , -- 编号
NAME VARCHAR(10) , -- 姓名
gender VARCHAR(2) DEFAULT '女' -- 性别 -- 默认约束 防止出现非法数据null(没有插入造成null值)
) ;
1. 默认约束(default)
默认约束,当前没有给那个字段设置值的时候,此时默认约束就会起作用
DROP TABLE test ;
INSERT INTO test VALUES(1,'张三','男') ;
-- 可能用户操作数据库的时候,插入非法数据(没有意义的数据)
-- insert into test values(2,null,'女') ;
-- 如果没有给某个字段赋值,默认值null
INSERT INTO test(id,NAME) VALUES(2,'高圆圆') ;
-- 通过sql语句修改表的类型,删除默认约束
ALTER TABLE test MODIFY gender VARCHAR(2) ;
INSERT INTO test(id,NAME) VALUES(3,'文章') ;
-- 修改表加入默认约束
ALTER TABLE test MODIFY gender VARCHAR(2) DEFAULT '女' ;
DELETE FROM test WHERE id = 3 ;
2. 非空约束(NOT NULL)
DROP TABLE test ;
CREATE TABLE test(
id INT ,
NAME VARCHAR(10) NOT NULL -- 非空约束
);
INSERT INTO test VALUES(1,NULL) ; -- 直接插入null值
-- insert into test (id) values(1) ; 没有给姓名赋值
INSERT INTO test VALUES(1,'') ; -- 存在值,只是空字符 和null不一样
-- 删除非空约束
ALTER TABLE test MODIFY NAME VARCHAR(10) ;
UPDATE test SET NAME = '高圆圆' WHERE id = 1 ;
INSERT INTO test VALUES(2,NULL) ; -- Column 'NAME' cannot be null
-- 修改表,加入非空约束
ALTER TABLE test MODIFY NAME VARCHAR(10) NOT NULL ;
DELETE FROM test WHERE id = 2 ;
3. 唯一约束(unique)
DROP TABLE test;
CREATE TABLE test(
id INT ,
NAME VARCHAR(10),
phone VARCHAR(11) UNIQUE -- 唯一约束 :可以有null值,不能重复
) ;
INSERT INTO test VALUES(1,'张三','13666668888') ;
-- INSERT INTO test VALUES(1,'张三',null) ;
-- INSERT INTO test(id,name) values(2,'李四') ;
INSERT INTO test VALUES(2,'李四','13666668889') ;
-- INSERT INTO test VALUES(2,'李四','13666668888') ;
-- Duplicate entry '13666668888' for key 'test.phone'
-- 通过语句删除唯一约束
-- alter table test modify phone varchar(11) ; 错误语法
-- 删除唯一约束的sql alter table test drop index 字段名称;
ALTER TABLE test DROP INDEX phone ;
INSERT INTO test VALUES(4,'赵六','13666668878') ;
DELETE FROM test WHERE id = 4 ;
-- 添加唯一约束
ALTER TABLE test MODIFY phone VARCHAR(11) UNIQUE ;
4. 主键约束 (非空+唯一特点) (primary key)
都会给当前非业务字段去设置主键(xxid)
DROP TABLE test ;
CREATE TABLE test(
id INT PRIMARY KEY , -- 非业务字段
NAME VARCHAR(10),
gender VARCHAR(2)
) ;
INSERT INTO test VALUES(1,'洪学佳','男'),(2,'马三奇','男') ;
-- insert into test values(1,'马嘉钰','男') ;id值重复 -- Duplicate entry '1' for key 'test.PRIMARY'
-- insert into test values(null,'雷郁','男') ;-- id直接插入null值 Column 'id' cannot be null
SELECT * FROM test ;
5. 外键约束(foreign key)
如果没有外键约束
-- 创建 部门表
CREATE TABLE dept(
id INT PRIMARY KEY AUTO_INCREMENT, -- 部门编号
dept_name VARCHAR(20) -- 部门名称
);
-- 插入数据
INSERT INTO dept VALUES(1,'开发部'),(2,'测试部'),(3,'运维部') ;
-- 创建表:员工表
CREATE TABLE emp(
id INT PRIMARY KEY AUTO_INCREMENT, -- 员工编号
NAME VARCHAR(20), -- 员工姓名
gender VARCHAR(10), -- 性别
dept_id INT -- 部门编号
) ;
-- 插入员工数据
INSERT INTO emp VALUES(1,'高圆圆','女',1),
(2,'赵又挺','男',1),
(3,'文章','男',2),
(4,'马伊琍','女',3),
(5,'姚笛','女',2),
(6,'马三奇','男',1) ;
-- 插入一个不存员工,而且插入一个不存在部门编号
INSERT INTO emp (NAME,gender,dept_id) VALUES('王宝强','男',4) ;
没有外键约束会插入成功.
加入外键约束 :
-- 创建员工表的时候就需要添加外键(外键所在表称为"从表")
CREATE TABLE emp(
id INT PRIMARY KEY AUTO_INCREMENT, -- 员工编号
NAME VARCHAR(20), -- 员工姓名
gender VARCHAR(10), -- 性别
dept_id INT, -- 部门编号
CONSTRAINT fk_demp_emp -- 声明 外键名称
FOREIGN KEY (dept_id) -- 作用在指定从表的指定字段
REFERENCES -- 关联
dept(id) -- 部门表的主键id
-- 插入员工数据
INSERT INTO emp VALUES(1,'高圆圆','女',1),
(2,'赵又挺','男',1),
(3,'文章','男',2),
(4,'马伊琍','女',3),
(5,'姚笛','女',2),
(6,'马三奇','男',1) ;
-- 插入一个不存员工,而且插入一个不存在部门编号
INSERT INTO emp (NAME,gender,dept_id) VALUES('王宝强','男',4) ;
插入失败;
-- 外键约束(两张表设置一个外键):两种设置方式
-- 1)创建表的直接添加外键
-- 2)通过alter table 表名 add .... 方式添加外键
-- 第二种方式:
ALTER TABLE
emp
ADD
CONSTRAINT -- 声明
fk_demp_emp -- 外键名称
FOREIGN KEY (dept_id) -- 作用在指定从表的指定字段
REFERENCES -- 关联
dept(id) ; -- 部门表的主键id
SELECT * FROM dept ; -- 查询部门表
SELECT* FROM emp ; -- 查询员工表
6. 级联操作:CASCADE
-- 有了外键有关联关系,所以不直接操作主表;修改/删除 (很麻烦)
-- 1)修改或者从表的数据,让这个数据和主表没有关联
-- 2)修改/删除 主表的数据
通过级联的方式 :
-- 级联操作:CASCADE
-- 级联修改 ON UPDATE CASCADE
-- 当前修改主表的数据,那么从表和主表相关关联的数据一会被随着更改掉
-- 级联删除 ON DELETE CASCADe,
-- 当删除主表的数据,那么从表和主表关联的数据一会被随着删除掉
-- 先sql语句将外键约束删除,然后添加外键并且添加级联操作
7. 数据库的备份和还原
-- 两种方式:
-- 图形界面话:简单直观
-- 选择数据库---右键----备份---->选择导出的路径(结构以及数据都保存)
-- 将之前存在库,然后选择 执行指定sql脚本----> 选择指定的sql脚本---进行执行即可!
-- 命令行方式
-- 备份:mysqldump -uroot -p密码 备份数据库名称 > 保存的本地地址
-- 还原:
-- 将原来删除,新建库,使用库, source 将保存的本地地址
-- 表与表的关系
-- 一对一:是一种特例 (应用场景不多)
-- 举例: 人和身份证
-- 一个人对应一张身份证
-- 一个身份证属于某个人的
-- 一对多的关系 ,多对多的关系
-- 员工表和部门表
-- 一部门可以有多个员工,一个员工属于某一个部门的(员工表中有一个外键)
8. 数据库的三大范式
-- 1NF :数据库表的每一列都是不可分割的原子数据项
-- 不能出现复合项
-- 2NF:第二范式就是在第一范式的基础上所有列(所有字段)完全 依赖于主键列(主键字段)。
-- 3NF:在第二范式2NF基础上,非主键字段不能传递依赖于主键(外键解决)
9.多表查询
1.内连接查询
-- 查询多张表:员工表5条记录,部门表有3条记录 (A*B=15)
-- 出现迪尔卡乘积的原因:没有连接条件
-- 通过外键解决字段冗余问题 员工表的部门dept_id关联与部门表的主键id
-- 多表查询之 内连接查询
-- 隐式内连接-----通过where 语句作为连接条件来查询(按照外键关联关系)
-- 操作步骤:
-- 1)查询哪张表
-- 2)要查询的是指定表中的哪个字段
-- 3)表和表之间的关联关系问题
-- 1)员工表和部门表
-- 2)查询员工表的中name,gender,salary以及部门表的部门名称
-- 3)连接条件:dept_id= 部门表的id
SELECT
e.name '员工姓名' ,
e.`gender` '性别',
e.`salary` '工资',
e.`dept_id` ,
d.id ,
d.`name` '部门名称'
FROM
emp e,dept d -- 员工表的别名e ,部门表的别名d
WHERE
e.`dept_id` = d.`id` ;
-- 内连接之显示内连接
-- (实际开发中,多去使用where条件查询,查询速度快 ,sql优化的一种方式)
-- select <字段列表>
-- from 表名1
-- (inner) join
-- 表名2
-- on 关联关系(连接条件);
SELECT
t1.*,-- 员工表中的所有字段 (实际开发中,* 不建议使用!)
t2.`name` -- 部门表的部门名称
FROM
emp t1
-- inner 可以省略,不写
JOIN dept t2
ON
t1.`dept_id` = t2.`id` ;
-- 要查询所有的员工表信息的同时关联查询部门表信息(部门名称即可)
-- 即使员工没有部门信息,也得查询出来...
SELECT
t1.`name` '员工名称',
t1.`gender` '性别',
t1.`salary` '工资',
t1.`join_date` '入职日期',
t2.`name` '部门名称'
FROM
emp t1
INNER JOIN dept t2
ON
t1.`dept_id` = t2.`id` ; -- 交集的条件
内连接查询存在的问题
-- 通过内连接查询存在问题 :只是查出了交集部分的数据,没有部门编号的员工并没有查询出来
2.外连接查询
-- 使用 多表查询之外连接查询
-- 左外连接 left (outer) join: 通用
-- 将两种表中左表的数据全部进行查询并且以及他们交集部分的数据全部查询
-- 右外连接 right (outer) join
-- 语法格式
-- select
-- 字段列表
-- from
-- 表名1 -- 左表
-- left outer join 表名2
-- on 连接条件 ;
SELECT
t1.`name` '姓名',
t1.`gender` '性别',
t1.`salary` '工资',
t1.`join_date` '入职日期',
t1.`dept_id` ,
t2.`name` '部门名称'
FROM
emp t1 -- 左表(见图中的A表)
LEFT
-- outer
JOIN
dept t2 -- (图中的B表)
ON
t1.`dept_id` = t2.`id` ; -- 交集部分数据
SELECT
*
FROM
dept t1
RIGHT OUTER JOIN emp t2
ON
t2.`dept_id` = t1.`id` ;
3.子查询(三种情况)
-- 情况1:单行单列的情况:通用使用运算符进行操作
-- 需求:查询最高工资的员工信息
-- 1)查询最高工资是多少 : 聚合函数
/*
select
max(salary)
from
emp ; -- 12000
*/
-- 2) 查询等于最高工资的员工信息
/*
select
emp.`name`,
emp.`salary`,
emp.`gender`,
emp.`join_date`
from
emp
where
salary = 12000 ;
*/
-- 一步走: 将12000的sql语句代入过来即可
SELECT
emp.`id`,
emp.`name`,
emp.`salary`,
emp.`gender`,
emp.`join_date`
FROM
emp
WHERE
salary = (SELECT MAX(salary) FROM emp ) ;
-- 查询工资小于平均工资的员工信息
-- 1)查询平均工资是多少
/*
select
avg (salary)
from emp ;
*/
-- 2)查询员工工资小于 6883.333....的员工信息
8
SELECT
*
FROM
emp
WHERE
salary < (SELECT AVG (salary) FROM emp ) ;
-- 子查询的第二种情况 :多行多列的情况 ---使用关键字in(集合数据)
-- 需求:查询 在财务部或者市场部的员工信息
-- 分步骤
-- 1)查部门表中财务部和市场部的id是多少
/*
select
id
from
dept
where
name = '市场部' or name = '财务部' ;-- 2 , 3
*/
-- 2)在员工表中查 id为 财务部 (3) 或者市场部(2)的员工信息
SELECT
t.`name` '姓名',
t.`gender` '性别',
t.`salary` '工资',
t.`join_date` '入职日期',
t.`dept_id` '部门编号'
FROM
emp t
WHERE
t.`dept_id` = 2 OR t.`dept_id` = 3 ;
-- 优化 步骤2)sql语句 or---->in(集合数据)
-- 在xxx数据内 /not in exists /not exists /any(sql)
SELECT
t.`name` '姓名',
t.`gender` '性别',
t.`salary` '工资',
t.`join_date` '入职日期',
t.`dept_id` '部门编号'
FROM
emp t
WHERE
t.`dept_id` IN(
SELECT
id
FROM
dept
WHERE
NAME = '市场部'
OR
NAME = '财务部') ;
-- 子查询的情况3:
-- 使用select语句查询的结果作为一个虚表,使用这个虚表然后继续和其他表进行查询
-- 查询入职日期大于'2011-3-14号的员工信息以及所在的部门信息
-- 入职日期 join_date > '2011-03-14' 将查询作为虚表 和部门表继续进行查询
SELECT * FROM emp WHERE join_date > '2011-03-14';
-- 可以使用左外连接
SELECT
t2.*, -- 员工的所有信息
t1.`name` '部门名称'
FROM
dept t1 -- 部门表
LEFT OUTER JOIN
(SELECT * FROM emp WHERE join_date > '2011-03-14') t2
ON
t1.`id` = t2.`dept_id` ;
-- 最基本隐式内连接
SELECT
t1.* , -- 员工所有信息
t2.`name` -- 部门名称
FROM
emp t1 ,
dept t2
WHERE
t1.`dept_id` = t2.`id`
AND
t1.`join_date` > '2011-03-14' ;
-- 多表查询:查询哪个表/哪个字段/连接条件
SELECT * FROM dept ;
SELECT * FROM emp ;
10. mysql事务
-- 事务:在业务操作过程中,一次性可能同时操作多个sql语句,防止
-- 操作多个sql语句时候出现问题,将这整个业务操作看成一个整体,进行处理
-- 这些多个sql要么一次性全部执行成功,要么同时失败!
-- 转账
-- 创建一张账户表
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT, -- 账户编号
NAME VARCHAR(20), -- 账户名称
balance INT -- 账户余额
) ;
INSERT INTO account(NAME ,balance) VALUES('zhangsan',1000),('lisi',1000) ;
SELECT * FROM account
-- 没有开启事务,需要管理多个操作(一次执行多个sql语句),可以将多个操作
-- 使用事务管理,开启事务
-- 没有使用事务之前
-- zhangsan- 500
UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan' ;
出问题了
UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi' ;
SELECT * FROM account ;
UPDATE account SET balance = 1000 ;
-- 将转账业务操作看成整体,开启事务
START TRANSACTION ; -- 开启手动提交事务(默认自动提交)
-- 执行业务 操作
UPDATE account SET balance = balance - 500 WHERE NAME = 'zhangsan' ;
-- 中间出问题了
UPDATE account SET balance = balance + 500 WHERE NAME = 'lisi' ;
-- 事务回滚:回滚到在操作语句之前的状态 (余额都是1000)
ROLLBACK ;
-- 如果转账中没问题,将手动事务进行提交
COMMIT;
1.事务的特点
-- 事务的特点:ACID
-- 原子性:就是事务操作业务的中sql,要么同时执行成功,要么同时失败!
-- 一致性:操作sql语句的前后,总数量保持不变
-- 转账之前:1000 1000
-- 转账之后:500 1500
-- 隔离性:
-- 将业务使用事务管理,业务和业务之间分离的,
-- 事务和事务之间不能相互影响---->事务是独立的
-- 持久性: 如果一旦事务被提交了,是永久存储的,即使关机也还存在!
-- 隔离级别
-- read uncommitted ; 读未提交 安全性最差 不能有效防止脏读
-- read committed ; 读已提交 安全相对第一个高一些,能够有效脏读,不能够防止可重复读的问题
-- repeatable read : 可重复读 能够有效防止脏读,可重复读
-- serializable 串行话
查看全局的隔离级别的命令
-- 查询隔离级别 select @@tx_isolation; mysql5.5/5.7都可以
-- SELECT @@transaction_isolation; mysql8.0
-- 设置隔离级别 set global transaction isolation level 级别字符串;
SELECT @@transaction_isolation;
-- 隔离级别不同,会出现不同的问题
-- 脏读:一个事务读取另一个没有提交的事务(最严重的问题)
-- 不可重复读: 一般都是update语句影响,两个事务中,一个事务读取的前后的内容不一致!
-- 幻读:一般insert/delete :影响两个事务中,前后数量不一致!
十三.JDBC
1.JDBC的本质
JDBC : java database connectivity : java操作数据库的连接器 提供的一个应用设计接口
JDBC的本质就是能够实现sun公司的Java.sql包下的相关的接口的实现类
使用java连接数据库的过程 :
1) 在java项目中添加额外的第三方的jar包
idea中 file----> project structure (ctrl+Alt+shift+s:默认快捷键_)
---model---->denpendies---->添加---jar包 ---将指定路径拿过来
2)注册驱动
3)获取数据库的连接对象
4)准备静态sql语句
5)通过数据库连接对象创建执行对象Statement
6)执行更新操作
7)释放资源
2.使用java连接数据库的过程
/*
* 使用java连接数据库过程:
* 1)在java项目添加额外的第三方的jar包
* idea中 file----> project structure (ctrl+Alt+shift+s:默认快捷键_)
* ---model---->denpendies---->添加---jar包 ---将指定路径拿过来
*
* 2)注册驱动
* 3)获取数据库的连接对象
* 4)准备静态sql语句
* 5)通过数据库连接对象创建执行对象Statement
* 6)执行更新操作
* 7)释放资源
*/
public class JdbcDemo {
public static void main(String[] args) throws Exception {
//1)导包了
//2)注册驱动
Class.forName("com.mysql.cj.jdbc.Driver") ; //mysql8.0 的驱动实现类:com.mysql.cj.jdbc.Driver
//mysql8.0以前的驱动实现类:com.mysql.jdbc.Driver
//3)获取数据库的连接对象
//使用驱动管理类 DriverManager:用于管理一组JDBC驱动程序的基本服务。
//public static Connection getConnection(String url,String user,String password) throws SQLException
//参数1:url: 连接数据库的路径: 协议名称 + ip地址/域名 :3306/数据库名称 针对mysql server 8.0以前这样使用
//参数1:url: 连接数据库的路径: 协议名称 + ip地址/域名 :3306/数据库名称?参数1=值2&参数2=值2 针对mysql server 8.0使用
//?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",
//参数2:用户名:root用户
//参数3:mysql连接的密码
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb_01?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",
"root",
"123456");
//4)准备sql语句
String sql = "insert into account(name,balance) values('赵又廷',1000)" ;
//5)通过数据库连接对象获取执行对象
//public Statement createStatement() ;用于将sql语句发送的数据库的执行器对象
Statement stmt = connection.createStatement();
//6)执行更新操作
//通用方法
//int executeUpdate(String sql) throws SQLException
int count = stmt.executeUpdate(sql);
System.out.println("影响了"+count+"行");
//7)释放资源
stmt.close() ;
connection.close();
}
}
3.JDBC_API 描述
//1)导包
//2)注册驱动
Class.forName("com.mysql.cj.jdbc.Driver") ; //为了向下兼容 获取当前类的字节码文件对象
获取类的字节码文件对象com.mysql.cj.jdbc.Driver :Class类对象
源码
加载类的时候,就将静态代码块执行了,核心代码就是注册驱动
static {
try {
java.sql.DriverManager.registerDriver(new Driver());//注册驱动
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
java.sql.DriverManager
//3)通过驱动管理了DrivarManager的getConnection(String url,String user,String password)
获取Connection
url:统一资源定位符 mysql8.0以后就必须带上了
jdbc:数据库协议://localhost:3306/ 数据库名称 ?参数(本地时区以及当前字符集&useSSL=false(不启用mysql连接认证)...
协议 域名或者ip:端口号/数据库名称?参数
user:用户名
password:密码
sun公司提供的 java.sql.Connection:接口
提供一些方法
public Statement createStatement() :创建执行对象Statement 接口
public PreparedStatement prepareStatement(String sql) :创建预编译对象的同时,发送参数化的sql
java.sql.Statement: 执行对象:将静态的sql发送到数据库中
每次发送的时都需要执行指定sql
通用的方法
int exeuteUpdate(String sql) :DDL语句:表的操作/以及DML语句insert into ,update,delete
ResultSet executeQuery(String sql):针对DQL语句:数据库的查询语句
select * from account where id = 值 ;
dml语句:数据操作语句
静态sql
insert into account(name,balance) values('值1',值2) ; (硬编码)
java.sql.prepearedStatement: 继承自 java.sql.Statement: 接口
预编译对象,发送的sql语句,
而且在通过连接Connection创建预编译对象的时候就已经将参数化的sql
insert into account(name,balance) values(?,?) ;
通用的方法
int exeuteUpdate() :DDL语句:表的操作/以及DML语句insert into ,update,delete
java.sql.ResultSet接口:数据库结果集的数据表
public boolean next():判断当前光标是否向前(有效数据行)移动(如果有数据,继续移动)
获取每一列的内容:根据每一个列的字段类型
通用的方法
XXX 变量名 = getXXX(列的名称/或者列的索引值) ; 方式1 :通用
getMetaData() ----ResultSetMetaData :数据表的元数据信息 方式2
4. JDBC_Statement对象操作DDL语句,DML语句,DQL语句
/*
* 通过java.sql.Statment接口:针对DDL语句操作:建表语句
*/
public class JdbcDemo {
public static void main(String[] args) {
Connection conn = null ;
Statement stmt = null ;
try {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver") ;
//创建Connection对象
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/ee_2106?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",//(8.0以后+?后面)
"root",
"123456");
//准备sql语句 DDL语句
String sql = "create table student(" +
"id int primary key auto_increment," +
" name varchar(20)," +
" gender varchar(10)," +
" email varchar(50) )" ;
//通过连接对象获取执行对象
stmt = conn.createStatement();
//发送sql语句到数据库中进行更新
// int exeuteUpdate(String sql) :DDL语句:表的操作/以及DML语句insert into ,update,delete
int count = stmt.executeUpdate(sql);
//0
System.out.println(count);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//释放资源
if(stmt!=null){ //释放执行对象
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){//释放连接对象
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/*
* Statment对象执行静态的sql语句----DML语句 insert into,delete,update
*/
public class JdbcDemo2 {
public static void main(String[] args) {
Connection conn = null ;
Statement stmt = null ;
try {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver") ;
//创建Connection对象
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/ee_2106?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",
"root",
"123456");
//准备sql语句 DML语句
String sql = "update student set gender = '女' where id = 3 " ;
//通过连接对象获取执行对象
stmt = conn.createStatement();
//发送sql语句到数据库中进行更新
// int exeuteUpdate(String sql) :DDL语句:表的操作/以及DML语句insert into ,update,delete
int count = stmt.executeUpdate(sql);
System.out.println(count);
System.out.println("修改成功");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//释放资源
if(stmt!=null){ //释放执行对象
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){//释放连接对象
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
/*
* ResultSet接口:一般都是select 查询语句查询的数据表的结果集
*
* 如何操作 select DQL语句
* 需求:将ee_2106这个数据库中的student表数据全部查询并遍历出来!
*
*/
public class JdbcDemo3 {
public static void main(String[] args) {
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
//注册驱动
try {
Class.forName("com.mysql.cj.jdbc.Driver") ;
//创建数据库连接对象
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/ee_2106?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true",
"root",
"123456");
//准备sql
String sql = "select * from student" ;
//创建执行对象
stmt = conn.createStatement() ;
//执行sql语句
rs = stmt.executeQuery(sql);
//先判断
//第一次获取
if(rs.next()) {
//如果有下一行数据,那么就获取
//通用方法
//XXX getXXX(int columnIndex):列的索引值获取 内容 index从1 开始,第一列值就是1
//XXX getXXX(String columnIabel):列的名称获取 内容
int id = rs.getInt(1);
String name = rs.getString(2) ;
String gender = rs.getString(3) ;
String email = rs.getString(4) ;
System.out.println(id+"\t"+name+"\t"+gender+"\t"+email);
}
/*
//继续判断:继续移动获取第二行数据
if(rs.next()) {
//如果有下一行数据,那么就获取
//通用方法
//XXX getXXX(int columnIndex):列的索引值获取 内容 index从1 开始,第一列值就是1
//XXX getXXX(String columnIabel):列的名称获取 内容
int id = rs.getInt("id");
String name = rs.getString("name") ;
String gender = rs.getString("gender") ;
String email = rs.getString("email") ;
System.out.println(id+"\t"+name+"\t"+gender+"\t"+email);
}
//继续判断:继续移动获取第三行数据
if(rs.next()) {
//如果有下一行数据,那么就获取
//通用方法
//XXX getXXX(int columnIndex):列的索引值获取 内容 index从1 开始,第一列值就是1
//XXX getXXX(String columnIabel):列的名称获取 内容
int id = rs.getInt("id");
String name = rs.getString("name") ;
String gender = rs.getString("gender") ;
String email = rs.getString("email") ;
System.out.println(id+"\t"+name+"\t"+gender+"\t"+email);
}
//继续判断:继续移动获取第三行数据
if(rs.next()) {
//如果有下一行数据,那么就获取
//通用方法
//XXX getXXX(int columnIndex):列的索引值获取 内容 index从1 开始,第一列值就是1
//XXX getXXX(String columnIabel):列的名称获取 内容
int id = rs.getInt("id");
String name = rs.getString("name") ;
String gender = rs.getString("gender") ;
String email = rs.getString("email") ;
System.out.println(id+"\t"+name+"\t"+gender+"\t"+email);
}*/
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name") ;
String gender = rs.getString("gender") ;
String email = rs.getString("email") ;
System.out.println(id+"\t"+name+"\t"+gender+"\t"+email);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
5. JDBC 封装成 JdbcUtils 工具类
/*
*
* 封装jdbc操作的工具类
* 频繁的去操作数据库:CRUD:增删查改
* 每一条sql都去创建Connection对象
* 而且还需不断的去释放资源 Statment对象,Connection,ResultSet集合对象
*
*/
//建立配置文件jdbc.properties :
url=jdbc:mysql://localhost:3306/ee_2106?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
driverClass=com.mysql.cj.jdbc.Driver
user=root
password=123456
public class JdbcUtils {
private static String url = null ;
private static String user = null ;
private static String password = null ;
private static String drivceClass = null ;
//构造方法私有化
private JdbcUtils(){}
//静态代码块
//JdbcUtils工具类一加载就执行静态代码块
static{
try {
//创建一个Properties属性列表
Properties prop = new Properties() ;
//读取src下面的jdbc.properties配置文件
//直接获取当前类的字节码文件对象,获取类加载,获取资源文件所在的输入流对象
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
//将流中内容加载到属性列表中
prop.load(inputStream);
System.out.println(prop);//测试属性列表中的内容
//通过key获取value
url = prop.getProperty("url") ;
user = prop.getProperty("user") ;
password = prop.getProperty("password") ;
drivceClass = prop.getProperty("driverClass") ;
//注册驱动
Class.forName(drivceClass) ;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//提供功能都是static
//通用的功能:获取数据库连接对象
public static Connection getConnection(){
Connection conn = null ;
try {
//通过驱动类管理类获取
conn = DriverManager.getConnection(url,user,password) ;
return conn ;
} catch (SQLException e) {
e.printStackTrace();
}
return null ;
}
//关闭资源:
//DDL语句或者DML语句----->Statement对象和Connection
public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);
}
//关闭资源
//DQL语句---->ResultSet对象,Statement对象和Connection
public static void close(ResultSet rs,Statement stmt,Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt !=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Connection connection = JdbcUtils.getConnection();
System.out.println(connection);
}
}
测试类
// 将获取连接对象以及关闭资源封装到工具类中了,测试下
public class JdbcDemo {
public static void main(String[] args) {
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
try {
//获取连接对象
conn = JdbcUtils.getConnection();
//准备sql语句
//DML语句
//String sql = "insert into student(name,gender,email) values('文章','男','wenzhang@163.com')" ;
//DQL语句
// String sql = "select * from student" ;
String sql = "select * from emp" ;
//执行对象
stmt = conn.createStatement();
//执行
// int count = stmt.executeUpdate(sql);
// System.out.println("执行成功"+count+"行受影响");
rs = stmt.executeQuery(sql) ;
// System.out.println("编号"+"\t"+"姓名"+"\t"+"性别"+"\t"+"邮箱");
while(rs.next()){
/*int id = rs.getInt("id") ;
String name = rs.getString("name") ;
String gender = rs.getString("gender") ;
String email = rs.getString("email") ;*/
// System.out.println(id+"\t\t"+name+"\t"+gender+"\t\t"+email);
int id = rs.getInt("id") ;
String name = rs.getString("name") ;
String gender =rs.getString("gender") ;
Double salary = rs.getDouble("salary") ;
Date join_date =rs.getDate("join_date") ;
//添加:java.util.Date----->java.sql.Data(添加/修改)..._
System.out.println(id+"\t"+name+"\t"+gender+"\t"+salary+"\t"+join_date);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
// JdbcUtils.close(stmt,conn);
JdbcUtils.close(rs,stmt,conn);
}
}
}
6. jdbc封装到List集合中
学生类
public class Student {
/*
Field Type Comment
id int NOT NULL
name varchar(20) NULL
gende rvarchar(10) NULL
email varchar(50) NULL*/
private int id ; //编号
private String name ;//姓名
private String gender ;//性别
private String email ; //邮箱
public Student() {
}
public Student(int id, String name, String gender, String email) {
this.id = id;
this.name = name;
this.gender = gender;
this.email = email;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", email='" + email + '\'' +
'}';
}
}
针对学生的数据库访问接口
public interface StudentDao {
/**
* 查询所有学生信息
* @return 返回学生列表数据
*/
List<Student> findAll() ;
/**
* /通过学生id学号查询某个学生信息
* @param id 学号id
* @return 返回的某个学生实体
*/
Student findById(int id) ;
/**
* 添加学生信息
* @param student 学生实体类
*/
void add(Student student) ;
/**
* 更新学生信息
* @param student
*/
void updateStudent(Student student) ;
/**
* 通过学号删除学生
* @param id 学生id号
*/
void delete(int id) ;
}
数据库访问实现层
public class StudentDaoImpl implements StudentDao {
@Override
public List<Student> findAll() {
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
//操作数据库
try {
//创建List集合对象
List<Student> list = new ArrayList<>() ;
//获取连接对象
conn = JdbcUtils.getConnection();
//准备sql语句
String sql = "select * from student" ;
//获取执行对象
stmt = conn.createStatement();
//执行查询
rs = stmt.executeQuery(sql);
//声明学生类型的变量student
Student student = null ;
while(rs.next()){
//创建学生对象,并封装学生数据
student = new Student() ;
//查询一行,将这条学生信息封装到学生类中
int id = rs.getInt("id");
String name = rs.getString("name");
String gender = rs.getString("gender") ;
String email = rs.getString("email");
student.setId(id);
student.setName(name);
student.setGender(gender);
student.setEmail(email);
//将学生添加到集合中
//返回集合List
list.add(student) ;
}
return list ;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public Student findById(int id) {
return null;
}
@Override
public void add(Student student) {
}
@Override
public void updateStudent(Student student) {
}
@Override
public void delete(int id) {
}
}
测试类
public class JdbcDemo2 {
public static void main(String[] args) throws Exception {
//接口多态方式
//遍历集合,获取结果
/* StudentDao sd = new StudentDaoImpl() ;
List<Student> list = sd.findAll();
for (Student student : list) {
//System.out.println(student);
System.out.println(student.getId()+"\t"+student.getName()+"\t"+student.getGender()+"\t"+student.getEmail()); //toString()
}*/
//反射创建当前StudentDaoImpl的实例
Class clazz = Class.forName("com.qf.jdbc_statment_03_jdbcutils.StudentDaoImpl");
//创建当前类实例
Object obj = clazz.newInstance(); //或者Constructor类对象
//获取Method类对象
Method method = clazz.getMethod("findAll");
//调用
Object result = method.invoke(obj); //toString()
System.out.println(result);
}
}
6.预编译对象PreparedStatement
1)是否能够防止sq1注入
Preparedstatement能够有效防止sq1注入(由于存在sq1语句字符串拼接造成sq1注入不安全)
statement:执行静态sq1语句,语句中就会在字符串拼接,造成sq1注入(不推荐)
2)是否高效(提高执行效率)
Preparedstatement因为操作的参数化的sq1语句,首先预编译---将sq7语句存储在预编译对象中,根据占位符号类型进行赋值setxxx(int parameterIndex ,实际参数(根据当前类型赋值))
直接通过预编译对执行sq7语句,发送到数据库中进行操作――(执行sq1效率高)
statement对象:每次将拼接好的sq1语句("硬编码")进行执行―(执行sq1效率低)
每次发送一个新的sq7到数据库中进行执行
executeupdate(string sq7)
executeQuery(string sq7)
预编译对象PreparedStatement 继承自Statment对象
操作步骤
1)注册驱动
2)获取数据库连接对象
3)准备参数化的sql 这些sql并非是静态sql语句
4)通过连接创建预编译对象并将参数化的sql语句保存在PreparedStatement对象中
5)给参数化的sql的数据进行赋值
6)执行更新/查询
7)释放资源
public class PerparedStatementDemo {
public static void main(String[] args) {
Connection conn = null ;
PreparedStatement stmt = null ;
ResultSet rs = null ;
try {
//获取数据库的连接对象
conn = Utils.getConnection();
//准备sql语句:参数化的sql
//占位符号:?(英文符号)
// String sql = "insert into student(name,gender,email) values(?,?,?)" ;
String sql = "select * from student" ;
//通过连接对象获取预编译对象并将参数化的sql保存到该对象中
//PreparedStatement prepareStatement(String sql)
stmt = conn.prepareStatement(sql); //预编译的过程:对sql语句中占位符号进行描述
//参数赋值
//void setXXX(int parameterIndex,XXX num)
//举例:setString(第几个占位符号(占位符号的索引值:1开始),当前占位符的实际参数"hello" )
/* stmt.setString(1,"王桑") ;
stmt.setString(2,"男") ;
stmt.setString(3,"wangsang@163.com") ;*/
//执行
//ResultSet executeQuery() :执行查询操作:DQL语句
//int executeUpdate() 执行通用的更新操作:DDL/DML语句(insert into /update/delete)
// int count = stmt.executeUpdate();
// System.out.println("影响了"+count+"行");
//查询
rs = stmt.executeQuery();
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String gender = rs.getString("gender");
String email = rs.getString("email");
System.out.println(id+"\t"+name+"\t"+gender+"\t"+email);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
//释放资源
JdbcUtils.close(stmt,conn);
}
}
}
使用Statement对象模拟登录操作
键盘录入用户名和密码,判断是否登录成功
分析:
1)创建一个表 user(id,username,password)三个字段
2)封装一个功能isLogin(String username,String password)---->boolean
3)利用jdbc基本操作 查询user表即可
4)通过ResultSet的next()判断,如果查询到了 返回true,否则false!
问题:
Statement对象操作静态sql语句:硬编码而且由于如果存在sql语句字符串拼接
造成sql注入问题,用户名和密码不一致,依然登录成功,最不安全的行为!
select * from user where username='sdfs' and password = 'a 'OR 'a' ='a' 后面恒成立
Statement对象和PreparedStatement对比:
1)后者防止sql注入,前者存在sql注入(存在sql语句字符串拼接)
2)执行效率后者高于前者
前者效率低
每次获取Statement对象
executeUpdate(String sql)
executeQuery(String sql)
后者:
String sql = "select * from user where username=? and password = ?" ;
每次获取PreparedStatement对象的时候就已经sql保存在对象中进行预编译过程
赋值
executeUpdate()
executeQuery()
public class Statement_And_PreparedStatementTest {
public static void main(String[] args) {
//创建键盘录入对象
Scanner sc = new Scanner(System.in) ;
//提示并录入数据
System.out.println("请输入用户名:");
String username = sc.nextLine() ;
System.out.println("请输入密码:");
String password = sc.nextLine() ;
//调用一个功能
boolean flag = isLogin2(username,password) ;
if(flag){
System.out.println("恭喜您,登录成功");
}else{
System.out.println("登录失败...");
}
}
//PreparedStatement
//定义登录方法
public static boolean isLogin2(String username, String password) {
//获取数据库的连接对象
Connection conn = null ;
PreparedStatement stmt = null ;
ResultSet rs = null ;
try {
conn = JdbcUtils.getConnection();
//准备sql :参数化sql
//通过用户和密码查询用户
String sql = "select * from user where username=? and password = ?" ;
System.out.println(sql);
//创建执行对象PreparedStatement预编译对象
stmt = conn.prepareStatement(sql) ;
//参数赋值
//在PerparedStatement对象中完成参数赋值过程
stmt.setString(1,username);
stmt.setString(2,password);
//执行查询
rs = stmt.executeQuery();
return rs.next() ;
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(rs,stmt,conn);
}
return false ;
}
//Statement对象
//定义登录方法
public static boolean isLogin(String username, String password) {
//获取数据库的连接对象
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
try {
conn = JdbcUtils.getConnection();
//准备sql :静态sql
//通过用户和密码查询用户
String sql = "select * from user where username='"+username+"' and password = '"+password+"' " ;
System.out.println(sql);
//创建执行对象Statement对象
stmt = conn.createStatement();
//执行查询
rs = stmt.executeQuery(sql);
return rs.next() ;
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(rs,stmt,conn);
}
return false ;
}
}
public class Statement_And_PreparedStatementTest {
public static void main(String[] args) {
//创建键盘录入对象
Scanner sc = new Scanner(System.in) ;
//提示并录入数据
System.out.println("请输入用户名:");
String username = sc.nextLine() ;
System.out.println("请输入密码:");
String password = sc.nextLine() ;
//调用一个功能
boolean flag = isLogin2(username,password) ;
if(flag){
System.out.println("恭喜您,登录成功");
}else{
System.out.println("登录失败...");
}
}
//PreparedStatement
//定义登录方法
public static boolean isLogin2(String username, String password) {
//获取数据库的连接对象
Connection conn = null ;
PreparedStatement stmt = null ;
ResultSet rs = null ;
try {
conn = JdbcUtils.getConnection();
//准备sql :参数化sql
//通过用户和密码查询用户
String sql = "select * from user where username=? and password = ?" ;
System.out.println(sql);
//创建执行对象PreparedStatement预编译对象
stmt = conn.prepareStatement(sql) ;
//参数赋值
//在PerparedStatement对象中完成参数赋值过程
stmt.setString(1,username);
stmt.setString(2,password);
//执行查询
rs = stmt.executeQuery();
return rs.next() ;
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(rs,stmt,conn);
}
return false ;
}
//Statement对象
//定义登录方法
public static boolean isLogin(String username, String password) {
//获取数据库的连接对象
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
try {
conn = JdbcUtils.getConnection();
//准备sql :静态sql
//通过用户和密码查询用户
String sql = "select * from user where username='"+username+"' and password = '"+password+"' " ;
System.out.println(sql);
//创建执行对象Statement对象
stmt = conn.createStatement();
//执行查询
rs = stmt.executeQuery(sql);
return rs.next() ;
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.close(rs,stmt,conn);
}
return false ;
}
}
十四.连接池
1.什么是连接池
是一种容器(集合),存储数据库连接对象;防止用户操作数据库的时候频繁,创建连接对象,而且当数据库连接Connection使用完毕,就释放close()---->消耗内存空间!
作用:提供操作数据库性能
当用户发送请求,需要连接数据库,频繁创建连接对象,还需要释放,这种情况耗时,消耗内存空间大
启用连接池,用户发送请求,直接通过连接池操作里面已经创建好的连接对象,获取当前线程中的某个Connection,然后直接去操作数据库,使用完毕连接对象之后,从当线程中进行解绑,回到连接池中!
提供固定的可重用的连接对象的容器
连接池的技术:
c3p0
dbcp
druid(推荐) 德鲁伊 阿里的开源项目
...
连接池本质就是需要实现sun公司提供的DataSource实现类(物理数据源代替 DriverManager)
1)导包 druid-1.1.10.jar
druid-1.1.10.source.jar:原码包(下载看DruidDataSource原码)
DruidDataSource 实现类
需要数据库驱动包:连接数据库mysql-connector-java-8.0.23.jar
2)准备好数据库连接池的配置文件
配置文件以.properties :
druid.properties
配置文件名称可以任意命名,但是必须在src目录下
3)读取配置文件
如何创建DataSource接口对象?
DruidDataSource-------> 此时会使用工厂模式
DruidDataSourceFactory工厂类
提供静态功能:创建数据源DruidDataSource
public static DataSource createDataSource(Properties properties) throws Exception
/*
1)导包 数据库驱动包/连接池的包
2)准备druid.properties
3)读取druid.properties配置文件
*/
public class DruidDataSourceDemo {
public static void main(String[] args) throws Exception {
//创建属性集合列表
Properties prop = new Properties() ;
//读druid.properties配置文件
InputStream inputStream = DruidDataSourceDemo.class.getClassLoader().getResourceAsStream("druid.properties");
//将druid.properties配置文件加载到属性列表中
prop.load(inputStream);
//通过Druid连接池提供的工厂类创建数据源对象DataSource 接口
//public static DataSource createDataSource(Properties properties) throws Exception
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
//底层起始就是 子实现类对象DruidDataSoure
//Connection getConnection()
for(int x = 1; x <= 11 ; x ++){
Connection connection = dataSource.getConnection();
if(x == 5){
connection.close(); //并不是真正释放,而是归还到连接池中,等待下一次利用!
}
System.out.println(connection) ; //com.mysql.cj.jdbc.ConnectionImpl@2a17b7b6
}
// connection.close(); //并不是真正释放,而是归还到连接池中,等待下一次利用!
}
}
1.连接池的工具类
/**
* 工具类---->DataSource----->获取数据库的连接对象 Connection以及后期管理事务
* 获取连接对象----静态方法
* 关闭资源-----静态方法
*/
创建配置文件:
/*druid.properties:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb_01?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
username=root
password=123456
#初始化连接数量
initialSize=5
#最大激活数量
maxActive=10
#最大等待时间(毫秒值,一旦超过3秒就报错了)
maxWait=3000
*/
public class DruidJdbcUtils {
//成员变量位置
private static DataSource ds ;
//为了保证线程安全:每一线程使用自己的Connection (张三/李四)
private static ThreadLocal<Connection> t1 = new ThreadLocal<>() ; //提供线程的局部变量保存连接对象
//构造方法私有化
private DruidJdbcUtils(){}
//静态代码块
static{
try {
//读取数据库连接池的配置文件----->通过DruidDataSourceFactory工厂类创建DataSource
//创建一个属性集合列表
Properties prop = new Properties() ;
//读取druid.properties
InputStream inputStream = DruidJdbcUtils.class.getClassLoader().
getResourceAsStream("druid.properties");
//将资源文件所在的输入流加载列表中
prop.load(inputStream);
ds = DruidDataSourceFactory.createDataSource(prop); //底层子实现类:DruidDataSource
//System.out.println("数据源获取成功");
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//提供静态方法:单独获取数据源
public static DataSource getDataSource(){
return ds ;
}
//获取连接对象Connection静态功能
public static Connection getConnection(){
//从ThreadLocal中获取局部变量的副本:Connection
/**
* public T get() :从线程中获取局部变量的副本!
*/
Connection conn = null ;
try {
conn = t1.get();
if(conn==null){
//如果空,需要从数据库的连接池中获取连接对象
conn = ds.getConnection();
//获取到之后,每一线程执行自己的Connection
//将获取到的连接对象 绑定到当前线程中
t1.set(conn);
}
//如果不为空,说明ThreadLocal线程中已经存在Connection
return conn ; //
} catch (SQLException e) {
e.printStackTrace();
}
return null ;
}
//关闭(释放资源)资源
public static void close(ResultSet rs, Statement stmt,Connection conn) {
if(rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
//关闭之后,归还到连接池中,需要从当前线程中解绑
t1.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close( Statement stmt,Connection conn) {
close(null,stmt,conn);
}
public static void rollback() {
try {
Connection connection = DruidJDBCTUtils.getConnection() ;
//调用回滚方法
connection.rollback();
//使用完毕,归还连接池中
connection.close();
//从当前线程中解绑
t.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2. Druid的应用
管理员实体类Admin
public class Admin {
/* FieldTypeComment
id int NOT NULL
usernam evarchar(20) NULL
gender varchar(5) NULL
age int NULL
address varchar(50) NULL
phone varchar(11) NULL*/
private int id ;
private String username ;
private String gender ;
private int age ;
private String address ;
private String phone ;
public Admin() {
}
public Admin(int id, String username, String gender, int age, String address, String phone) {
this.id = id;
this.username = username;
this.gender = gender;
this.age = age;
this.address = address;
this.phone = phone;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Admin{" +
"id=" + id +
", username='" + username + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", address='" + address + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
针对Admin业务接口层AdminService
public interface AdminService {
/**
* 查询所有的Admin管理员实体
* @return 返回列表数据
*/
List<Admin> getAllAdmin() ;
/**
* 通过id 查询管理员
* @param id 管理员编号
* @return 返回实体对象
*/
Admin getAdmin(int id) ;
/**
* 查询总记录数
* @return
*/
int getCount() ;
}
针对Admin的业务接口实现层AdminServiceImpl
public class AdminServiceImpl implements AdminService {
@Override
public List<Admin> getAllAdmin() {
//创建数据库访问接口对象AdminDao
AdminDao adminDao = new AdminDaoImpl() ;
List<Admin> list = adminDao.findAll();
return list;
}
/**
* 获取指定的管理员
* @param id 管理员编号
* @return
*/
@Override
public Admin getAdmin(int id) {
//调用dao层
AdminDao adminDao = new AdminDaoImpl() ;
Admin admin = adminDao.selectAdminById(id) ;
if(admin!=null){
return admin;
}else{
System.out.println("没有查找到管理员");
}
return null ;
}
@Override
public int getCount() {
//调用dao层
AdminDao adminDao = new AdminDaoImpl() ;
int count = adminDao.selectTotalCount() ;
return count;
}
}
针对Admin的数据库访问接口层AdminDao
public interface AdminDao {
/**
* 数据库访问的查询所有的管理员列表
* @return 返回列表数据
*/
List<Admin> findAll() ;
/**
* 根据id编号查询指定的管理员
* @param id 编号
* @return 获取实体
*/
Admin selectAdminById(int id);
/**
* 查询总记录数
* @return
*/
int selectTotalCount();
}
针对Admin的数据库访问接口实现层AdminDaoImpl
public class AdminDaoImpl implements AdminDao {
Connection conn = null ;
PreparedStatement stmt = null ;
ResultSet rs = null ;
@Override
public List<Admin> findAll() {
try {
//获取连接对象
conn = DruidJdbcUtils.getConnection();
//准备sql
String sql ="select * from admin" ;
//创建预编译对象
stmt = conn.prepareStatement(sql) ;
rs = stmt.executeQuery() ;
//创建List
List<Admin> list = new ArrayList<>() ;
//声明admin变量 Admin类型的
Admin admin = null ;
while(rs.next()){
admin = new Admin() ;
int id = rs.getInt("id");
String username = rs.getString("username");
String gender = rs.getString("gender");
int age = rs.getInt("age");
String address = rs.getString("address");
String phone = rs.getString("phone");
//封装Admin实体
admin.setId(id);
admin.setUsername(username);
admin.setGender(gender);
admin.setAge(age);
admin.setAddress(address);
admin.setPhone(phone);
//添加到集合中
list.add(admin) ;
}
return list ;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DruidJdbcUtils.close(rs,stmt,conn);
}
return null;
}
//查询单个实体
@Override
public Admin selectAdminById(int id) {
//获取数据库的连接对象
try {
//获取连接对象
conn = DruidJdbcUtils.getConnection();
//准备sql
String sql ="select * from admin where id = ?" ;
//创建预编译对象
stmt = conn.prepareStatement(sql) ;
//参数赋值
stmt.setInt(1,id);
//执行更新
rs = stmt.executeQuery() ;
Admin admin = null ;
while(rs.next()){
admin = new Admin() ;
int adminId = rs.getInt("id");
String username = rs.getString("username");
String gender = rs.getString("gender");
int age = rs.getInt("age");
String address = rs.getString("address");
String phone = rs.getString("phone");
//封装
admin.setId(adminId);
admin.setUsername(username);
admin.setGender(gender);
admin.setAge(age);
admin.setAddress(address);
admin.setPhone(phone);
}
return admin ;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DruidJdbcUtils.close(rs,stmt,conn);
}
return null;
}
@Override
public int selectTotalCount() {
//获取数据库的连接对象
try {
//获取连接对象
conn = DruidJdbcUtils.getConnection();
//准备sql
String sql ="select * from admin" ; //全部数据
//创建预编译对象
stmt = conn.prepareStatement(sql) ;
//执行更新
rs = stmt.executeQuery() ;
//int getRow():ResultSet 获取行数 (第一行1,第二行2)
int countRow = 0 ;
while(rs.next()) {
countRow ++ ;
}
return countRow ;
} catch (SQLException e) {
e.printStackTrace();
}finally {
DruidJdbcUtils.close(rs,stmt,conn);
}
return 0 ;
}
}
测试类
public class MyTest {
/* public static void main(String[] args) {
//调用业务层Service
AdminService adminService = new AdminServiceImpl() ;
List<Admin> list = adminService.getAllAdmin();
if(list!=null){
for (Admin admin : list) {
System.out.println(admin);
}
}
}*/
//测试查询功能所有
//单元测试方法
@Test
public void testFindAll(){
//调用service
AdminService adminService = new AdminServiceImpl() ;
List<Admin> list = adminService.getAllAdmin();
if(list!=null){
for (Admin admin : list) {
System.out.println(admin);
}
}
}
//测试查询某个管理员
@Test
public void testfindById(){
//调用service
AdminService adminService = new AdminServiceImpl() ;
Admin admin = adminService.getAdmin(2);
System.out.println(admin);
}
//测试查询总记录数
@Test
public void testSelectTotalCount(){
//调用service
AdminService adminService = new AdminServiceImpl() ;
int totalCount = adminService.getCount();
System.out.println(totalCount);
}
}
3.单元测试
1.黑白盒测试
黑盒测试: 测试某个功能,针对测试人员来说不需要编写代码. 输入参数----->某个模块功能------>输出结果(不看过程)
白盒测试: 需要懂开发流程,编写相关的功能进行测试
编写测试用例
测试登录/注册
通过Junit.jar包来完成功能测试
提供类:断言
预期的结果和调用某个的功能获取的结果是否匹配
单元测试:
java项目:需要导入核心jar包
junit-版本号4.13.1.jar
依赖包 hamcrest-core-1.3.jar
以后maven项目(项目管理工具:管理jar包)
junit-版本号4.13.1.jar
4.引入Dbutiles
1)导包 核心工具包
commons-dbutils-1.7.jar
依赖包 commons-logging:支持相关的
commons-collections:工具类中涉及其他的api
2)获取数据库的连接对象Connection
3)获取执行对象:通过物理数据源DataSource:java.sql.DataSource(sun公司提供一个接口)---->替代了DriverManager
QueryRunner
//基本使用
public class DbUtilsDemo {
public static void main(String[] args) throws SQLException {
//1)创建执行对象QueryRunner (执行对象)
// public QueryRunner(DataSource ds) { //数据源
// super(ds);
// }
//属于自动提交
QueryRunner queryRunner = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//需要给ee_2106的admin表 添加一条数据
//插入数据
String sql = "insert into admin(username,gender,age,address,phone) values(?,?,?,?,?)" ;
//执行通用方法:update()DML语句 ,query()DQL
// updateS(String sql,Object...parames):参数1sql 参数2:可变参数,可以使用多个实际参数
int count = queryRunner.update(sql, "王宝强", "男", 20, "西安市", "13566662222");
System.out.println(count);
//不需要关闭资源
}
}
5.使用Dbutiles实现增删查改
Admin管理员类
public class Admin implements Serializable {
private int id ;
private String username ;
private String gender ;
private int age ;
private String address ;
private String phone ;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() { //getXXX() get()去掉 username 称为 当前类bean属性
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Admin{" +
"id=" + id +
", username='" + username + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", address='" + address + '\'' +
", phone='" + phone + '\'' +
'}';
}
}
针对Admin业务接口层
public interface AdminService {
/**
* 获取所有管理员列表
* @return 列表
*/
List<Admin> getAllAdmin() ;
/**
* 查询编号获取Admin实体
* @param id 编号
* @return 返回实体对象
*/
Admin getAdmin(int id) ;
/**
* 查询总记录
* @return
*/
long getTotal() ;
/**
* 根据id修改admin
* @param admin
* @return
*/
int update(Admin admin) ;
}
针对Admin的业务接口实现类
public class AdminServiceImpl implements AdminService {
@Override
public List<Admin> getAllAdmin() {
//调用数据库访问接口
AdminDao adminDao = new AdminDaoImpl() ;
List<Admin> list = adminDao.findAll() ;
if(list!=null){
return list ;
}
return null ;
}
@Override
public Admin getAdmin(int id) {
//调用数据库访问接口
AdminDao adminDao = new AdminDaoImpl() ;
Admin admin = adminDao.findAdminById(id) ;
if(admin!=null){
return admin ;
}
return null;
}
@Override
public long getTotal() {
//调用数据库访问jiekou
AdminDao adminDao = new AdminDaoImpl() ;
long count = adminDao.selectTotalCount() ;
return count;
}
@Override
public int update(Admin admin) {
//1)修改管理员数据,需要通过id查询Admin
AdminDao adminDao = new AdminDaoImpl() ;
//通过编号查询Admin
Admin selectAdmin = adminDao.findAdminById(admin.getId());
if(selectAdmin !=null){
//存在
//进行修改
int count = adminDao.updateAdmin(admin) ;
return count ;
}
return 0;
}
public static void main(String[] args) {
AdminService adminService = new AdminServiceImpl() ;
Admin admin = new Admin() ;
admin.setId(3) ;
admin.setUsername("张三丰");
admin.setGender("男");
admin.setAge(20);
admin.setAddress("南窑国际");
admin.setPhone("13588889999");
int count = adminService.update(admin);
System.out.println(count);
}
}
针对Admin数据库访问接口
public interface AdminDao {
/**
* 获取所有管理员列表
* @return 列表
*/
List<Admin> findAll();
/**
* 查询编号获取Admin实体
* @param id 编号
* @return 返回实体对象
*/
Admin findAdminById(int id);
/**
* 查询总记录
* @return
*/
long selectTotalCount();
/**
* 修改admin
* @param selectAdmin
* @return
*/
int updateAdmin(Admin selectAdmin);
}
针对Admin的数据库访问接口实现类
public class AdminDaoImpl implements AdminDao {
@Override
public List<Admin> findAll() {
try {
//创建执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//准备sql语句
String sql = "select * from admin" ;
//执行查询
//query(String sql,ResuletSetHandler handler)
//参数1:sql
//参数2:结果集的处理
//子类:BeanListHandler<T> 将查询的多条记录封装到List集合中
//List<当前指定的javeBean实体>
// BeanListHandler<将查询的结果封装实体类型>(当前实体的类Class)
List<Admin> list = qr.query(sql, new BeanListHandler<Admin>(Admin.class));
return list ;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//通过id查询Admin实体
@Override
public Admin findAdminById(int id) {
try {
//创建执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "select * from admin where id = ?" ;
//执行查询
//query(String sql,ResultSetHandler handler,Object...params)
//将查询的某一条记录封装到实体类(JavaBean)中,使用到类BeanHandler<T>
Admin admin = qr.query(sql, new BeanHandler<Admin>(Admin.class), id);
return admin;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//查询总记录数
@Override
public long selectTotalCount() {
//QueryRunner执行对象
try {
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "select count(id) from admin" ;
//查询一些单行单列的数据(总记录数),使用单类ScalerHandler<>
long count = (Long)qr.query(sql, new ScalarHandler<>());
return count ;
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
//修改Admin
@Override
public int updateAdmin(Admin selectAdmin) {
try {
//QueryRunner
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "update admin set username = ? ,gender =?,age = ?,address = ? ,phone =? where id = ?" ;
//更新操作
int count = qr.update(sql,
selectAdmin.getUsername(),
selectAdmin.getGender(),
selectAdmin.getAge(),
selectAdmin.getAddress(),
selectAdmin.getPhone(),
selectAdmin.getId());
System.out.println(count);
return count ;
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
}
测试类(单元测试)
public class DbutilsTest {
@Test
public void testFindAll(){
//调用业务层方法
AdminService adminService = new AdminServiceImpl() ;
List<Admin> list = adminService.getAllAdmin();
for (Admin admin : list) {
System.out.println(admin);
}
}
@Test
public void testFindById(){
AdminService adminService = new AdminServiceImpl() ;
Admin admin = adminService.getAdmin(3);
System.out.println(admin);
}
@Test
public void testSelectTotalCount(){
AdminService adminService = new AdminServiceImpl() ;
long count = adminService.getTotal();
System.out.println(count);
}
@Test
public void testUpdateAdmin(){
AdminService adminService = new AdminServiceImpl() ;
//创建Admin
Admin admin = new Admin() ;
admin.setId(3) ;
admin.setUsername("张三丰2");
admin.setGender("男2");
admin.setAge(22);
admin.setAddress("南窑国际2");
admin.setPhone("13588889888");
int count = adminService.update(admin);
System.out.println(count);
}
}
十五. 注解
- 注解:Java中针对 类的编译,加载,运行时提供一种特殊的代码标记
可以被解析-----就是通过反射实现的
1.普通注解
@Override:一般方法中使用,标记这个方法是否是重写方法
@SupressWarning:压制警告 (项目部署上线的时候,项目中不能出现黄色警告线)
@Deprecated:标记方法已经过时
@FunctionalInterface:标记某个接口是否为函数式接口
接口中有且仅有一个抽象方法
人类
public class Person {
public void show(){
System.out.println("show person...");
}
}
工人类
//工人类继承人类
//给worker去使用 @MyAnno
//使用某一个注解,那么要给注解中的所有的属性赋值
@MyAnno(name="高圆圆",age=20,direction = MyEnum.LEFT,value = @MyAnno2("hello"),strs = {"aaa","bbb","ccc"})
public class Worker extends Person {
@Override
public void show() {
System.out.println("show worker....");
}
//@Override报错
@Deprecated // 标记这个方法为过时方法
public void method(){}
}
测试类
public class Test {
public static void main(String[] args) {
//创建Worker
Worker worker = new Worker() ;
worker.method();
}
}
2. 元注解
@Target:当前这个注解能够使用的范围
ElementType[] value(); value属性(“抽象方法名”) 返回值枚举数组类型
ElementType 枚举中的都是常量字段 (等价于 public static final …)
TYPE,能够使用在类上或者接口上
FIELD:能够使用在成员变量上
METHOD:能够使用在成员方法上
//自定义的枚举类
public enum MyEnum {
//常量
FRONT,
RIGHT,
BEHIND,
LEFT;
}
@Retention:标记当前这个某个注解保留的阶段
RetentionPolicy value(); value属性 它的返回值枚举类
RetentionPolicy
SOURCE 原码编译阶段
CLASS 类的加载,反射阶段
RUNTIME 类的运行阶段
注解(本质接口)中的方法名----- 称为"属性"
属性中可以是什么样的数据类型呢?
基本数据类型
String类型
枚举类型
注解类型
以上类型的数组格式
public @interface MyAnno {
//字符串类型
String name() ; //name属性
//基本数据类型
int age() ; // age属性
//枚举类型
MyEnum direction() ;//direction
//注解类型
MyAnno2 value() ;//value属性
//以上类型的数组格式
String[] strs() ;//strs属性
}
3. 自定义注解
1)自定义一个注解 @Annotation
2)在加上一些元注解,标记当前你自己的定义这个的@Target:作用的位置
@Retention:保留的阶段(Runtime,source,class)
3)如何解析自定义注解
反射的方式
public <A extends AnnotationA getAnnotation(Class<A annotationClass)
参数:表示当前注解类型的Class
4.元注解的测试题
学生类
public class Student {
public void love(){
System.out.println("爱学习,爱Java");
}
}
工人类
public class Worker {
public void love(){
System.out.println("爱生活,爱高圆圆,爱Java..");
}
}
元注解类
/元注解
@Target(ElementType.TYPE) //@MySelfAnno就可以作用在类上
@Retention(RetentionPolicy.RUNTIME) //@MySelfAnno保留在运行阶段 (一般自定义注解:设计层面,考虑运行阶段)
public @interface MySelfAnno {
//定义一些属性(方法---返回值类型(五种))
String className() ;
String methodName() ;
}
三种方式的测试类
@MySelfAnno(className ="com.qf.annotation_03.Worker",methodName = "love")
public class Test {
public static void main(String[] args) throws Exception {
//创建一个学生类
Student s = new Student() ;
s.love();
//需求改进了:使用Worker类
Worker worker = new Worker() ;
worker.love();
//需要不断改进,一会创建学生类,一会创建工人类,就会不断修改代码
//开发的设计原则:开闭原则:对修改关闭,对扩展开放
System.out.println("---------------------------------------");
//创建一个属性集合类
Properties properties = new Properties() ;
//读取src下面的classname.properites
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("classname.properties");
//将资源文件输入流对象加载属性列表中
properties.load(inputStream);
//通过key获取value
String className = properties.getProperty("className");//全限定名称
String methodName = properties.getProperty("methodName");//方法名
//反射技术
Class clazz = Class.forName(className);
//创建当前类实例
Object obj = clazz.newInstance();
//获取方法的Method类对象
Method method = clazz.getMethod(methodName);
//调用方法
method.invoke(obj) ;
System.out.println("-----------------------------");
//解析当前类Test上的 @MySelfAnno注解
//1)获取当前类的字节码文件对象
Class testClass = Test.class ;
//2)当前类的Class类对象获取注解的实例
//public <A extends Annotation> A getAnnotation(Class<A> annotationClass)
// 参数:表示当前注解类型的Class
//获取的公共接口Annotation--->向下转型 具体的注解类型
MySelfAnno annotation = (MySelfAnno) testClass.getAnnotation(MySelfAnno.class);
/**
* public interface MySelfAnno{
*
*
* String className() ;
* String methodName() ;
* }
*
* public class XXX implements MySelfAnno{
*
*
* 重写了
* public String className(){
* return "com.qf.annotation_03.Student" ;
* }
* public String methodName(){
*
* return "love" ;
* }
*
* }//子类
*
*
*/
String className1 = annotation.className();
String methodName1 = annotation.methodName();
//继续反射
Class clazz1 = Class.forName(className1);
//创建当前类实例
Object clazzObj = clazz1.newInstance();
//获取当期类的Method
Method method1 = clazz1.getMethod(methodName1);
method1.invoke(clazzObj) ;
}
}
5.JDBC管理事物
事务:
一种机制,某个业务中多次调用dao完成多个sql同时执行,要么同时执行成功/要么同时执行失败.否则,就出现数据紊乱! 使用事务管理(JDBC方式操作)
用户发送请求,通过连接池获取Connection对象
Connection:管理事务的功能
void setAutoCommit(boolean autoCommit) throws SQLException
默认情况下,新创建的连接对象处于自动提交模式 参数为true,声明为false,禁用自动提交需要收到提交sql
void rollback() throws SQLException:当前执行提交之前,如果sql发生异常,将撤销之前的所有操作
void commit() SQLException:如果执行过程中没有问题或者回滚了,都提交事务,将之前的所有操作永久保存!
测试题转账
需求:mydb_01数据库中的account表
zhangsan账户---- lisi账户 转账500
1)获取连接对象
2)准备sql语句
两个update的sql
zhagnsan -500
lisi+ 500
3)分别执行
4)释放资源
public class JdbcTransactionDemo {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement stmt1 = null;
PreparedStatement stmt2 = null;
try {
//通过DruidJdbcUtils获取连接对象
connection = DruidJdbcUtils.getConnection();
//开启事务---将自动提交禁用调用,参数为false,手动提交
connection.setAutoCommit(false);
//准备sql语句
String sql1 = "update account set balance = balance - ? where id = ?" ;
//创建预编译对象对sql1进行预编译
stmt1 = connection.prepareStatement(sql1);
//参数赋值
stmt1.setInt(1,500);
stmt1.setInt(2,1);
//执行更新
stmt1.executeUpdate() ;
//程序出问题了
// int i = 10 /0 ;
String sql2 = "update account set balance = balance + ? where id = ? " ;
//创建预编译对象对sql2进行预编译
stmt2 = connection.prepareStatement(sql2);
//参数赋值
stmt2.setInt(1,500);
stmt2.setInt(2,2);
//执行更新
stmt2.executeUpdate() ;
//如果没有问题,正常提交
connection.commit();
System.out.println("转账成功...");
} catch (SQLException e) {
//处理异常: 直接事务回滚
//有异常,执行catch语句,就回滚
try {
System.out.println("程序出问题了...");
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
//e.printStackTrace();
}
//释放资源
DruidJdbcUtils.close(stmt1,connection);
DruidJdbcUtils.close(stmt2,connection);
}
}
十六.HTML
html: Hyper Text Markup Language:超文本标记语言
h5的一些标签对视频或者音频操作!
通过html标签 写半成品的网页 ----- 类似于房屋的主体结构
通过css(层叠样式表):能针对html标签进行修饰 ----类似于房屋的装修
通过js(javascript):实现效果(轮播图,js事件编程)----类似房屋中的功能
1.文本标签
<title>01_html文本标签</title>
2.标题标签(h1-h6)
<!-- 标题标签 h1-h6:从大到小 闭合标签:有开始有结束-->
<h1>JavaEE</h1>
<h2>JavaEE</h2>
<h3>JavaEE</h3>
<h4>JavaEE</h4>
<h5>JavaEE</h5>
<h6>JavaEE</h6>
3.滚动标签(marquee)
<!-- 滚动标签marquee
不指定滚动方向,默认从右到左滚动
属性:
behavior:滚动方式
slide:滚动到一边停止
alternate:来回滚动
scroll:(交替滚动 碰到边框不会停止)
direction:滚动的方向:默认值 left(右到左)
scrollamount:滚动的速度: 正整数值(越大,速度快)
bgcolor:背景色
-->
<marquee behavior="scroll"direction
="right" scrollamount="22" bgcolor="pink">hello,html</marquee>
4.段落标签(闭合标签)§
<!-- 段落标签p 闭合标签 -->
<p>
高圆圆,原名高园园,1979年10月5日出生于北京市丰台区,祖籍河北唐山,中国内地影视女演员、模特。1996年,被广告公司发掘,随后拍摄大量的商业广告,在广告圈中崭露头角。1997年,主演个人首部大银幕作品《爱情麻辣烫》,从此开始了她的演艺生涯。2003年,凭借古装武侠爱情剧《倚天屠龙记》受到广泛关注。2005年,因在剧情片《青红》中饰演女主人公青红入围戛纳电影节最佳女主角奖。2006年,凭借喜剧
</p>
<p>
1996年,被广告公司发掘,随后拍摄大量的商业广告,在广告圈中崭露头角。1997年,主演个人首部大银幕作品《爱情麻辣烫》,从此开始了她的演艺生涯。2003年,凭借古装武侠爱情剧《倚天屠龙记》受到广泛关注 [2] 。2005年,因在剧情片《青红》中饰演女主人公青红入围戛纳电影节最佳女主角奖 [3] 。2006年,凭借喜剧动作片《宝贝计划》入围百花奖最佳女演员提名 [4] 。2007年,因在爱情电影《男才女貌》中饰演聋哑幼师秦小悠获得第11届电影表演艺术学会奖新人奖 [5] 。2010年,凭借都市爱情电影《单身男女》提名第31届香港电影金像奖最佳女主角、华语电影传媒大奖观众票选最受瞩目女演
</p>
5.换行标签(非闭合标签)(br)
<!-- 换行标签 非闭合标签 br -->
hello,高圆圆 <br />
hello,马三奇<br />
6.水平标签(非闭合标签)(hr)
<!-- 水平标签 非闭合标签 hr -->
<hr/>
7.上下标标签(sup和sub)
<!-- 上下标标签sup和sub 应用场景:在门户系统最后一行 注册商品以及版权所有 -->
数学公式:x^2 <br />
x<sup>2</sup> <br />
化学公式: H<sub>2</sub>O
<br/>
8.转义字符
<!-- html转义字符
版权所有 : © 代表 © 版权所有
注册商品 : ® 代表 ®
空格 : 一个 代表一个空格
  一个转义字符 代表两个空格
-->
xxx公司版权所有2021-2022<sup>©</sup> <br/>
xx公司注册商品<sup>®</sup><br/>
中  国<br />
9.加粗标签(strong/b)
<!-- 加粗标签 strong / b
语义强调的加粗使用b标签,一般通用strong
-->
<strong>阿富汗</strong>
<b>奥拉夫</b><br />
10.倾斜标签(em/i)
<!-- 倾斜标签 em/ i
义强调的加粗使用i标签, 一般通用em
-->
<em>路飞</em>
<i>索隆</i>
11.居中标签(center)
<!-- 居中标签center 将文本内容进行居中-->
<center>HTML</center>
12.原样输出标签(pre)
<!-- 原样输出标签 pre -->
<pre>
举头望明月,低头思故乡.
funciton hello(){
alert("hello") ;
}
</pre>
13.列表标签
<!-- 列表标签
无序列表ul li (列表项)
有序列表ol li
-->
<br/>
以下的明星喜欢谁?
<!-- 无序列表
ul:
type属性 :指定列表项前面的标记disc
-->
<ul type="square">
<li>高圆圆</li>
<li>姆巴佩</li>
<li>赵又廷</li>
</ul>
<hr/>
以下的明星喜欢谁?
<ol type="1">
<li>高圆圆</li>
<li>姆巴佩</li>
<li>赵又廷</li>
</ol>
14.超链接标签(a)
超链接a标签来表示
协议:
ftp
http协议
thunder://
超链接标签的属性
href: 后面跟统一资源定位符 url 以后本地地址或者服务器地址
需要加上协议:http://www.baidu.com
http协议请求规则
C:\Windows\System32\drivers\etc
hosts文件
记录ip地址 域名
127.0.0.1(本地回环地址) localhost
如果在hosts文件本地找不到www.baidu.com对应的ip地址那么就会调用网卡联网操作
DNS服务器(网络运营商)
ip地址1 www.baidu.com
ip地址2 www.sina.com.cn
回显百度首页给用户
指定服务器地址
http://locahost:8080/后台地址
url地址:统一资源定位符
http:// 域名:端口号/
端口号:80端口可以省略不写
target普通用法:打开方式
_blank:新建窗口打开
_self:当前窗口打开
如果现在在框架标签中使用target 指定哪个frame中打开
超链接标签的用法
1)作为资源跳转进行使用
2)作为锚链接来使用
同一个页面(使用居多)
a)打锚点(创建一个跳转标记)
b)创建跳转链接
跳转
不同页面跳转
a)打锚点(创建一个跳转标记) :在另一个页面的某个位置
b)在当前的某个位置创建跳转链接
跳转到另一个页面链接
<!-- 跳转到另一个页面链接 -->
<a href="00_html文本标签.html#ul" target="_self">跳转到指定页面的某个位置</a><br/>
跳转标记
<!-- 跳转标记 -->
<a name="top"></a>
<a href="#foot">[跳转底部]</a><br/>
<a href="http://www.baidu.com:80">链接到百度</a><br/>
<a href="http://14.215.177.39:80">链接到百度</a><br/>
<a href="thunder://www.dytt8.net"><<特种部队.中英双字>></a>
<p>
20世纪90年代,硬件领域出现了单片式计算机系统,这种价格低廉的系统一出现就立即引起了自动控制领域人员的注意,因为使用它可以大幅度提升消费类电子产品(如电视机顶盒、面包烤箱、移动电话等)的智能化程度。Sun公司为了抢占市场先机,在1991年成立了一个称为Green的项目小组,帕特里克、詹姆斯·高斯林、麦克·舍林丹和其他几个工程师一起组成的工作小组在加利福尼亚州门洛帕克市沙丘路的一个小工作室里面研究开发新技术,专攻计算机在家电产品上的嵌入式应用。
</p>
<p>
由于C++所具有的优势,该项目组的研究人员首先考虑采用C++来编写程序。但对于硬件资源极其匮乏的单片式系统来说,C++程序过于复杂和庞大。另外由于消费电子产品所采用的嵌入式处理器芯片的种类繁杂,如何让编写的程序跨平台运行也是个难题。为了解决困难,他们首先着眼于语言的开发,假设了一种结构简单、符合嵌入式应用需要的硬件平台体系结构并为其制定了相应的规范,其中就定义了这种硬件平台的二进制机器码指令系统(即后来成为“字节码”的指令系统),以待语言开发成功后,能有半导体芯片生产商开发和生产这种硬件平台。对于新语言的设计,Sun公司研发人员并没有开发一种全新的语言,而是根据嵌入式软件的要求,对C++进行了改造,去除了留在C++的一些不太实用及影响安全的成分,并结合嵌入式系统的实时性要求,开发了一种称为Oak的面向对象语言。
</p>
<p>
由于在开发Oak语言时,尚且不存在运行字节码的硬件平台,所以为了在开发时可以对这种语言进行实验研究,他们就在已有的硬件和软件平台基础上,按照自己所指定的规范,用软件建设了一个运行平台,整个系统除了比C++更加简单之外,没有什么大的区别。1992年的夏天,当Oak语言开发成功后,研究者们向硬件生产商进行演示了Green操作系统、Oak的程序设计语言、类库和其硬件,以说服他们使用Oak语言生产硬件芯片,但是,硬件生产商并未对此产生极大的热情。因为他们认为,在所有人对Oak语言还一无所知的情况下,就生产硬件产品的风险实在太大了,所以Oak语言也就因为缺乏硬件的支持而无法进入市场,从而被搁置了下来。
</p>
<p>
1995年,互联网的蓬勃发展给了Oak机会。业界为了使死板、单调的静态网页能够“灵活”起来,急需一种软件技术来开发一种程序,这种程序可以通过网络传播并且能够跨平台运行。于是,世界各大IT企业为此纷纷投入了大量的人力、物力和财力。这个时候,Sun公司想起了那个被搁置起来很久的Oak,并且重新审视了那个用软件编写的试验平台,由于它是按照嵌入式系统硬件平台体系结构进行编写的,所以非常小,特别适用于网络上的传输系统,而Oak也是一种精简的语言,程序非常小,适合在网络上传输。Sun公司首先推出了可以嵌入网页并且可以随同网页在网络上传输的Applet(Applet是一种将小程序嵌入到网页中进行执行的技术),并将Oak更名为Java(在申请注册商标时,发现Oak已经被人使用了,再想了一系列名字之后,最终,使用了提议者在喝一杯Java咖啡时无意提到的Java词语)。5月23日,Sun公司在Sun world会议上正式发布Java和HotJava浏览器。IBM、Apple、DEC、Adobe、HP、Oracle、Netscape和微软等各大公司都纷纷停止了自己的相关开发项目,竞相购买了Java使用许可证,并为自己的产品开发了相应的Java平台。
</p>
<br/><br/><br/>
<p>
1997年2月,JDK 1.1面世,在随后的3周时间里,达到了22万次的下载量。4月2日,Java One会议召开,参会者逾一万人,创当时全球同类会议规模之纪录。9月,Java Developer Connection社区成员超过10万。
1998年12月8日,第二代Java平台的企业版J2EE发布。1999年6月,Sun公司发布了第二代Java平台(简称为Java2)的3个版本:J2ME(Java2 Micro Edition,Java2平台的微型版),应用于移动、无线及有限资源的环境;J2SE(Java 2 Standard Edition,Java 2平台的标准版),应用于桌面环境;J2EE(Java 2Enterprise Edition,Java 2平台的企业版)`,应用于基于Java的应用服务器。Java 2平台的发布,是Java发展过程中最重要的一个里程碑,标志着Java的应用开始普及。
</p>
创建一个锚点: 跳转标记
<!-- 创建一个锚点:跳转标记 -->
<a name="foot"></a>
跳转链接
<!-- 跳转链接 -->
<a href="#top">[跳转顶部]</a>
<center>xxx公司版权所有<sup>©</sup></center>
15.表格标签(table)
表格标签属性
属性:
显示表格样式 必须指定border 边框
algin:对齐方式
width/height:宽度和高度(px/百分比(占当前系统分辨率权重百分比))
bgColor:背景色
cellspacing:设置边框线和单元格的距离
表格标签的子标签
capation:表格表格标签
tr:行标签
td:单元格标签
th:表格的表头标签:特殊的单元(自动居中,加粗)
单元格合并:td的属性
合并行: rowspan属性
占用的单元格数量
合并列:colspan属性
占用单元格数量
<body>
<!-- 写一个三行四列的表格 -->
<table border="1px" width="100%" height="300px" cellspacing="0"
bgcolor="greenyellow">
<!-- 标题 -->
<caption>xxx学校成绩表</caption>
<tr>
<th>编号</th>
<th>姓名</th>
<th>班级</th>
<th>成绩</th>
</tr>
<tr align="center">
<td>1</td>
<td>高圆圆</td>
<td>Java基础班</td>
<td>98</td>
</tr>
<tr align="center">
<td>2</td>
<td>赵又廷</td>
<td>Java基础班</td>
<td rowspan="2">89</td>
</tr>
<tr align="center">
<td>3</td>
<td>张三丰</td>
<td>计算机1班</td>
<!-- <td>76</td> -->
</tr>
<!-- 平均分 -->
<tr align="center">
<!-- 合并列coslpan -->
<td colspan="3">平均分</td>
<!-- <td></td>
<td></td> -->
<td>80</td>
</tr>
</table>
</body>
表格标签的应用-----布局
<body>
<!-- 以前的早期布局:table布局 弊端:一旦宽度和高度尺寸变化,整个布局就乱了 -->
<table border="1" width="100%" height="100%">
<tr>
<td>
<!-- 第一部分logo部分 -->
<table>
<tr>
<td>
<!-- 图片logo -->
<img src="img/logo2.png" width="100%" />
</td>
<td>
<img src="img/header.png" />
</td>
<td>
<a href="#">登录</a>
<a href="#">注册</a>
</td>
</tr>
</table>
</td>
</td>
</tr>
<tr style="background-color: black;">
<td>
<a href="#">首页</a>
<a href="#">手机数码</a>
<a href="#">笔记本专区</a>
<a href="#">其他</a>
</td>
</tr>
</table>
</body>
16.表单标签(form)
表单标签属性
method:提交方式 默认get请求(用户直接在浏览器访问)
post
action:url 本地提交或者提交后台服务器地址
http://localhost:8080/xxx
表单标签的应用场景
注册: (同步)
需要输入用户相关的信息 点击----“注册”------用户的数据提交后台服务器
—查询用户名是否存在,如果存在,不能进行注册了,“请更换用户名”"
登录(同步)
输入用户名和密码---- 点击登录----服务器里面先通过查询用户名是否存在,
如果存在,就在通过获取到的用户对应的密码和前台提交的密码如果匹配,登录成功,否则登录失败!
get方式和post方式的区别
get方式
1)将用户提交的数据 -----提交到后台地址上地址上
url?key1=value1&key2=value2…
2)因为提交到地址栏上,提交数据大小有限制的
3)不适合提交私密数据,相对post不安全
post方式
1)不会将用户的数据提交地址上
通过浏览器进行查看:
f12—network网络部分查看 post提交参数
request header 请求头
form-data (实体参数)
key1=value1&key2=value2
2)这种方式不提交到地址栏上,提交大小没有限制
3)相对get方式,安全一些
密码后期都需要处理 加密方式 (md5加密)
123456 ---- "随机字符的值是固定的
springSecurity:安全卫士(密码进行加密) 更好一些
123456 每次密码随机不一样
<body>
<form action="server.html" method="post">
<!-- 表单项 中必填name属性:需要给后台提交,后台需要知道书写的参数名称 -->
<!-- 文本输入框
placeholder:H5的属性 提示输入的内容,当鼠标光标获取焦点并输入内容,自动清空
-->
用户名:<input type="text" name="username" placeholder="请输入用户名" /><br/>
<!--
input type="password"密码输入框 非明文
-->
密  码:<input type="password" name="password" placeholder="请输入密码"
/><br/>
<!-- 提交按钮 -->
<input type="submit" value="登录" />
</form>
</body>
表单标签常用表单项目-----输入型
文本输入框,密码输入框,日期组件,邮箱…
必须要指定name属性
input
type=“text” 文本输入框
type=“password” 密码输入框
type="radio"单选按钮
type="checkbox"复选框
type="date"日期格式
type=“eamil” 必须符合邮箱格式@字符
type=“hidden”:隐藏域(没有效果,可以携带数据)
表单标签常用表单项-----下拉菜单
select标签:普通下拉菜单 选择一个
option:下列选项
表单标签常用表单项------文本域
textarea
rows:文本域中指定多少行内容
cols:一行里面指定多少个字符
表单标签常用表单项------按钮
特殊按钮:提交按钮
input
type="submit"提交按钮(将表单中的所有表单项中的内容全部提交后台)
跟 input type="submit"等价的一个标签 button
提交
type=“reset” 重置按钮
普通按钮
type=“button” value=“点击…”
表单标签常用表单项-----举例
<body>
<h3>注册会员</h3>
<form action="server.html" method="get">
<!-- 表单项中必须指定name属性:要给后台标记用户输入的内容 -->
用户名:<input type="text" name="username" placeholder="请输入用户名" /><br />
密  码:<input type="password" name="password" placeholder="请输入密码" /><br />
确认密码:<input type="password" name="repassword" placeholder="请再次输入密码" /><br/>
性别:
<!--
将它同一组信息 name属性值必须一致
-->
<input type="radio" name="gender" value="男" />男
<input type="radio" name="gender" value="女" />女<br />
出生日期
<input type="date" name="birthday" />
<br/>
技术栈:
<!--
将它同一组信息 name属性值必须一致
checked:选中
-->
<input type="checkbox" name="tecknology" value="Spring" />Spring
<input type="checkbox" name="tecknology" value="JDBC" />JDBC
<input type="checkbox" name="tecknology" value="MySQL" />MySQL
<input type="checkbox" name="tecknology" value="MyBatis" />Mybatis<br />
学历:
<!-- multiple 属性:表示多选
multiple="multiple"
-->
<select name="edu" >
<option value="请选择">请选择</option>
<option value="大专">大专</option>
<option value="本科">本科</option>
<option value="研究生">研究生</option>
</select>
<br />
<!-- 文件上传组件 -->
上传照片:
<input type="file" name="img" />
<br />
自我介绍
<textarea rows="5" cols="20">
优秀!
</textarea><br />
<!-- 验证码 -->
<!--
组成:前面是文本输入框
后面可以通过js 的画布来完成也可以通过后台servlet(利用第三方绘制图形)
-->
<!-- <input type="text" placeholder="输入验证码" /> -->
普通按钮
<input type="button" value="点我试试" /><br />
提交按钮
<input type="submit" value="注册" /><input type="reset" value="清空" />
</form>
</body>
格式优雅的表单
form嵌套table
<body>
<form action="server.html" method="get">
<!-- 块标签:占一行 -->
<div>会员注册</div>
<table border="1px" cellspacing="0" width="300px" height="200px">
<tr>
<td>用户名</td>
<td>
<input type="text" name="username" placeholder="请输入用户名" />
</td>
<tr>
<td>密码</td>
<td>
<input type="password" name="password" placeholder="请输入密码" />
</td>
</tr>
<tr>
<td>出生日期</td>
<td>
<input type="date" name="birthday" />
</td>
</tr>
<tr>
<td>性别</td>
<td>
<input type="radio" name="gender" value="男" />男
<input type="radio" name="gender" value="女" />女
</td>
</tr>
<tr>
<td>爱好</td>
<td>
<input type="checkbox" name="hobit" value="跑步" />跑步
<input type="checkbox" name="hobit" value="lol" />lol
</td>
</tr>
<tr align="center">
<td colspan="2">
<input type="submit" value="注册" />
</td>
</tr>
</table>
</form>
</body>
17.框架标签(frameset)
当前这个整个结构4个页面组成
<frameset rows="10%,*,10%">
<!-- 三个部分:分别是frame -->
<frame src="header.html"/><!-- 头部页面 -->
<!-- 从左--到右 -->
<frameset cols="15%,*">
<!-- 两个部分页面通过frame导入 -->
<frame src="menu.html" />
<!-- 给当前中间页面表示的frame给定name -->
<frame src="main.html" name="main" />
</frameset>
<frame src="footer.html" /><!-- 底部页面 -->
</frameset>
顶部(header)
<html>
<head>
<meta charset="utf-8">
<title>logo</title>
</head>
<body>
<img src="../img/index_2.jpg" height="100px" width="95%" />
</body>
</html>
底部(footer)
<html>
<head>
<meta charset="utf-8">
<title>底部页面</title>
</head>
<body>
<center>xxx公司版权所有<sup>©</sup>2021-2025</center>
</body>
</html>
主页(main)
<html>
<head>
<meta charset="utf-8">
<title>主页</title>
</head>
<body>
<p> 欢迎访问该系统</p>
</body>
</html>
18.图像标签(img src)
img src = "图片地址"
src:加载的图片资源地址 使用的是相对路径
width和height:宽度和高度属性指定像素,或者指定占当前整个宽高分辨率的百分比
alt:替代文本,当图片失效是起作用,描述这个图片
title:给图片设置标题内容,当鼠标悬浮在图片时候,提示文字
<body>
<!--
.. 退出当前目录(访问上级目录)
-->
<img src="../img/1.jpg" width="200px" height="300px" alt="手机图片" /><br />
<img src="../img/mm.jpg" width="200px" height="300px" title="高圆圆" />
</body>