day33
晨写
# 晨写
## 1.DML语句添加/删除/修改的操作语法 (表的记录)
```sql
-- 插入全表的字段
insert into 表名 values(值1,值2...值n),(值1,值2,...值n) ,(...) ;
-- 插入部分字段
insert into 表名(字段名称1,字段名称2,...字段名称n) values(值1,值2,...值n),(....),(....) ;
-- 删除(带条件删除)
delete from 表名 where 字段名称 = 值 ;
delete from 表名 where 字段名称 = 值 and 字段名称2 = 值2.... ;
-- 删除全表
delete from 表名;
-- 修改(带条件修改)
update 表名 set 字段名称= 值 ,字段名称2 =值2 ...where 非业务字段id = 值 ;
-- 批量修改
update 表名 set 字段名称 = 值 ,字段名2=值2...;
```
## 2.delete from 表名 和truncate table 表名的区别?
```markdown
共同点:
两个都是将整个表的所有记录数全部删除
不同点:
1)是否会删除表
delete from 表名 ;仅仅是将表的所有记录数删除,不会删除这张表
truncate table 表名; 会将表直接删除(所有数据清空了)
2)是否会影响自增主键id值
delete from 表名,由于不会删除表,表的结构还在,只是表的数据清了,再次去插入数据,会在上次基础上id继续自增(不影响自增长主键id值)
truncate table 表名,由于删除表,自动一张一模一样的空表,将之前的结构就删除了,直接影响了自增长主键id的值;
```
## 3.DQL语句 中的聚合函数列举
```sql
-- 查询某列的最大值
select max(字段名称) from 表名 ; -- min(字段名称)最小值
-- 查询某列的平均分
select avg(字段名称) from 表名 ;
-- 某列的总数:求和
select sum(字段名称) from 表名 ;
-- 统计表的总记录数(总条数)
select count(非业务字段) from 表名;
```
## 4.DQL语句中模糊查询的语法
```sql
select 指定的字段名称(字段列表) from 表名 where 某个字段名称 like '%关键字%' ; -- '关键字%'
```
## 5.基本的带条件的查询语法
```sql
select 字段列表 from 表名 where 某个字段名称= 值;
select 字段列表 from 表名 where 某个字段名称= 值 and 字段名称2 = 值2 ;-- 多个条件
select 字段列表 from 表名 where 某个字段名称 <=或者 >=,!=,<> 某个值 -- 比较运算符
-- between 值1 and 值2 ;
```
----
-----
# 今日内容
## 1.DQL语句
```
1.where条件查询 ----and 并列多个条件
2. 多个or 或 的关系
select 字段列表 from 表名 where 某个字段名称 = 值1 or 字段名称 = 值2 or 字段名称=值3;
3. 某个字段名称 in(值1,值2,值3) ; 等价于上面
4.排序oder by 和where一块使用----where在前面 ,order by 在后面
5.分组查询 group by
6.having :帅选...
7.分页查询 limit
```
## 2.关于数据库的备份和还原
```
备份和还原
--图形界面化方式备份和还原 (简单易于上手)
--命令行的备份和还原(记住一些指令)
```
## 3.数据库的约束 (约束用户操作库中的某张表的行为)
```
每一张表的id,不重复(唯一),非空,设置自增长
```
## 4.设计数据库的三大范式(了解) ---表和表的关系
```
1NF:最起码的基本要求:每一张表的字段必须独立的列!
2NF
3NF
```
## 5.多表查询
```
内连接
隐式内连接
显示内连接
外连接
左外/右外 (推荐左外)
子查询: select 嵌套select
1)聚合函数---和select语句嵌套
2)in(指定的字段的名称:select语句)子查询
3)通过select语句---查询来多行多例的数据---(虚表)和另一张表 进行多表查询
```
MYsql2
-- DQL语句其他的语法
-- 条件查询---in关键字 (值1,值2,值3,,,值n) ; 代表多个or连接(多个并集查询)
-- 需求:查询student3表中年龄是18或者30或者45岁的学生的所有信息
SELECT
*
FROM
student3
WHERE age = 18
OR age = 30
OR age = 45 ;
SELECT
*
FROM
student3
WHERE age IN(18,30,45) ;
-- 条件查询:查询指定的字段是null的信息
-- select 字段列表 from 表名 where 某个字段名称 is null ;
-- 需求:查询student3表中english是null的学生的所有信息
/*
select
*
from
student3
where english = null ; -- 错误语法
*/
SELECT
*
FROM
student3
WHERE english IS NULL ;
-- 查询某个字段不是null的信息: is not null
-- 需求: 查询英语成绩不是null的学生的所有信息
SELECT
*
FROM
student3
WHERE english IS NOT NULL ;
-- DQL语句之排序查询
-- select 字段列表 from 表名 order by 字段名称 排序规则;
-- 排序规则:默认值asc :升序
-- desc:降序
-- 多个排序条件
-- select 字段列表 from 表名 order by 字段名称1 排序规则,字段名称2 排序规则...;
-- 需求:按照学生的数学成绩升序排序
SELECT
*
FROM
student3
ORDER BY math ASC ;-- 默认不写,asc
-- 需求:查询学生的所有信息,同时 数学成绩降序,英语成绩升序 获取学生所有信息
SELECT
*
FROM
student3
ORDER BY math DESC,
english ASC ;
-- 需求:查询数学成绩大于70分的学生的所有信息并且进行升序排序
-- where 在 order by的前面
SELECT
*
FROM
student3
WHERE math > 70
ORDER BY math ASC ;
-- DQL语句之分组查询:group by
/**
group by 后面跟分组字段,在select的时候-查询分组字段
聚合函数在group by的后面不能使用
*/
-- select 字段列表 from 表名 group by 字段名称; -- 分组基本查询
-- 一般情况:group by 和where 搭配使用
-- 需求:按照性别分组,查询数据成绩大于70分的信息;
-- 数学成绩的大于70分的人参与分组,查询出每一组的数学平均分
/*
select
sex '性别'
from
student3
group by sex
where
math > 70 ;
*/
-- 先满足条件:当前where 和group by一块使用,先执行where ,然后在执行group by 分组字段;
SELECT
sex '性别',
AVG(math)
FROM
student3
WHERE math > 70
GROUP BY sex ;
-- 筛选having
/*
一个sql语句,where条件,有分组group by ,有having(它的后面可以跟聚合函数)
依次先后顺序,先满足where,在分组,分组基础上进行筛选
*/
-- 需求:查询(分组信息/数学平均分/人数大于2的) 学生的数学成绩大于70分的参与分组(按照性别分组)
-- 同时,筛选出某组人数大于2的一组!
SELECT
sex '性别', -- 查询分组字段
AVG(math) '数学平均分',
COUNT(id) '总人数' -- 查询满足筛选条件的信息
FROM
student3
WHERE math > 70
GROUP BY sex
HAVING
COUNT(id)> 2 ;
-- DQL语句分页查询
-- limit关键字
-- 语法:select 字段列表 from 表名 limit 起始行数,每页显示的条数;
-- 起始行数:从0开始 --- = (当前页码数-1)*每页显示的条数;
-- 实际开发中:页码数currentPage/每页显示的条数pageSize (必须已知数据:前端传来的)
-- student3:9条数据,已知:每页显示2条,
-- student3的第一页的数据;
SELECT * FROM student3 LIMIT 0,2 ;
-- 第二页数据
SELECT * FROM student3 LIMIT 2,2 ;
-- 第三页数据
SELECT * FROM student3 LIMIT 4,2;
-- 第四页数据
SELECT * FROM student3 LIMIT 6,2 ;
-- 第五也数据
SELECT * FROM student3 LIMIT 8,2;
-- 查询 这个表的时候限制指定的条数
-- select * from 表名 limit 值;
SELECT * FROM student3 LIMIT 4;
-- 数据库的备份和还原
/*
DBA(数据库管理员) 设计完数据库,对库中的所有的数据以及结构进行备份;
防止数据库的信息丢失等等
数据库还原:就是将备份库 加载已知存在的库中(使用备份好.sql脚本文件,执行这个sql脚本)
备份和还原:
1)命令行方式的备份(dos管理员的运行)
mysqldump -uroot -p输入密码 库名称 > 指定磁盘路径 "d:\\mysql_dump\\xx.sql文件"
1)命令行方式的还原:
1.1)dos登录mysql,将之前的库删除
1.2)新建一个新的库,使用库
1.3)source 执行本地或者远程路径上的.sql文件 (加载指定的sql脚本文件)
2)使用图形界面化工具去备份和还原
任何的图形界面化工具都是一样的
备份:
在已知的库上右键---backup--->backup SQL 备份到本地磁盘上的sql脚本
还原:删除已知库,新建库,使用库, 存在库上右键--->import--->选择执行 execute SQL script 本地sql文件
*/
CREATE DATABASE myee_2302;
-- 修改id为5的学生的英语成绩
UPDATE student3 SET english = 91 WHERE id = 5;
-- 添加学生
INSERT INTO student3 VALUES(9,'高圆圆',44,'女','西安',86,100) ;
SELECT * FROM student3;
-- 2.数据库的约束
-- 约束:限制用户操作数据库的行为
-- 举例:插入数据的时候,直接插入null值(这个字段是业务字段,是null,,没有意义)
-- 通过约束将上面的用户插入非法数据的行为进行限制!
-- 2.1默认约束:default
CREATE TABLE test(
id INT, -- 编号
NAME VARCHAR(10), -- 姓名
gender VARCHAR(3) DEFAULT '女' -- 性别 使用默认约束default关键字
) ;
INSERT INTO test VALUES(1,'高圆圆','女'),(2,'文章','男') ;
-- 插入部分字段
-- insert into test(id,name) values(3,'张佳宁') ; -- 没有插入的数据null值(没有意义)
-- 添加默认约束后,没有插入的字段:默认约束default起作用!
INSERT INTO test(id,NAME) VALUES(3,'张佳宁') ;
INSERT INTO test(id,NAME,gender) VALUES(4,'刘亦菲',NULL) ; -- 直接插入null值,default无法约束
DROP TABLE test;
-- 2.2 非空约束not null
CREATE TABLE test(
id INT , -- 编号
NAME VARCHAR(10) NOT NULL , -- 姓名 不能为空
address VARCHAR(50) NOT NULL -- 地址 不能为空
);
INSERT INTO test VALUES(1,'张三丰','武当山'),(2,'令狐冲','泰山') ;
-- 用户直接插入null值(非法数据),加入约束之后:Column 'name' cannot be null
INSERT INTO test VALUES(3,'王五','西安') ;
-- insert into test(id,name) values(4,'李国栋') ;-- 非空约束,不给字段数据,会出现错误
-- sql更改表中默认约束(去掉默认约束)
-- 删除name字段非空约束
ALTER TABLE test MODIFY NAME VARCHAR(10) ;
-- sql 添加字段的not null非空约束
ALTER TABLE test MODIFY NAME VARCHAR(10) NOT NULL ;
-- insert into test values(4,null,'咸阳') ;
DROP TABLE test;
-- 2.3 唯一约束unique不能重复
CREATE TABLE test(
id INT, -- 编号
NAME VARCHAR(10), -- 姓名
telephone VARCHAR(11) UNIQUE -- 手机号 创建表,给手机号添加唯一
) ;
INSERT INTO test VALUES
(1,'高圆圆','13366668888'),
(2,'文章','18788886666') ;
-- 插入一个数据
-- (非法数据了手机号或者邮箱/能够去代表身份信息必须唯一的!)
-- 如果使用手机号获取用户信息:获取不到了!
INSERT INTO test VALUES(3,'李国栋','13366668888') ; -- Duplicate entry '13366668888' for key 'telephone'
-- 通过sql语句更改表,删除唯一删除
-- 错误语法: alter table test modify telephone varchar(11) ;
-- 正常语法:alter table 表名 drop index 唯一约束的名称 (默认和当前的字段名称一致)
ALTER TABLE test DROP INDEX telephone ;
-- 通过sql添加唯一约束
-- alter table 表名 add constraint(声明) 唯一约束索引的名称 unique(给哪个字段名称);
ALTER TABLE test
ADD CONSTRAINT unique_telephone UNIQUE (telephone) ;
-- 2.4 主键约束 primary key
/*
特点:非空且唯一
不能直接插入null值,不能重复
primary key 作用每一张表的id(非业务字段,设置为主键)
*/
CREATE TABLE test(
id INT PRIMARY KEY AUTO_INCREMENT, -- 创建表的时候添加主键,同时自增长
NAME VARCHAR(10)
);
-- insert into test values(1,'张三'),(2,'李四') ;
INSERT INTO test(NAME) VALUES('张三'),('李四') ;
INSERT INTO test VALUES(1,'王五') ;-- id重复 无法添加
INSERT INTO test VALUES(NULL,'赵六') ;-- 直接插入null值 .无法获取
-- sql语句直接主键删除
-- alter table 表名 drop primary key ; -- 将唯一删除,not null非空还存在
ALTER TABLE test DROP PRIMARY KEY ;
-- sql语句添加主键约束(唯一特性加上)
-- alter table 表名 modify id int PRIMARY KEY ;
ALTER TABLE test MODIFY id INT PRIMARY KEY ;
-- 一般主键约束和自增长约束auto_increment,一块用
-- 插入数据insert into 之后,id值不断自增1!
-- id为2的值改成10
UPDATE test SET id = 10 WHERE id = 2 ;
INSERT INTO test (NAME) VALUES('高圆圆') ;
DROP TABLE test ;
SELECT * FROM test ;
-- 2.5 外键约束 foreign key
-- 创建一个员工表employee
CREATE TABLE emplyee(
id INT PRIMARY KEY AUTO_INCREMENT, -- 员工编号
NAME VARCHAR(10), -- 员工姓名
gender VARCHAR(3) , -- 员工性别
salary DOUBLE, -- 员工工资
dept_name VARCHAR(10) -- 员工所在的部门名称
) ;
INSERT INTO emplyee(NAME,gender,salary,dept_name)
VALUES('高圆圆','女',8000.0,'测试部'),
('文章','男',6500.0,'运维部'),
('李国栋','男',10000.0,'开发部'),
('马保国','男',6000.0,'安保部'),
('张三丰','男',9500.0,'开发部'),
('邓超','男',7000.0,'运维部'),
('张佳宁','女',9500.0,'测试部');
SELECT * FROM emplyee ;
-- 查询员工的姓名以及它部门名称信息
SELECT
NAME '员工姓名',
dept_name '部门名称'
FROM emplyee ;
-- 问题:查询员工信息,部门名称查询"字段冗余",大量重复数据不友好!
-- 如何优化呢, 创建两张表,分别描述两件事情:一个部门表:描述部门名称
-- 一个员工表:描述员工信息
DROP TABLE emplyee ;
-- 创建一张部门表:id:部门编号 dept_name:部门的名称
CREATE TABLE dept(
id INT PRIMARY KEY AUTO_INCREMENT, -- 部门编号
dept_name VARCHAR(10) -- 部门名称
) ;
-- 插入部门
INSERT INTO dept(dept_name) VALUES ('测试部'),('运维部'),('开发部'),('安保部') ;
-- 创建员工表:id 员工编号,name 员工姓名,gender 性别,salary工资,员工所在的部门编号dept_id
CREATE TABLE employee(
id INT PRIMARY KEY AUTO_INCREMENT, -- 员工编号
NAME VARCHAR(10), -- 员工姓名
gender VARCHAR(3), -- 员工性别
salary DOUBLE, -- 工资
dept_id INT -- 所在的部门编号
);
INSERT INTO employee
(NAME,gender,salary,dept_id)
VALUES('高圆圆','女',8000.0,1),
('文章','男',6500.0,2),
('李国栋','男',10000.0,3),
('马保国','男',6000.0,4),
('张三丰','男',9500.0,2),
('邓超','男',7000.0,3),
('张佳宁','女',9500.0,2);
-- 新入职一个员工
INSERT INTO employee (NAME,gender,salary,dept_id)
VALUES('王宝强','男',12000.0,5) ;
-- 删除员工表
DROP TABLE employee ;
-- 不存在5号部门,需要建立两张表的关系!
-- 员工表的 dept_id关联dept部门表id字段---外键约束
/*
外键所在的表---从表的某个字段依赖于主表的主键字段
部门表:主表
员工表:从表
*/
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
-- 员工编号
NAME VARCHAR (10),
-- 员工姓名
gender VARCHAR (3),
-- 员工性别
salary DOUBLE,
-- 工资
dept_id INT,
-- 所在的部门编号
-- 声明
CONSTRAINT -- 外键名称:主表名_从表名_fk
dept_emp_fk -- 外键作用在哪个字段上
FOREIGN KEY (dept_id) -- 关联
REFERENCES -- 主表的主键字段(就是id)
dept(id)
) ;
/*
有了外键约束,直接操作主表(删除/修改),直接报错!
因为从表的数据和主表有关联!
*/
-- 删除id为4号部门
-- delete from dept where id = 4; -- 4号部门有员工--Cannot delete or update a parent row:
-- 将4号部门的员工先删除,(没有关联了),在删除主表数据
DELETE FROM employee WHERE dept_id = 4 ;
DELETE FROM dept WHERE id = 4 ;
-- 将1号部门修改为5号部门
-- update dept set id = 5 where dept_name = '运维部' ; -- 也不能直接修改
-- 将2号部门的员工修改为 "其他部门"
UPDATE employee SET dept_id = 2 WHERE NAME = '高圆圆' ;
-- 修改主表将1号--5号
UPDATE dept SET id = 5 WHERE dept_name = '测试部' ;
UPDATE employee SET dept_id = 5 WHERE NAME = '高圆圆' ;
SELECT * FROM dept ;
SELECT * FROM employee ;
day34
晨写
# 晨写
## 1.分页查询的语法
```sql
select 指定的字段列表 from 表名 limit 起始索引,每页显示的条数;
-- 起始索引 = (当前页码数-1)*每页显示的条数;
-- 当前页码数以及 每页显示的条数----前端传过来的数据!
-- 后端 pageBean:自定义分页组件
class PageBean<T>{
private int currentPage; 当前页码
private int pageSize ; 每页显示的条件
private int totalPage; 总页数 ---
-- (totalCount % pageSize == 0)?totalCount/pageSize:向上取整;
private int totalCount ; 总记录数
private List<T> list ; 分页的列表数据
}
```
## 2.通过sql添加/删除为唯一约束的语法
```sql
-- 通过sql添加唯一约束
-- 如果在创建表的时候,没有设置唯一约束名称,默认和字段名称一致;
alter table 表名 add constraint <唯一约束名称> unique(字段名称) ;
-- 通过sql删除唯一约束
alter table 表名 drop index 唯一约束名称;
```
## 3.通过sql添加外键约束和删除外键约束
```sql
-- 添加外键约束
alter table 表名
add
constraint 外键名称(主表名缩写_从表名的缩写_fk)
foreign key(外键作用字段名称)
references 主表名(主键id字段) ;
-- 删除外键约束
alter table 表名 drop foreign key 外键名称;
```
## 4.模糊搜索的语法
```sql
select 指定的字段列表 from 表名 where 字段名称 like '%关键字%' ; -- '关键字%'
```
## 5.反射获取一个类的字节码文件后如何调用成员方法?
```java
//获取当前正在运行的这个类Class,字节码文件对象
Class c = Class.forName("包名.类名") ;
//如果当前类的无参构造方法公共的,直接创建当前类实例
Object obj = c.newInstance();
//通过字节码文件对象获取成员方法所在的Method类对象
Method m = c.getDeclareedMethod("方法名",形式参数类型的.class属性...) ;
//取消Java语言访问检查
c.setAccessiable(true) ;
//调用方法
m.invoke(obj,实际参数和形式参数类型匹配) ;
//有返回值直接返回Object,或者没有返回值,单独调用或者return null;
```
----
----
# 今日内容
## 1.外键约束---通过sql添加/删除
## 2.级联操作CASCADE
```
ON update CASCADE /ON delete CASCADE:级联修改和级联删除
相对在多表中:有外键关系的时候,直接操作主表数据,跟主表相关的从表数据会随之改变
```
## 3.多表关系(设计数据库的三大范式:了解设计数据库注意事项)
```
一对多:
多对多:
一对一:一种特例:
1NF
2NF
3NF
```
## 4.多表查询
```
笛卡尔乘积查询:
A表 3条记录
B表 5条件记录
不设置多张表的连接条件---->15条数据
内连接
隐式内连接---where 条件1 and 添加2 and 添加3....
显示内连接 表名1 (inner) join 表名2 on 连接条件
外连接
左外: left (outer) join
right (outer) join
子查询
select 嵌套 select
利用聚合函数--查询出来的单行单列的数据和比较运算符 (完成嵌套)
条件字段名称 in(嵌套selct...)
(select 指定字段列表 from 表名)----> 虚表 和另一种张进行连接条件
```
## 5.数据库的事务
mysql_3
-- 通过sql删除外键约束
-- alter table 表名 drop foreign key 外键名称 ;
ALTER TABLE employee DROP FOREIGN KEY dept_emp_fk;
-- 测试是否删除了外键
INSERT INTO employee(NAME,gender,salary,dept_id)
VALUES('张艺兴','男',5000.0,4) ;
DELETE FROM employee WHERE id = 9 ;
-- 通过sql 添加外键约束
/*
alter table 表名 add
constraint 外键名称
foreign key (外键作用从表的字段名称)
references 主表名(主键字段) ;
*/
ALTER TABLE employee
ADD CONSTRAINT de_em_fk FOREIGN KEY (dept_id) REFERENCES dept(id) ;
-- 级联操作:CASCADE(修改以及删除主表数据,从表数据随之改变)
-- 级联删除和级联修改
-- 情况1:创建表的时候添加外键的同时后面添加级联删除以及级联修改
/*
CREATE TABLE employee (
字段名称1 字段类型1,
字段名称2 字段类型2,
....,
dept_id INT, -- 外键要作用的字段名称
-- 所在的部门编号
-- 声明
CONSTRAINT
外键名称 -- 外键名称:主表名_从表名_fk
FOREIGN KEY (外键作用的从表的字段名称) -- 外键作用在哪个字段上
REFERENCES -- 关联
主表名(主键字段)-- 主表的主键字段(就是id)
on update cascade
on delete cascade
) ;
*/
CREATE TABLE employee (
id INT PRIMARY KEY AUTO_INCREMENT,
-- 员工编号
NAME VARCHAR (10),
-- 员工姓名
gender VARCHAR (3),
-- 员工性别
salary DOUBLE,
-- 工资
dept_id INT,
-- 所在的部门编号
-- 声明
CONSTRAINT -- 外键名称:主表名_从表名_fk
dept_emp_fk -- 外键作用在哪个字段上
FOREIGN KEY (dept_id) -- 关联
REFERENCES -- 主表的主键字段(就是id)
dept(id)
ON UPDATE CASCADE -- 级联修改
ON DELETE CASCADE -- 级联删除
) ;
DROP TABLE employee ;
-- 删除5号部门
DELETE FROM dept WHERE id = 5;
-- 将部门表id为2的部门修改为 id=4的部门
UPDATE dept SET id = 4 WHERE id = 2 ;
-- 通过sql添加外键的同时,设置级联操作(级联修改和级联删除)
/*
alter table 表名 add
constraint 外键名称
foreign key (外键作用从表的字段名称)
references 主表名(主键字段)
on update cascade
on delete cascade ;
*/
SELECT * FROM dept ;
SELECT * FROM employee ;
-- 表和表的关系问题
/*
一般情况:两张表一个外键
两张表以上以上,可以设置多个外键
一对多
部门和员工的关系
从部门维度--->一个部门中包含多个员工
从员工维度---> 一个员工从属于某个部门
用户和订单
一个用户(用户维度):一个用户可以下多个订单
订单(订单维度):一个订单在某个时刻属于某个用户的
一个订单可以可以从属于多个用户的
多对多
学生表和选课表
一个学生可以选择多个课程
一个课程被多个学生选择
用户表和角色表 (用户表和角色表中间表)
从用户维度--看角色:一个用户有多个角色
从角色维度--看用户:一个角色赋予多个用户
角色表和权限表 (角色表和权限表中间表)
从角色表维度--看权限:一个角色可以多个权限
CEO:crud(添加,查询,修改和删除)
从权限维度---看角色:一个权限可以被多个角色赋予
查询权限:
CEO
hr
root管理员
普通管理员
*/
-- 数据库的三大范式:(理论)
-- 1NF:保证数据库表中的每一列是不能在拆分的原子数据项(单独某一列)
-- 2NF:在1NF的基础上,数据库表中每一个非主键字段必须完全依赖主键字段
-- 1)一张表示描述一件事情
-- 2) 非主键字段必须完全依赖主键字段
-- 3NF:在2NF基础上,数据库表中每一个非主键字段不能产生传递依赖(必须在2NF上满足非主键字段必须完全依赖主键字段)
-- 表中A字段依赖于B字段,B字段依赖于C字段, ---->A字段依赖于C字段(字段冗余非常大了)
-- 多表查询
/*
笛卡尔乘积查询:
A表 3条记录
B表 5条件记录
不设置多张表的连接条件---->15条数据
内连接
隐式内连接---where 条件1 and 添加2 and 添加3....
显示内连接 表名1 (inner) join 表名2 on 连接条件
外连接
左外: left (outer) join
right (outer) join
子查询
select 嵌套 select
利用聚合函数--查询出来的单行单列的数据和比较运算符 (完成嵌套)
条件字段名称 in(嵌套selct...)
(select 指定字段列表 from 表名)----> 虚表 和另一种张进行连接条件
*/
CREATE DATABASE myee2302_db_2;
# 创建部门表
CREATE TABLE dept(
id INT PRIMARY KEY AUTO_INCREMENT, -- 部门编号
NAME VARCHAR(20) -- 部门名称
);
INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部');
# 创建员工表
CREATE TABLE emp (
id INT PRIMARY KEY AUTO_INCREMENT, -- 员工编号
NAME VARCHAR(10), -- 员工名称
gender CHAR(1), -- 性别
salary DOUBLE, -- 工资
join_date DATE, -- 入职日期
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES dept(id) -- 外键,关联部门表(部门表的主键) (省略constraint 外键名称)
) ;
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男
',7200,'2021-02-24',1);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男
',3600,'2022-12-02',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES
('唐僧','男',9000,'2020-08-08',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女
',5000,'2021-10-07',3);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女
',4500,'2021-11-14',1);
/*
多表查询:
最基本的语法:
select 指定字段名称 from 表名1 ,表名2;
*/
-- 查询员工表中的id,姓名,性别,工资,入职日志以及部门表的部门名称
SELECT
emp.`id` '员工编号',
emp.`name` '员工姓名',
emp.`gender` '员工性别',
emp.`salary` '员工工资',
emp.`join_date` '入职日期',
dept.`name` '部门名称'
FROM
emp,
dept;
-- 问题: 员工表就5个人,查询出来15条数据---错误写法
-- 原因: 没有查询的时候,指定两张表的关联关系(存在外键),造成数据紊乱;
-- 迪卡尔乘积:两张表没有建立连接关系,A表的记录数 * B表的记录数----等于总记录数
-- .关系数据库中提供了多表查询之内连接---解决笛卡尔乘积问题
/*
多表查询的分析思路:
1)要查询哪张表
2)要查询指定表中的哪些字段名称
3)表和表关系问题 (需要建立连接条件)
*/
-- 内连接的隐式内连接:就是利用where语句完成条件 的拼接(&&,或者and关键字)
-- select 字段列表 from 表名1 ,表名2 where 连接条件1 and....连接条件2....
-- 查询员工表中的id,姓名,性别,工资,入职日志以及部门表的部门名称
SELECT
e.`id` '员工编号',
e.`name` '员工姓名',
e.`gender` '性别',
e.`salary` '工资',
e.`join_date` '入职日期',
d.`name` '部门名称'
FROM
emp e,
-- 给表起一个别名
dept d -- d是部门表的别名
WHERE e.`dept_id` = d.`id` ;
-- 显示内连接 --inner join
-- select 字段列表 from 表名1 (inner) join 表名2 on 连接条件1 and 连接条件2.....
-- 需求:查询员工表名的姓名,工资,入职日期以及部门表的部门名称
SELECT
t1.`name` '员工姓名',
t1.`salary` '工资',
t1.`join_date` '入职日期',
t2.`name` '部门名称'
FROM
emp t1
INNER JOIN dept t2
ON t1.`dept_id` = t2.`id` ;
-- 插入一条数据
INSERT INTO emp(NAME,gender,salary,join_date)
VALUES('高圆圆','女',8000,'2018-10-30') ;
-- 多表查询之外连接
-- 左外连接/右外连接
-- 一般比较常用的左外连接: 将左边的数据全部查询以及两张表之间的交集数据(连接条件的数据)全部查询!
/*
select
指定字段列表
from 表名1
left outer join 表名2 / right outer join 右外连接
on 连接条件
*/
-- 需求:查询员工表的所有数据以及部门的部门名称数据,没有部门的员工信息也得查询
SELECT
t1.*, -- 查询员工的所有数据
t2.`id` '部门编号',
t2.`name` '部门名称' -- 部门名称
FROM
emp t1
LEFT OUTER JOIN
dept t2
ON
t1.`dept_id` = t2.`id` ;
-- 多表查询里面的子查询 (就是select嵌套select语句)
-- 情况1:利用聚合函数查询出来的单列的数据和select语句进行嵌套
-- 需求:查询最高工资的员工信息以及员工所在的部门信息
-- 1)查询出最高工资是多少
-- select max(salary) from emp ; -- 9000
-- 2)查询员工工资是9000的员工信息以及部门信息
/*
select
t1.* , -- 员工所有信息
t2.* -- 部门所有信息
from
emp t1,
dept t2
where
t1.`salary` = 9000 and t1.`dept_id` = t2.`id`;
*/
-- 一步走:
SELECT
t1.*,
-- 员工所有信息
t2.* -- 部门所有信息
FROM
emp t1,
dept t2
WHERE t1.`dept_id` = t2.`id`
AND t1.`salary` =
(SELECT
MAX(salary)
FROM
emp) ;
-- 需求:查询出来员工工资大于等于平均工资的员工信息(员工姓名,性别,工资,入职日期)同时所在的部门名称信息;
-- 1)查询出来平均工资是多少
SELECT AVG(salary) FROM emp ; -- 6216.67
-- 2)查询出来员工工资大于等于6216员工的部分信息以及部门名称信息,没有部门的员工信息查询出来
SELECT
e.`name` '员工姓名',
e.`gender` '员工性别',
e.`salary` '员工工资',
e.`join_date` '入职日期',
d.`name` '部门名称'
FROM
emp e,
dept d
WHERE e.`dept_id` = d.`id`
AND e.`salary` >= 6216 ;
-- 一步走
SELECT
e.`name` '员工姓名',
e.`gender` '员工性别',
e.`salary` '员工工资',
e.`join_date` '入职日期',
d.`name` '部门名称'
FROM
emp e,
dept d
WHERE e.`dept_id` = d.`id`
AND e.`salary` >=
(SELECT AVG(salary) FROM emp) ;
INSERT INTO emp(NAME,gender,salary,join_date) VALUES('文章','男',7000.0,'2020-05-01') ;
-- 如果提示:仅仅没有部门的员工的也需要查询出来,使用左外连接
SELECT
e.`name` '员工姓名',
e.`gender` '员工性别',
e.`salary` '员工工资',
e.`join_date` '入职日期',
d.`name` '部门名称'
FROM
emp e
LEFT JOIN dept d
ON e.`dept_id` = d.`id` -- 作为两张表的连接条件
WHERE e.`salary` >= -- 附件条件
(SELECT
AVG(salary)
FROM
emp) ;
-- 查询出在财务或者市场部的员工的信息以及部门名称信息
-- 1)查询财务部和市场部id 是多少
SELECT
id
FROM
dept
WHERE NAME = '市场部'
OR NAME = '财务部' ;
-- 2)利用子查询 in(值1,值2,值3集合数据) ;
-- 查询在2或者3号部门的员工信息以及部门名称信息
SELECT
e.*,
d.`id` '部门编号',
d.`name` '部门名称'
FROM
emp e,
dept d
WHERE e.`dept_id` = d.`id`
AND e.`dept_id` IN (2, 3) ;
-- 一步走
SELECT
e.*,
d.`id` '部门编号',
d.`name` '部门名称'
FROM
emp e,
dept d
WHERE e.`dept_id` = d.`id`
AND e.`dept_id` IN
(SELECT
id
FROM
dept
WHERE NAME IN('市场部','财务部') ) ;
-- 多表查询子查询的情况3:
-- 将某张表select的结果集---当做一张虚表和其他表建立连接查询
-- 需求:要查询出员工入职日期大于2021-10-07的员工的所有信息以及它的部门信息;
-- 查询员工入职日期大于 2021-10-07的员工所有信息 (查询出来的结果集当作 "虚表")
SELECT
t1.*,
-- 所有的部门信息
t2.* -- 所有员工信息
FROM
dept t1
INNER JOIN
(SELECT
*
FROM
emp
WHERE emp.`join_date` > '2021-10-07') t2
ON t1.`id` = t2.`dept_id` ;
-- 优化隐式内连接
SELECT
e.*,
d.*
FROM
emp e,
dept d
WHERE e.`dept_id` = d.`id`
AND e.`join_date` > '2021-10-07' ;
SELECT * FROM dept ;
SELECT * FROM emp;
-- 数据库的事务
/*
什么是数据库的事务?
事务就是DBA(数据库管理员)操作多个sql的时候,可能有一条执行语句可能异常了,其他sql不执行了,就会导致出现紊乱,
将整个sql(多个sql)的执行看成一个"整体",要么同时执行成功,要么同时执行失败!
始终保证的数据的一致性(高并发中,读/写)
*/
-- 创建一个账户表account
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT, -- 账户id
NAME VARCHAR(10) , -- 账户名称
balance INT -- 账户余额
);
-- 插入两个账户信息
INSERT INTO account(NAME,balance) VALUES('zhangsan',1000),('lisi',1000) ;
-- zhangsan 给 lisi转账500
UPDATE account SET balance = balance - 500 WHERE id = 1 ;
-- 出问题了,这块不执行了
UPDATE account SET balance = balance + 500 WHERE id = 2 ;
UPDATE account SET balance = 1000 ;
-- 将 "转账业务"看成一个整体,
-- 开启事务
START TRANSACTION ; -- 将自动提交切换为手动提交
UPDATE account SET balance = balance - 500 WHERE id = 1 ;
-- 出问题了,这块不执行了
UPDATE account SET balance = balance + 500 WHERE id = 2 ;
-- 有问题回滚;事务回滚:撤销之前的所有操作,默认回滚到执行更新之前的数据
ROLLBACK ;
-- 没有问题,正常提交事务
COMMIT ; -- 不提交,不会永久性的更改数据---
-- 传统事务特点(重点)ACID
-- 原子性:执行的某业务操作,同时操作多个sql(增删改),要么同时执行成功,要么同时执行失败!
-- 一致性:在实际开发中,多个线程同时去写和读的时候,数据必须一致性
-- 隔离性:事务和事务之间(业务和业务之间)相互独立的, 互不影响!
-- 持续性:就是对增删改这些sql语句,一旦事务提交了,对数据库中表的操作是永久性,即使关机了,数据永久保存!
-- 传统事务的隔离级别:
-- 查询数据库的隔离基本
SELECT @@tx_isolation; -- mysql8.0服务器版---SELECT @@transaction_isolation
-- 设置隔离级别
-- set global transaction isolation level (级别的名称) ;
-- 执行效率从大到小,安全性:从低到高
-- 读未提交:read uncommited;
/*
一个事务读取到另一个没有没有提交的事务(最不安全),出现"脏读" (最严重的问题!)
*/
-- 读已提交:read committed;
/*
有效防止脏读! 会出现另一种问题"不可重复读",
读取到自己本身没有提交的事务的数据前后两次不一致,本身这个事务没有提交,多次读取到的数据必须一致!
*/
-- 可重复读 :mysql的默认隔离级别 (repetable-read)
/*
有效防止脏读,不可重复读,可能出现"幻读"
*/
-- 串行话:serializable
SELECT * FROM account ;
day35
晨写
# 晨写
## 1.什么是事务?
```markdown
关系型数据库中--支持"多事务",都会在事务机制,当某个业务同时操作多个sql语句或者是多张表的sql插入/删除/修改),这多个sql要么同时执行成功,要么同时执行失败,保证数据的完整性!
```
## 2.事务的特点和事务的隔离级别
```markdown
事务的特点
原子性:多个sql要么同时执行成功,要么同时执行失败,保证数据的完整性!
一致性:事务同时去操作多个sql的时候,需要保证数据的一致性!
隔离性:多个事务(业务和业务之间)之间互相独立的,互不影响
持久性:当事务一旦提交,对数据的更改是永久性的,即使关机也存在!
事务的隔离级别:
-- 下面依次从小到大,安全性低大高,效率:从高到低;
read uncommitted ; 读未提交
read committed ; 读已提交
repetable read ; 可重复读 mysql的默认级别:有效防止脏读以及不可重复读
serializable ; 串行话
```
## 3.mysql多表查询内连接和左外连接的语法
```sql
-- 隐式内连接
select 指定的字段名称 from 表名1,表名2 where 条件1 and 条件2.... ;
-- 显示内连接
select 指定的字段名称 from 表名1 inner join 表名2 on 连接条件;
-- 左外连接语法:将左表数据全部查询以及它们的交集部分的数据
select 指定的字段名称 from 表名1 left outer join 表名2 on 连接条件 where 附件条件..
```
## 4.数据库的三大范式描述
```markdown
1NF:
数据库表中的每一列是独立,不能在拆分的原子数据项
2NF:
在1NF基础上
1)数据库指定表中,描述一件事情(一张表描述一件事情)
2)数据库表中的非主键字段必须完全依赖主键字段
3NF:
在2NF基础上
数据库表中非主键字段之间不能产生传递依赖!
非主键字段A---字段B---->字段C :产生传递依赖
-- 解决冗余:比如多对多---拆分中间表:设置外键连接
```
## 5.通过mysql指令如何控制事务
```sql
-- 开启事务:将mysql自动提交,切换手动提交事务
start transaction ;
-- 设置回滚命令
rollback;
-- 永久更新数据(保证数据的持久性)
commit ;
-- 设置隔离级别
set global transaction isolation level 级别名称;
-- 查看隔离级别
select @@tx_isolation ;
```
-----
-------
# 今日内容
## 1.JDBC入门
```
什么是jdbc?
jdbc的本质是什么?
```
## 2.jdbc的操作步骤 (DDL,DML :插入/update/delete)
```markdown
1)导入驱动包
2)注册驱动
3)准备好sql语句
4)获取数据库的连接对象Connnection
5)通过连接对象获取执行对象:Statement
6)执行静态sql
7)释放资源---Connection Statement都必须释放
```
### 2.1 单元测试---junit完成单元测试
```
自己导入junit的jar包完成单元测试
测试:
白盒 :技术含量---测试人员懂 java基础,python基础...需要写测试用例
功能性测试:
黑盒 :没有技术含量的
```
## 3.jdbc操作(Statement)查询sql语句
```
查询select语句----将select语句的结果集的数据表---存储在jdk提供ResultSet
遍历数据,获取数据----自己封装数据!
```
## 4.mysql视图 (view) : (掌握基础指令)
```
通过查询的select结果集----存储在虚拟视图中,提高数据的安全性!
虚拟表(视图) --- 基础表(真实存在的表)
create view 视图名称 as <select语句>
```
jdbc入门
package com.qf.jdbc_01;
import java.sql.Connection; // 全部是接口下的java.sql.xx
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author 高圆圆
* @date 2023/4/4 10:03
*
* jdbc入门
*/
public class JdbcDemo {
public static void main(String[] args) throws ClassNotFoundException,
SQLException {
//1)导入驱动包
//2)注册驱动
Class.forName("com.mysql.jdbc.Driver") ;//mysql-5.1xxxjar包
//DriverManager.registerDriver( new com.mysql.jdbc.Driver());
//3)准备sql语句
String sql = "insert into account(name,balance) values('文章',1000);" ;
//4)获取数据库的连接对象
//jdk提供了类DriverManager:管理jdbc服务的
/**
* public static Connection getConnection(String url, 统一资源定位符:连接的具体库
* String user, 用户名
* String password)登录mysql的密码
* throws SQLException
*/
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/myee2302_db_2",
//jdbc协议:数据库协议://域名:端口号/库名
//如果使用mysql8.0的jar包(mysql-server:安装的是8.0)此时后面带上一些参数:时区以及编码格式
//jdbc:mysql://localhost:3306/myee2302_db_2?characterEncoding=utf-8&timezone=utc
"root",
"123456");
//5)通过数据库的连接对象获取执行对象
//java.sql.Connnection接口---->Statement createStatement()
Statement stmt = conn.createStatement();
//6)执行对象执行sql
//java.sql.Statemnt--->通用方法:更新操作(插入/修改/删除)
//int executeUpdate(String sql)throws SQLException
int count = stmt.executeUpdate(sql);
System.out.println("影响了"+count+"行");
//7)释放资源
stmt.close() ;
conn.close();
}
}
package com.qf.jdbc_01;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author 高圆圆
* @date 2023/4/4 11:31
* jdbc操作DDL语句,创建一张表 student
* id int ,name varchar(10),age int,gender varchar(3),address varchar(50),
*
*/
public class JdbcDemo2 {
public static void main(String[] args) {
//捕获异常
//1)导包
//2)注册驱动
Connection conn = null ;
Statement stmt = null ;
try {
Class.forName("com.mysql.jdbc.Driver") ;
//3)获取数据库连接对象
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/myee2302_db_2",
"root",
"123456");
//4)准备sql
String sql = "create table student(" +
"id int primary key auto_increment," +
"name varchar(10)," +
"age int," +
"gender varchar(3)," +
"address varchar(50));";
//5)通过连接对象获取执行对象
stmt = conn.createStatement();
//6)执行更新操作 DDL语句 executeUpdate(String sql)
int count = stmt.executeUpdate(sql);
System.out.println(count);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
if(stmt!=null){
try {
stmt.close() ;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close() ;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
jdbc操作过程中的api
JDBC七大步骤
1.导包
mysql5.1的jar包---com.mysql.jdbc.Driver 驱动类
mysql8.0的jar包---com.mysql.cj.jdbc.Driver 驱动类
2.注册驱动
本身注册驱动的写法:
jdk提供的DriverManager驱动管理类:管理jdbc服务的
public static void registerDriver(Driver driver)throws SQLException 注册驱动
参数:java.sql.Driver接口
书写代码中不需要上面的这个步骤
因为
com.mysql.jdbc.Driver 这个类里面已经注册驱动了
里面有静态代码块
static {
try {
//注册驱动
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
Class.forName("com.mysql.jdbc.Driver") ; 获取正在运行的这个类的字节码文件对象:
1)加载这个类会执行com.mysql.jdbc.Driver这个类的staitc静态代码块,立即注册驱动!
2)为了向下兼容!
3.准备sql语句---定义String类型,里面书写插入/修改/删除
静态sql语句,里面的参数要么键盘录入,要么直接给值,存在字符串拼接!
String sql = "insert into account(name,balance) values('文章',1000);" ;
4.获取数据库的连接对象 java.sql.Connnection
java.sql.DriverManager类:
提供静态方法:
public static Connection getConnection(String url,
String user,
String password)
throws SQLException
url:统一资源定位符
jdbc:mysql://本地默认localhost或者127.0.0.1:端口号/库名 (mysql驱动包5.1jar包这个地址不需要带参数)
mysql驱动包8.0jar包后面带参数
jdbc:mysql://本地默认localhost或者127.0.0.1:端口号/库名?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
编码格式/是否启用证书(不启用,都是默认密码登录)/时区/是否允许数据库公钥验证(默认true):验证登录的密码
端口号:3306 (mysql)
user:用户名:
password:登录mysql的密码
java.sql.Connection:接口:代表的与特定数据库的连接会话
获取执行对象执行静态sql语句
Statement createStatement()
java.sql.Statement:在数据库中执行sql语句的接口
针对DML语句(insert into/delete/update) ,直接影响了的行数,返回值int
针对ddl语句(创建表/修改表/删除表):返回值是0
int executeUpdate(String sql)throws SQLException
执行DQL语句,数据库查询语句 select语句
ResultSet executeQuery(String sql)throws SQLException
java.sql.ResultSet:获取数据表的结果集 (接口)
获取查询的多条件记录或者单条记录或者查询单行单列数据....
jdbc工具类写法
package com.qf.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* 将jdbc步骤优化
* 自定义工具类,将获取连接对象,释放资源都放在静态方法中,方便去使用!
*/
public class JdbcUtils {
//统一资源定位符
private static String url = null ;
//用户名
private static String user = null ;
//密码
private static String password = null ;
//驱动类
private static String driverClassName = null ;
//定义静态代码块
static{
try {
//1)读取src下面的jdbc.properties文件
//创建一个属性集合列表
Properties prop = new Properties() ;
//System.out.println(prop) ;
InputStream inputStream = JdbcUtils.class.getClassLoader().
getResourceAsStream("jdbc.properties");
//将字节输入流加载到属性集合列表中
prop.load(inputStream) ;
//System.out.println(prop) ;
//2)通过key获取value
driverClassName = prop.getProperty("driverClassName");
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password");
//3)注册驱动
Class.forName(driverClassName) ;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//对外私有,不让外界new
private JdbcUtils(){}
//定义公共的静态方法,返回值就是Connection
//public static Connection getConnection(String url,String user,String password){ //一会还得需要传入参数,
public static Connection getConnection(){ //一会还得需要传入参数,
Connection conn = null ;
try {
conn = DriverManager.getConnection(url,user,password) ;//url,user,password
return conn;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null ;
}
//释放资源
public static void close(ResultSet rs,Statement stmt,Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//主要针对DDL/DML语句操作释放资源
public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);
}
public static void main(String[] args) {
Connection conn = JdbcUtils.getConnection();
System.out.println(conn);
}
}
package com.qf.jdbc_02;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 测试自定义的工具类给myee2303_db_2库中student添加一条数据
*/
public class JdbUtilsTest {
public static void main(String[] args) throws SQLException {
//1)导包/注册驱动/获取直接对象
Connection conn = JdbcUtils.getConnection();
//2)sql
String sql = "insert into student(name,age,gender,address)" +
"values ('张三丰',25,'男','西安市'),('高圆圆',25,'女','咸阳市');";
//3)获取执行对象
Statement stmt = conn.createStatement();
//4)执行
int count = stmt.executeUpdate(sql);
System.out.println("影响了"+count+"行") ;
//5)释放资源
JdbcUtils.close(stmt,conn);
}
}
jdbc.properties的内容
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/myee2302_db_2
user=root
password=123456
Statement执行对象操作DML语句之修改操作
package com.qf.jdbc_02;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
/**
* Statement执行对象操作DML语句之修改操作
*/
public class Statement_Operator_DML {
public static void main(String[] args) {
Connection conn = null ;
Statement stmt = null ;
try {
//获取数据库连接对象
conn = JdbcUtils.getConnection();
//准备sql
String sql = "update student set name = '赵又廷',age = 35,address='西安市南窑国际' where id = 1;" ;
//获取执行器对象Statement
stmt = conn.createStatement();
//执行sql
int count = stmt.executeUpdate(sql);
System.out.println("影响了"+count+"行");
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils.close(stmt,conn);
}
}
}
使用Statment执行对象来操作DQL语句,并去将所有的结果集数据表打印在控制台上
package com.qf.jdbc_03;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 使用Statment执行对象来操作DQL语句,并去将所有的结果集数据表打印在控制台上
*/
public class Statement_DQL {
public static void main(String[] args) {
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
try {
//注册驱动并获取连接对象
conn = JdbcUtils.getConnection() ;
//准备sql语句
String sql = "select * from student;" ;
//通过连接对象获取执行对象,准备发送sql到数据库
stmt = conn.createStatement() ;
//执行通用的查询操作
rs = stmt.executeQuery(sql) ;
//第一次获取
System.out.println("编号\t姓名\t\t\t年龄\t性别\t地址") ;
//有下一行数据获取
//boolean next() ;
/* if(rs.next()){
//列的索引值获取
int id = rs.getInt(1) ;
String name = rs.getString(2) ;
int age = rs.getInt(3) ;
String gender = rs.getString(4);
String address = rs.getString(5);
System.out.println(id+"\t"+name+"\t\t"+age+"\t"+gender+"\t"+address);
}
//第二次获取
//通过列的名称获取
if(rs.next()){
int id = rs.getInt("id") ; //id名称
String name = rs.getString("name") ; //name名称
int age = rs.getInt("age") ; //age名称
String gender = rs.getString("gender") ; //gender
String address = rs.getString("address") ; //address
System.out.println(id+"\t"+name+"\t\t"+age+"\t"+gender+"\t"+address);
}
//第三次获取
//通过列的名称获取
if(rs.next()){
int id = rs.getInt("id") ; //id名称
String name = rs.getString("name") ; //name名称
int age = rs.getInt("age") ; //age名称
String gender = rs.getString("gender") ; //gender
String address = rs.getString("address") ; //address
System.out.println(id+"\t"+name+"\t\t\t"+age+"\t"+gender+"\t"+address);
}*/
//当不明确多少个结果,while循环
while(rs.next()){
//有数据,获取
int id = rs.getInt("id") ; //id名称
String name = rs.getString("name") ; //name名称
int age = rs.getInt("age") ; //age名称
String gender = rs.getString("gender") ; //gender
String address = rs.getString("address") ; //address
System.out.println(id+"\t"+name+"\t\t\t"+age+"\t"+gender+"\t"+address);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
JdbcUtils.close(rs,stmt,conn);
}
}
}
加上面的工具类
单元测试类写法
package com.qf.test_04;
import com.qf.pojo.Student;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
/**
* 需求:
* 将student表中的所有数据查询出来,表中每一条数据封装到Student类中,将这个多条记录
* 添加到集合中,最终遍历集合,展示ArrayList<Student>里面的数据!
*
*/
public class DqlTest {
public static void main(String[] args) {
List<Student> list = getStudents();
for (Student s : list) {
System.out.println(s) ;
}
}
//获取数据表中的所有数据,添加集合中;
public static List<Student> getStudents(){
//创建一个集合
List<Student> list = null ;
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
try {
list = new ArrayList<>() ;
//注册驱动,同时获取数据库的连接对象
conn = JdbcUtils.getConnection();
//sql
String sql = "select * from student;" ;
//获取执行对象
stmt = conn.createStatement() ;
//执行查询操作
rs = stmt.executeQuery(sql) ;
//声明一个学生变量s
Student s = null ;
//遍历结果集
while(rs.next()){
//有数据行,获取数据
//封装学生对象
s = new Student() ;
s.setId(rs.getInt("id")) ;
s.setName(rs.getString("name")) ;
s.setAge(rs.getInt("age")) ;
s.setGender(rs.getString("gender")) ;
s.setAddress(rs.getString("address")) ;
//将学生对象添加list集合中
list.add(s) ;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return list;
}
}
package com.qf.pojo;
/**
* @author 高圆圆
* @date 2023/4/4 15:49
*
* 学生实体中,属性名称和数据表中的字段对应!
*/
public class Student {
/**
* id int(11) NOT NULL
* name varchar(10) NULL
* age int(11) NULL
* gender varchar(3) NULL
* address varchar(50) NULL
*/
//属性字母都是小写,可以有_下划线
//如果属性 private String NAME ; 大写,后期通过一些"高级jdbc"技术,没有办法封装数据!
private int id ; //学生编号
private String name; //姓名
private int age ; //年龄
private String gender ; //性别
private String address ;//地址
public Student() {
}
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
", address='" + address + '\'' +
'}';
}
}
package com.qf.dao;
import com.qf.pojo.Student;
import java.util.List;
/**
* 针对学生的数据访问接口
*/
public interface StudentDao {
/*
* 查询所有学生信息
* @return 返回学生列表
*/
List<Student> findAll() ;
/**
* 添加学生信息
* @param s 学生实体
*
*/
void add(Student s) throws Exception;
// void add(Student s) ;
}
package com.qf.dao.impl;
import com.qf.dao.StudentDao;
import com.qf.pojo.Student;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
/**
* 针对学生的数据访问接口实现
*
* dao层: data access Object :数据访问对象! :操作数据库,这一层代码需要抛出throws
*/
public class StudentDaoImpl implements StudentDao {
/*
* 查询所有学生信息
* @return 返回学生列表
*/
@Override
public List<Student> findAll() {
//创建一个集合
List<Student> list = null ;
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
try {
list = new ArrayList<>() ;
//注册驱动,同时获取数据库的连接对象
conn = JdbcUtils.getConnection();
//sql
String sql = "select * from student;" ;
//获取执行对象
stmt = conn.createStatement() ;
//执行查询操作
rs = stmt.executeQuery(sql) ;
//声明一个学生变量s
Student s = null ;
//遍历结果集
while(rs.next()){
//有数据行,获取数据
//封装学生对象
s = new Student() ;
s.setId(rs.getInt("id")) ;
s.setName(rs.getString("name")) ;
s.setAge(rs.getInt("age")) ;
s.setGender(rs.getString("gender")) ;
s.setAddress(rs.getString("address")) ;
//将学生对象添加list集合中
list.add(s) ;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return list;
}
/**
* 添加学生信息
* @param s 学生实体
*/
@Override
public void add(Student s) throws Exception{ //测试的时候传入学生对象
//注册驱动,同时获取数据库的连接对象
Connection conn = JdbcUtils.getConnection();
//sql
String sql = "insert into student(name,age,gender,address) values('"+s.getName()+"','"+s.getAge()+"','"+s.getGender()+"','"+s.getAddress()+"') ;" ;
//获取执行对象
Statement stmt = conn.createStatement() ;
int count = stmt.executeUpdate(sql);
System.out.println(count) ;
JdbcUtils.close(stmt,conn);
}
}
package com.qf.junit_05;
import com.qf.dao.StudentDao;
import com.qf.dao.impl.StudentDaoImpl;
import com.qf.pojo.Student;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
/**
* 测试学生接口
*
* @Test:标记这个方法是单元测试方法
* @Before:标记的方法:是在执行单元测试之前执行
* @After:标记的方法:是在执行单元测试之后执行
*/
public class StudentTest {
private StudentDao sd ; //声明学生接口StudentDAo
@Before
public void init(){
System.out.println("执行单元测试方法之前执行了...");
sd = new StudentDaoImpl() ;
System.out.println(sd) ;
}
@Test
//测试查询所有的功能 (测试功能是否完整)
public void testFindAll(){
System.out.println("进入单元测试方法了");
//调用功能
List<Student> list = sd.findAll();
for(Student s:list){
System.out.println(s) ;
}
}
@Test
//测试添加学生
public void testaddStudent() throws Exception {
//System.out.println("进入单元测试方法了");
//调用功能
Student s = new Student() ;
s.setName("梁飞虎") ;
s.setAge(23) ;
s.setGender("男") ;
s.setAddress("西安市");
sd.add(s);
}
@After
public void close(){
System.out.println("执行单元测试方法之后执行了...");
}
}
day36
晨写
# 晨写
## 1.jdbc的本质是什么?
```markdown
Jdbc是一个普通的Java类,是数据库厂商提供的驱动jar包,可以实现sun提供一套接口规范!
java.sql.Driver:驱动接口---->驱动jar包--->实现了这个接口
java.sql.Connection:数据库的连接会话--->驱动jar包--->ConnectionImpl实现了这个接口
...
```
## 2.jdbc的7大操作步骤,代码体现
```java
// 7大步骤 后期--->开源框架:就是对原生Jdbc代码的封装+一些配置文件
//1)导入驱动jar包 --mysql-connnecto-java驱动jar包
//2)注册驱动
Class.forName("com.mysql.jdbc.Driver") ;
//3)获取数据库的连接对象
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/库名",
"root",
"123456") ;
//4)准备sql语句:DDL语句或者DML语句
String sql = "insert into employee(name,age,gender,birthday,address) values(xxx,xx,xx,xx,xx,xx)" ;
//5)通过连接对象获取执行对象Statement
Statement stmt = conn.createStatement() ;
//6)执行sql,通用的更新
int count = stme.executeUpdate(sql) ;
//7)释放资源
stmt.close() ;
conn.close() ;
```
## 3.jdbc针对DQL语句,遍历结果集
```java
// 7大步骤 后期--->开源框架:就是对原生Jdbc代码的封装+一些配置文件
//1)导入驱动jar包 --mysql-connnecto-java驱动jar包
//2)注册驱动
Class.forName("com.mysql.jdbc.Driver") ;
//3)获取数据库的连接对象
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/库名",
"root",
"123456") ;
//4)准备sql语句:DDL语句或者DML语句
String sql = "select 指定的字段列表 from 表名" ;
//5)通过连接对象获取执行对象Statement
Statement stmt = conn.createStatement() ;
//6)执行sql,通用的查询操作
ResultSet rs = stmt.executeQuery(sql) ;
//遍历结果集
while(rs.next()){
//获取数据
//通用的写,列的名称获取/列的索引值获取
XXX 变量= rs.getXXX("字段名称") ;
//自己封装数据...
}
//7)释放资源
stmt.close() ;
conn.close() ;
```
## 4.自定义工具类JdbcUtils的步骤
```properties
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/库名
user=root
password=123456
```
```java
class JdbcUtils{
private static String user = null ;
private static String password = null ;
private static String driverClass = null ;
private static String url = null ;
//定义static代码块
static{
//读取配置文件
Properties prop = new Properties() ;
//获取key对应的value,赋值给上面四个成员变量
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties") ;
user = prop.getProperty("user") ;
password = prop.getProperty("password") ;
url = prop.getPrpperty("url") ;
driverClass = prop.getProperty("driverClass") ;
//注册驱动
Class.forName(driverClass) ;
}
private JdbcUtils(){}
public static Connnection getConnection(){
Connection conn = DriverManager.getConnection(url,user,password) ;
return conn ;
}
//释放资源
//带三个参数:ResultSet ,Statment,Connection---针对DQL语句操作
//带两个参数:Satement,Connection--->针对DML语句
}
```
## 5.junit单元测试的使用步骤
```markdown
1)导入junit的核心包: 4.13.1以及依赖包
2)建立测试类---XXXTest
3)定义单元测试方法,在方法上加入@Test
4)方法中要么Assert断言,要么直接测试指定功能(XXXDao--->添加,删除,修改,查询的功能)
```
---
---
# 今日内容
## 1.作业:使用Statement完成对一张表的内容增删查改
```
员工类
编号
姓名
年龄
性别
工资
....
EmployeeDao--->针对员工的数据访问接口
添加员工
修改员工
删除员工
查询所有员工
通过指定员工编号查询员工...
EmployeeDaoIml---实现
定义EmployeeTest--->提供单元测试方法:针对上面所有功能单独测试....
```
## 2.视图 View(了解)
```
虚拟表 (通过建立视图和基础表的内容一模一样) 和基础表(真实存在的数据)
```
## 3.使用Statement的弊端
```java
使用Statement完成用户登录---------->有一张user表
id username password
键盘录入用户名和密码,
User类:private int id;
private String username;
private String password;
定义一个UserDao接口---->boolean isLogin(String username,String password) ;
userDaoImpl接口实现类----->实现这个业务
select * from user where username = 'xxx' and passwod='xxxx' ;
纯sql拼接会出现安全漏洞!
select * from user where username = 'xxx' and passwod='xxxx' or '1'='1' ;
如果查询到了数据---->结果集对象.next()--->返回true
--- 效率低
--- 造成sql注入(用户名和随便输入,依然登录成功)
开发中使用:PreparedStatement---执行预编译sql语句
select * from user where username = ? and passwod=? ; //? 占位符符号
```
## 4.PreparedStatement预编译对象的使用:执行预编译sql语句(重点)
```
一条sql:insert into/update/delete /select--->有参数--->全部都是? 占位符号
```
## 5.数据库连接池(重点)
```
c3p0
dbcp
druid:德鲁伊---->阿里巴巴提供的 (开源的)
druid.jar包
只是提供数据库连接池---不能连接数据库(要连接数据库---提供mysql驱动jar包)
JdbcUtils就需要优化了
模拟线程 ThreadLocal<T> : 每一个线程都有自己的Connection
set(T t):绑定内容
remove():解绑
T get() :获取指定的内容
```
-----
----
## Statement和PreparedStatement的区别?
```markdown
1)共同点:
都是可以发送sql到数据库的,都是执行对象,后者继承前者(java.sql.包下的接口)
2)不同点:
2.1)是否会造成sql注入
Statement永远执行的是静态sql语句:语句中存在"硬编码",存在SQL的字符串拼接,就造成sql注入,不安全!
PreparedStatement:永远执行的是参数化的sql语句,全部参数都是"?",占位符号,有效防止sql的拼接,预防sql注入,提高执行sql的安全性!
2.2)是否会提高sql执行效率
Statement:不会提高sql执行效率,
获取执行对象,然后将sql语句发送数据库(没写一个sql,发一次!),频繁的操作访问数据库
PreparedStatement:大大提高SQL执行效率
参数化sql是在获取PreparedStatement,就已经发送给数据库了,然后在PreparedStatement赋值不同的值;
```
## 德鲁伊Druid配置文件的参数说明
```properties
#这些名称都是DruidDataSoure提供的参数
#连接数据库的驱动类
driverClassName=com.mysql.jdbc.Driver
#连接数据库的url地址:统一资源定位符
url=jdbc:mysql://localhost:3306/myee2302_db_2
#用户名
username=root
#密码
password=123456
#连接池一旦创建,初始化5个连接数量
initialSize=5
#最大连接数量值:默认值8个,自己设定值,和maxIdel:最大空闲数量相等
maxActive=10
#最大等待时间:为毫秒值,一旦连接池中创建连接对象超过了最大连接数量,等待3秒中,如果还连接不上,连接池会产生错误日志,提示"连接超时"
maxWait=3000
```
Statement作业讲解+预编译方法
/*
package com.qf.dao.impl;
import com.qf.dao.EmployeeDao;
import com.qf.pojo.Employee;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
*/
/**
* 针对员工的数据接口实现(持久层的实现类---有异常---全部throws)
*
* service:业务层:调用dao层代码获取数据---->必须try...catch...捕获
*
* dao层: 数据访问----throws抛出
*//*
public class EmployeeDaoImpl implements EmployeeDao {
*/
/**
* 查询所有员工
* @return 返回员工列表
*//*
@Override
public List<Employee> findAll() throws SQLException {
//创建一个集合
List<Employee> list = new ArrayList<>() ;
//获取连接对象
Connection conn = JdbcUtils.getConnection();
//准备sql语句
String sql = "select * from employee" ;
//获取执行对象
Statement stmt = conn.createStatement() ;
//执行查询
ResultSet rs = stmt.executeQuery(sql);
Employee emp = null ;
//遍历结果集
while(rs.next()){
//获取数据,将数据封装员工对象中
emp = new Employee() ;
emp.setId(rs.getInt("id")) ;
emp.setName(rs.getString("name"));
emp.setGender(rs.getString("gender")) ;
emp.setAge(rs.getInt("age"));
emp.setSalary(rs.getDouble("salary")) ;
emp.setAddress(rs.getString("address"));
emp.setBirthday(rs.getDate("birthday")) ;
//将所有员工添加到集合中
list.add(emp) ;
}
JdbcUtils.close(rs,stmt,conn);
return list;
}
*/
/**
* 添加员工
* @param employee 员工实体
*//*
@Override
public void add(Employee employee) throws SQLException {
Connection conn = JdbcUtils.getConnection();
//sql
String sql = "insert into employee(name,gender,age,salary,address,birthday) " +
"values('"+employee.getName()+"','"+employee.getGender()+"','"+employee.getAge()+"','"+employee.getSalary()+"','"+employee.getAddress()+"','"+employee.getBirthday()+"')" ;
System.out.println(sql);
Statement stmt = conn.createStatement();
int count = stmt.executeUpdate(sql);
System.out.println(count);
}
*/
/**
* 通过员工编号删除员工
* @param id 要传进来的员工id
*//*
@Override
public void deleteEmployeeById(int id) throws SQLException {
Connection conn = JdbcUtils.getConnection();
//sql
String sql = "delete from employee where id ='"+id+"' " ;
System.out.println(sql);
Statement stmt = conn.createStatement();
int count = stmt.executeUpdate(sql);
System.out.println(count);
}
*/
/**
* 修改员工
* @param employee 员工实体
*//*
@Override
public void updateEmployee(Employee employee) throws SQLException {
Connection conn = JdbcUtils.getConnection();
//sql
String sql = "update employee set name = '"+employee.getName()+"',age ='"+employee.getAge()+"',gender='"+employee.getGender()+"',address='"+employee.getAddress()+"' where id = '"+employee.getId()+"' " ;
System.out.println(sql);
Statement stmt = conn.createStatement();
int count = stmt.executeUpdate(sql);
System.out.println(count);
}
*/
/**
* 根据员工编号查询员工
* @param id 员工编号
* @return 返回员工实体
*//*
@Override
public Employee findEmpById(int id) throws SQLException {
//获取连接对象
Connection conn = JdbcUtils.getConnection();
//准备sql语句
String sql = "select * from employee where id = '"+id+"'" ; //id字段主键,自增长
//获取执行对象
Statement stmt = conn.createStatement() ;
//执行查询
ResultSet rs = stmt.executeQuery(sql);
Employee emp = null ;
//遍历结果集
while(rs.next()){
//获取数据,将数据封装员工对象中
emp = new Employee() ;
emp.setId(rs.getInt("id")) ;
emp.setName(rs.getString("name"));
emp.setGender(rs.getString("gender")) ;
emp.setAge(rs.getInt("age"));
emp.setSalary(rs.getDouble("salary")) ;
emp.setAddress(rs.getString("address"));
emp.setBirthday(rs.getDate("birthday")) ;
}
return emp;
}
}
package com.qf.dao.impl;
import com.qf.dao.EmployeeDao;
import com.qf.pojo.Employee;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* @author 高圆圆
* @date 2023/4/6 14:25
* 使用PreparedStatement来操作数据库
*/
public class EmployeeDaoImpl2 implements EmployeeDao {
/**
* 修改员工
* @param employee 员工实体
*/
@Override
public void updateEmployee(Employee employee) throws SQLException {
//获取连接对象
Connection connection = JdbcUtils.getConnection();
//sql--参数化的sql
String sql = "update employee set name = ?,gender=?,age=?,salary=?,address=?,birthday=? where id =?" ;
System.out.println(sql) ;
//获取预编译对象
PreparedStatement ps = connection.prepareStatement(sql) ;
//参数赋值
ps.setString(1,employee.getName()) ;
ps.setString(2,employee.getGender()) ;
ps.setInt(3,employee.getAge()) ;
ps.setDouble(4,employee.getSalary()) ;
ps.setString(5,employee.getAddress()) ;
ps.setDate(6,new java.sql.Date(employee.getBirthday().getTime())) ;
ps.setInt(7,employee.getId()) ;
//执行
int count = ps.executeUpdate();
System.out.println(count) ;
}
}
*/
package com.qf.dao;
import com.qf.pojo.Employee;
import java.sql.SQLException;
import java.util.List;
/**
*针对员工持久层(操作数据库:数据访问接口)
*/
public interface EmployeeDao {
/**
* 查询所有员工
* @return 返回员工列表
*/
// List<Employee> findAll() throws SQLException ;
/**
* 添加员工
* @param employee 员工实体
*/
//void add(Employee employee) throws SQLException;
/**
* 通过员工编号删除员工
* @param id 要传进来的员工id
*/
//void deleteEmployeeById(int id) throws SQLException;
/**
* 修改员工
* @param employee 员工实体
*/
void updateEmployee(Employee employee) throws SQLException;
/**
* 根据员工编号查询员工
* @param id 员工编号
* @return 返回员工实体
*/
// Employee findEmpById(int id) throws SQLException;
}
package com.qf.pojo;
import java.util.Date;
/**
* @author 高圆圆
* @date 2023/4/6 9:47
* 员工实体
*/
public class Employee {
/**
* CREATE TABLE employee(
* id INT PRIMARY KEY AUTO_INCREMENT,
* NAME VARCHAR(10) ,
* gender VARCHAR(3) ,
* age INT ,
* salary DOUBLE,
* address VARCHAR(20),
* birthday DATE
* );
*/
private int id ;
private String name ;
private String gender ;
private int age;
private double salary ;
private String address ;
private Date birthday ;
public Employee() {
}
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 int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
", salary=" + salary +
", address='" + address + '\'' +
", birthday=" + birthday +
'}';
}
}
package com.qf.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* 将jdbc步骤优化
* 自定义工具类,将获取连接对象,释放资源都放在静态方法中,方便去使用!
*/
public class JdbcUtils {
//统一资源定位符
private static String url = null ;
//用户名
private static String user = null ;
//密码
private static String password = null ;
//驱动类
private static String driverClassName = null ;
//定义静态代码块
static{
try {
//1)读取src下面的jdbc.properties文件
//创建一个属性集合列表
Properties prop = new Properties() ;
//System.out.println(prop) ;
InputStream inputStream = JdbcUtils.class.getClassLoader().
getResourceAsStream("jdbc.properties");
//将字节输入流加载到属性集合列表中
prop.load(inputStream) ;
//System.out.println(prop) ;
//2)通过key获取value
driverClassName = prop.getProperty("driverClassName");
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password") ;
//3)注册驱动
Class.forName(driverClassName) ;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//对外私有,不让外界new
private JdbcUtils(){}
//定义公共的静态方法,返回值就是Connection
//public static Connection getConnection(String url,String user,String password){ //一会还得需要传入参数,
public static Connection getConnection(){ //一会还得需要传入参数,
Connection conn = null ;
try {
conn = DriverManager.getConnection(url,user,password) ;//url,user,password
return conn;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null ;
}
//释放资源
public static void close(ResultSet rs,Statement stmt,Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//主要针对DDL/DML语句操作释放资源
public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);
}
public static void main(String[] args) {
Connection conn = JdbcUtils.getConnection();
System.out.println(conn);
}
}
package com.qf.test;
import com.qf.dao.EmployeeDao;
//import com.qf.dao.impl.EmployeeDaoImpl;
import com.qf.dao.impl.EmployeeDaoImpl2;
import com.qf.pojo.Employee;
import org.junit.Before;
import org.junit.Test;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* @author 高圆圆
* @date 2023/4/6 10:03
*/
public class EmployeeTest {
private EmployeeDao employeeDao ;
@Before
public void init(){
// employeeDao = new EmployeeDaoImpl() ;
employeeDao = new EmployeeDaoImpl2() ;
}
//测试查询所有员工
/*@Test
public void testFindAll() throws SQLException {
List<Employee> list = employeeDao.findAll();
if(list!=null){
for(Employee emp: list){
System.out.println(emp) ;
}
}
}*/
//测试添加员工
/*@Test
public void testAddEmp() throws SQLException, ParseException {
Employee emp = new Employee() ;
emp.setName("张蒋杰") ;
emp.setGender("男") ;
emp.setAge(24) ;
emp.setSalary(8000.00) ;
emp.setAddress("南窑国际");
//日期文本
String dateStr = "1990-02-27" ;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
Date date = sdf.parse(dateStr);//java.util.Date--->java.sql.date;
//获取Date指定的日期的时间毫秒值
long time = date.getTime();
//创建java.sql.Date对象 --->Date(long date)
java.sql.Date myDate = new java.sql.Date(time) ;
emp.setBirthday(myDate);
employeeDao.add(emp) ;
}*/
//测试删除员工
/* @Test
public void delete() throws SQLException {
employeeDao.deleteEmployeeById(3) ;
}*/
//测试修改员工
@Test
public void updateEmp() throws SQLException, ParseException {
Employee emp = new Employee() ;
emp.setId(5) ;
emp.setName("文章") ;
emp.setAge(35) ;
emp.setGender("男") ;
emp.setSalary(10000.0) ;
String dateStr = "1985-10-30" ;
Date date = new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);
emp.setBirthday(date);
emp.setAddress("西安市") ;
employeeDao.updateEmployee(emp);
}
//根据员工编号查询指定员工信息
/* @Test
public void testFindEmpById() throws SQLException {
Employee emp = employeeDao.findEmpById(1);
System.out.println(emp);
}*/
}
连接池+预编译
package com.qf.dao;
import com.qf.pojo.User;
import java.sql.SQLException;
/**
* @author Kuke
* @date 2023/4/6 11:18
*
* 持久层
* 针对用户实体的数据访问接口
*/
public interface UserDao {
/**
* 判断用户是否登录
* @param user 用户实体
* @return 返回true,表示登录成功,否则,登录失败!
*/
boolean isLogin(User user) throws SQLException;
}
package com.qf.dao.impl;
import com.qf.dao.UserDao;
import com.qf.pojo.User;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @author 高圆圆
* @date 2023/4/6 11:19
* 持久层的实现类(针对用户的)
*/
public class UserDaoImpl implements UserDao {
/**
* 判断用户是否登录
* @param user 用户实体
* @return 返回true,表示登录成功,否则,登录失败!
*/
@Override
public boolean isLogin(User user) throws SQLException {
//获取数据库连接对象
Connection conn = JdbcUtils.getConnection() ;
//sql :直击通过用户名和密码一块查询
String sql = "select * from user where username = '"+user.getUsername()+"' and password ='"+user.getPassword()+"'" ;
System.out.println(sql);
//获取执行对象
Statement stmt = conn.createStatement();
//执行查询
ResultSet rs = stmt.executeQuery(sql);
boolean flag = rs.next() ;
//JdbcUtils.close(rs,stmt,conn);
return flag;
}
}
package com.qf.dao.impl;
import com.qf.dao.UserDao;
import com.qf.pojo.User;
import com.qf.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
*针对用户数据访问接口的 实现
*/
public class UserDaoImpl2 implements UserDao {
/**
* 判断用户是否登录
* @param user 用户实体
* @return 返回true,表示登录成功,否则,登录失败!
*/
@Override
public boolean isLogin(User user) throws SQLException {
//获取数据库连接对象
Connection conn = JdbcUtils.getConnection();
//准备sql---参数化的sql(预编译sql语句)
//select * from user where username = 'xxx' and password= 'xxx';
String sql = "select * from user where username = ? and password = ?" ; //英文符号
System.out.println(sql) ;
//1)通过数据库的连接对象获取预编译对象:将参数化的sql发送数据库,将具体的参数类型以及第几个占位符都存在了PreparedStatement中
/**
* PreparedStatement prepareStatement(String sql)
* throws SQLException创建一个PreparedStatement对象,用于将参数化的SQL语句发送到数据库
*/
PreparedStatement stmt = conn.prepareStatement(sql) ;
//2)使用预编译对象给参数进行赋值
//1:表示第一个,2:表示第二个占位符号
//通用的方法public void setXXX(占位符号的索引值,赋值的实际参数) ;
//举例:预编译对象.setString(1,"高圆圆) ;
stmt.setString(1,user.getUsername()) ;
stmt.setString(2,user.getPassword()) ;
//3)通过预编译对象执行这些参数(在预编译对象中执行参数化的sql)
//int executeUpdate();
//ResultSet executeQuery() ;
ResultSet resultSet = stmt.executeQuery();
boolean flag = resultSet.next();
JdbcUtils.close(resultSet,stmt,conn);
return flag ;
}
}
package com.qf.pojo;
/**
* @author 高圆圆
* @date 2023/4/6 11:17
* 用户实体
*/
public class User {
/**
* CREATE TABLE USER(
* id INT PRIMARY KEY AUTO_INCREMENT ,
* username VARCHAR(10),
* PASSWORD VARCHAR(10)
* ) ;
*/
private int id ; //用户编号
private String username ; //用户名
private String password ; //密码
public User() {
}
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 getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
package com.qf.preparedStatement_login_02;
import com.qf.dao.UserDao;
import com.qf.dao.impl.UserDaoImpl2;
import com.qf.pojo.User;
import java.sql.SQLException;
import java.util.Scanner;
/**
* 需求:
* 使用PreparedStatementStatement完成模拟用户登录操作
* 1)键盘录入用户名和密码
* 2)判断如果键盘录入的信息和已知库存在的信息已知
* 2.1)提示"登录成功"
* 2.3)否则"登录失败"
*/
public class PrepearedStatementTest {
public static void main(String[] args) throws SQLException {
//创建键盘录入对象
Scanner sc = new Scanner(System.in) ;
//提示并录入数据
System.out.println("请输入用户名:" ) ;
String username = sc.nextLine() ;
System.out.println("请输入密码:" ) ;
String password = sc.nextLine() ;
//调用UserDao里面的登录方法
UserDao ud = new UserDaoImpl2() ;
User user = new User() ;
user.setUsername(username) ;
user.setPassword(password);
boolean flag = ud.isLogin(user);
if(flag){
System.out.println("登录成功");
}else{
System.out.println("登录失败!");
}
}
}
package com.qf.statement_login_01;
import com.qf.dao.UserDao;
import com.qf.dao.impl.UserDaoImpl;
import com.qf.pojo.User;
import java.sql.SQLException;
import java.util.Scanner;
/**
* 需求:
* 使用Statement完成模拟用户登录操作
* 1)键盘录入用户名和密码
* 2)判断如果键盘录入的信息和已知库存在的信息已知
* 2.1)提示"登录成功"
* 2.3)否则"登录失败"
*
* 测试正常数据没有问题:
* 但是用户名和密码随便输入,而进行sql拼接,就导致出现安全问题:
*
* SELECT * FROM USER WHERE username = 'adsfds' AND PASSWORD ='sdfsds 'OR'1'='1';-- 恒成立
* 1)sql注入
* 原因:sql语句的实际参数存在字符串拼接,导致语句出现漏洞;(不安全)
* 2)执行sql语句的非常低
* 每一次将写的sql发送数据库,(写一个sql,发送一个次sql,频繁的操作库),
* 高并发情况下,可能造成系统奔溃!
*
* 开发中不会使用Statement去操作数据库,使用PreparedStatement预编译对象
*/
public class StatementTest {
public static void main(String[] args) throws SQLException {
//创建键盘录入对象
Scanner sc = new Scanner(System.in) ;
System.out.println("请您输入用户名:" ) ;
String username = sc.nextLine();
System.out.println("请您输入密码:" ) ;
String password = sc.nextLine();
//创建UserDao接口对象
UserDao ud = new UserDaoImpl() ;
//封装User实体
User user = new User() ;
user.setUsername(username) ;
user.setPassword(password);
boolean flag = ud.isLogin(user);
if(flag){
System.out.println("登录成功!");
}else{
System.out.println("对不起,用户名或者输入错误,登录失败!");
}
}
}
package com.qf.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* 将jdbc步骤优化
* 自定义工具类,将获取连接对象,释放资源都放在静态方法中,方便去使用!
*/
public class JdbcUtils {
//统一资源定位符
private static String url = null ;
//用户名
private static String user = null ;
//密码
private static String password = null ;
//驱动类
private static String driverClassName = null ;
//定义静态代码块
static{
try {
//1)读取src下面的jdbc.properties文件
//创建一个属性集合列表
Properties prop = new Properties() ;
//System.out.println(prop) ;
InputStream inputStream = JdbcUtils.class.getClassLoader().
getResourceAsStream("jdbc.properties");
//将字节输入流加载到属性集合列表中
prop.load(inputStream) ;
//System.out.println(prop) ;
//2)通过key获取value
driverClassName = prop.getProperty("driverClassName");
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password");
//3)注册驱动
Class.forName(driverClassName) ;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//对外私有,不让外界new
private JdbcUtils(){}
//定义公共的静态方法,返回值就是Connection
//public static Connection getConnection(String url,String user,String password){ //一会还得需要传入参数,
public static Connection getConnection(){ //一会还得需要传入参数,
Connection conn = null ;
try {
conn = DriverManager.getConnection(url,user,password) ;//url,user,password
return conn;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null ;
}
//释放资源
//public static void close(ResultSet rs,Statement stmt,Connection conn){
public static void close(ResultSet rs,PreparedStatement stmt,Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//主要针对DDL/DML语句操作释放资源
/* public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);
}*/
public static void close(PreparedStatement stmt,Connection conn){
close(null,stmt,conn);
}
public static void main(String[] args) {
Connection conn = JdbcUtils.getConnection();
System.out.println(conn);
}
}
package com.qf.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
/**
* Jdbc方式---加入Druid连接池,----自动的封装配置文件的所有数据
* 封装一个静态方法--->获取Connection(从连接池获取)
* 获取DataSource:数据源----> 连接参数(数据库的信息)以及初始化数量,最大激活数量,最大等待时间
* 释放资源
*/
public class DruidJdbcUtils {
//静态实例--->java.lang.ThreadLocal<T> 模拟线程,每一个用户都有自己的Connection
private static ThreadLocal<Connection> tl = new ThreadLocal<>() ; //里面没有连接对象
//声明一个DataSource接口类型 变量
private static DataSource ds ;
//构造方法私有化
private DruidJdbcUtils(){}
//静态代码块
static{
try {
//1)读取德鲁伊的配置文件,让德鲁伊自己封装配置文件参数
InputStream inputStream = DruidJdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//创建属性集合列表
Properties prop = new Properties() ;
//加载属性集合列表
prop.load(inputStream) ;
//2)创建DruidDataSource(连接池)
ds = DruidDataSourceFactory.createDataSource(prop) ;
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取DataSource数据源信息 (连接池的所有参数)
public static DataSource getDataSource(){
return ds ;
}
//从数据源(连接池) 获取连接对象
public static Connection getConnection(){
//1)线程本地线程中获取连接对象---ThreadLocal<T>--->T get():从当前线程中获取存储的内容
Connection conn = tl.get() ;
try {
//2)判断
//如果当前conn对象为null,说明当前线程中没有要操作的连接对象
if(conn==null){
//3)从数据源(连接池中)获取Connection
conn = ds.getConnection();
//4)将从数据源中获取到的conn连接对象,绑定当当前线程中
//ThreadLocal<T>--->public void set(T t):绑定
tl.set(conn) ;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return conn ;
}
//释放资源
public static void close(ResultSet rs, PreparedStatement stmt, Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
//从当前线程解绑
tl.remove(); //ThreadLocal<T> :从线程中移出
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//主要针对DDL/DML语句操作释放资源
/* public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);
}*/
public static void close(PreparedStatement stmt,Connection conn){
close(null,stmt,conn);
}
public static void main(String[] args) {
System.out.println(DruidJdbcUtils.getDataSource());
System.out.println(DruidJdbcUtils.getConnection());
}
}
package com.qf.druid_03;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* Druid连接池的使用步骤:
* 1)导包 druid-1.1.10.jar包
* 2)准备好连接池的配置文件
* 3)从连接池获取连接对象
* 提供jar包--->com.alibaba.druid.pool.DruidDataSource--->本质实现了一个接口javax.sql.DataSource
* --->Connection getConnection()
*
*/
public class DruidDemo {
public static void main(String[] args) throws Exception {
//3)创建数据源DruidDataSource对象
//德鲁伊提供的数据源工厂:DruidDataSourceFactory提供静态方法,返回值就是DataSource
//public static DataSource createDataSource(Properties properties)
//创建属性集合列表
Properties prop = new Properties() ;
//读取src下配置文件
InputStream inputStream = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
//将字节输入流加载进来
prop.load(inputStream) ;
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
for(int x = 1 ; x <=11 ; x++){
//System.out.println(dataSource.getConnection());
Connection conn = dataSource.getConnection() ;
if(x==3){
conn.close(); //暂时释放,归还连接池中
}
System.out.println(conn);
}
/**
* 上面这个方法底层已经集成好了
* public static DataSource createDataSource(Map properties) throws Exception {
* DruidDataSource dataSource = new DruidDataSource(); //创建德鲁伊的数据源
* config(dataSource, properties); //封装数据:将配置文件的内容封装到dataSource数据源对象汇总
* return dataSource;//返回
* }
*/
}
}
package com.qf.druid_03;
import com.qf.utils.DruidJdbcUtils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author 高圆圆
* @date 2023/4/6 16:30
*/
public class DruidTest {
public static void main(String[] args) throws SQLException, ParseException {
//获取连接对象
Connection connection = DruidJdbcUtils.getConnection();
//sql
String sql = "insert into employee(name,gender,age,salary,address,birthday) values(?,?,?,?,?,?)" ;
//获取预编译对象
PreparedStatement ps = connection.prepareStatement(sql) ;
//参数赋值
ps.setString(1,"陈志龙") ;
ps.setString(2,"男") ;
ps.setInt(3,35) ;
ps.setDouble(4,15000.0);
ps.setString(5,"西安市") ;
String dateStr = "1981-01-01" ;
Date date = new SimpleDateFormat("yyyy-MM-dd").parse(dateStr);
java.sql.Date sqlDate = new java.sql.Date(date.getTime());
ps.setDate(6,sqlDate);
//执行
int count = ps.executeUpdate();
System.out.println(count) ;
//释放资源
DruidJdbcUtils.close(ps,connection);
}
}
day37
晨写
# 晨写
## 1.使用PreparedStatement完成jdbc操作:DQL语句操作
```java
//导包 (驱动jar包)
//注册驱动
Class.forName("com.mysql.jdbc.Driver") ;
//获取数据库连接对象
Connection conn = DriverManager.getConnecton(
"jdbc:mysql://localhost:3306/库名",
"用户名",
"密码") ;
//sql
String sql = "select * from employee where id = ?" ;
//通过数据库连接对象获取预编译对象,同时发送参数化sql到数据库
PreparedStatement ps = conn.prepareStatement(sql) ;
//对参数进行赋值
ps.setInt(1,id实际值) ;
//通过赋值执行sql语句
Resultset rs = ps.executeQuery() ;
//遍历结果集,封装数据
while(rs.next()){
//获取,封装数据到实体对象中
类名 对象名 = new 类名() ;
对象名.setXXX属性名称(rs.getXXX("column名称")) ;
}
//释放资源
//关闭结果集,关闭预编译对象,关闭连接对象
```
## 2.PreparedStatement和Statement的区别?
```markdown
共同点:
1)都是可以将sql发送到数据库(执行sql语句的执行器),前者接口继承自后者
不同点:
1)是否执行的是静态sql语句
前者是参数化ql语句(里面所有参数都是占位符号?),不是静态sql语句
后者执行的都是静态sql语句,存在一种"硬编码",存在字符串拼接sql的问题
2)是否可以提高sql执行效率
前者:可以提高sql执行效率,只需要给数据库发送一次,然后通过预编译对象赋值不同的参数
后者:相对来说,它效率低;针对每一次新的sql,单独发送一次;
3)是否能够造成sql注入
前者:有效防止sql注入,不存sql的字符串拼接,不会出现安全漏洞
后者:存在安全漏洞,因为存在字符串拼接sql问题;(非常严重的问题)
```
## 3.数据库连接池的好处?
```markdown
节省资源:当前连接池(DataSource接口)创建之后,就会初始化一些连接对象,节省连接对象的创建;
重复利用:当某个线程持有的连接对象被使用完之后,归还连接池等待下次利用;
提高性能:连接池中:只需要配置好这些连接参数(最基本参数:数据库的相关信息)/连接池中的优化参数,DataSource里的实现类---DruidDataSource自己封装这里面的所有连接参数;(还可以不断监控连接池中连接数量的变化)
```
## 4.Druid连接池的使用步骤
```markdown
1)导包druid-1.1.10.jar /mysql的驱动jar包
2)准备配置文件
url=jdbc:mysql://localhost:3306库名
driverClassName=com.mysql.jdbc.Driver
username=root
password=123456
initialSize=初始化连接数量
maxActive=最大激活数量
minIdel=最小空闲数量
maxidel=最大空闲数量
maxWait=最大等待时间毫秒值
3)创建数据源javax.sql.DataSoure接口对象,
//属性集合列表
Properties prop = new Properties() ;
//读取src下面的druid.propertites---->当前类的字节码文件对象获取类加载器--->资源文件的输入流对象
prop.load(字节输入流) ;
DataSource ds = DruidDataSourceFatory.createDataSource(prop) ;
Connection conn = ds.getConnection() ;
```
## 5.加入连接池后自定义工具类的步骤,代码体现
```java
class JdbcUtils{
private static ThreadLocal<Connection> tl = new ThreadLocal<>() ;//每一个线程都有自己的连接对象
private static DataSource ds ;
private JdbcUtils(){}
static{
//1)读取德鲁伊的配置文件,让德鲁伊自己封装配置文件参数
InputStream inputStream = DruidJdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//创建属性集合列表
Properties prop = new Properties() ;
//加载属性集合列表
prop.load(inputStream) ;
//2)创建DruidDataSource(连接池)
ds = DruidDataSourceFactory.createDataSource(prop);
}
public static Connection getConnection(){
Connection conn = tl.get() ;
if(conn==null){
//从数据源中(连接池)获取连接对象
conn = ds.getConnection() ;
//将当前连接对象绑定到自己的线程上
t1.set(conn) ;
}
return conn ;
}
}
```
# 今日内容
## 1.Jdbc控制事务---->javax.sql.Connection
```java
转账业务操作:
同时操作多个账户内容(更新),这些更新语句要么同时执行成功,要么同时执行失败!
public void setAutoCommit(boolean auto) ;参数为true,表示自动提交,如果是false,手动提交
void rollback():事务回滚,撤销之前所有更改,必须释放连接对象,连接对象需要从线程中解绑
void commit():提交事务,将更改数据永久保存,提交完毕,释放连接对象,需要从线程中解绑
throws SQLException
```
## 2.Apache组织结构提供可以工具类库:commons-dbutils (知道每一个api功能作用)
```
对原生jdbc的7大步骤进行简易封装!
sql怎么写?以及要返回结果集里面到低给什么类型的数据?
查询的时候,将查询的多条记录封装到集合List中
查询的时候,将某条记录封装到实体中
查询的时候,将查询的表的总记录数展示( 聚合函数:单行单列的内容)---->封装到Object类中
```
## 3.前端HTML/CSS/Js/
Dbutils
commons-dbutils的使用步骤:
1)导包 commons-dbutils-1.6.jar
2)创建执行器 QueryRunner---> 底层PreparedStatement
public QueryRunner(DataSource ds) 参数就是数据源--->自定义工具获取到了数据源 (自动提交)
public QueryRunner():创建执行器,手动提交
3)准备好sql语句
DML语句---添加/修改/删除
insert into
update
delete from...
QueryRunner提供通用的更新操作:
public int update(Connection conn, String sql, Object... params) throws SQLException :手动提交
public int update(String sql, Object... params):自动提交
DQL语句--- 查询语句
QueryRunner提供的通用查询操作
public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params) throws SQLException
第一个参数:查询的sql语句
第二个参数:ResultSetHandler结果集的处理
它的子实现类:BeanHandler---->将查询的某一条记录封装到实体了中(JavaBean:是具体类,属性私有,对外提供setXXX()/getXXX)
public BeanHandler(Class<T> type) ---> 参数针对查询的记录--封装到类名.class中
子实现类:BeanListHandler<T> ---将查询的多条记录封装到List集合中,List集合都是当前类对象
public BeanListHandler(Class<T> type)
子实现类:
ScalarHandler:通过聚合函数(count(列名称),其他max(xx),avg(xxx))查询单行单的列的数据
的结果封装到Object类中
第三个参数:就是赋值的实际参数params,没有参数可以不写
注意事项:
使用commons-dbutils工具库:无论添加/删除/修改/查询数据,
必须保证实体类的属性名称和表的字段名称一一对应,否则数据封装不上去的,永远是null!
如果不对应,可以在(查询语句)--->给这字段给别名(保证别名和实体类的属性名称一致)
实体类的属性名称不要出现大写字母,否则也可能是null值!
Dbutils对employee增删查改实例
package com.qf.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
/**
* Jdbc方式---加入Druid连接池,----自动的封装配置文件的所有数据
* 封装一个静态方法--->获取Connection(从连接池获取)
* 获取DataSource:数据源----> 连接参数(数据库的信息)以及初始化数量,最大激活数量,最大等待时间
* 释放资源
*/
public class DruidJdbcUtils {
//静态实例--->java.lang.ThreadLocal<T> 模拟线程,每一个用户都有自己的Connection
private static ThreadLocal<Connection> tl = new ThreadLocal<>() ; //里面没有连接对象
//声明一个DataSource接口类型 变量
private static DataSource ds ;
//构造方法私有化
private DruidJdbcUtils(){}
//静态代码块
static{
try {
//1)读取德鲁伊的配置文件,让德鲁伊自己封装配置文件参数
InputStream inputStream = DruidJdbcUtils.class.getClassLoader().getResourceAsStream("druid.properties");
//创建属性集合列表
Properties prop = new Properties() ;
//加载属性集合列表
prop.load(inputStream) ;
//2)创建DruidDataSource(连接池)
ds = DruidDataSourceFactory.createDataSource(prop);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取DataSource数据源信息 (连接池的所有参数)
public static DataSource getDataSource(){
return ds ;
}
//从数据源(连接池) 获取连接对象
public static Connection getConnection(){
//1)线程本地线程中获取连接对象---ThreadLocal<T>--->T get():从当前线程中获取存储的内容
Connection conn = tl.get() ;
try {
//2)判断
//如果当前conn对象为null,说明当前线程中没有要操作的连接对象
if(conn==null){
//3)从数据源(连接池中)获取Connection
conn = ds.getConnection();
//4)将从数据源中获取到的conn连接对象,绑定当当前线程中
//ThreadLocal<T>--->public void set(T t):绑定
tl.set(conn) ;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return conn ;
}
//释放资源
public static void close(ResultSet rs, PreparedStatement stmt, Connection conn){
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null) {
try {
conn.close();
//从当前线程解绑
tl.remove(); //ThreadLocal<T> :从线程中移出
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//主要针对DDL/DML语句操作释放资源
/* public static void close(Statement stmt,Connection conn){
close(null,stmt,conn);
}*/
public static void close(PreparedStatement stmt,Connection conn){
close(null,stmt,conn);
}
/**
* 开启事务
*
*/
public static void setAutoCommit() throws SQLException {
//获取连接对象
Connection conn = getConnection();
//设置手动提交
conn.setAutoCommit(false) ;
}
/**
* 事务回滚
*/
public static void rollbackAndClose() throws SQLException {
//获取连接对象
Connection conn = getConnection();
conn.rollback() ;
//释放连接对象,从当前线程中解绑
conn.close() ;
tl.remove() ;
}
/**
* 提交事务
*/
public static void commitAndClose() throws SQLException {
//获取连接对象
Connection conn = getConnection();
conn.commit(); ;
//释放连接对象,从当前线程中解绑
conn.close();
tl.remove() ;
}
public static void main(String[] args) {
System.out.println(DruidJdbcUtils.getDataSource());
System.out.println(DruidJdbcUtils.getConnection());
}
}
package com.qf.pojo;
import java.util.Date;
/**
* @author 高圆圆
* @date 2023/4/7 11:10
* 员工实体
*/
public class Employee {
/**
*
id int(11) NOT NULL
name varchar(10) NULL
gender varchar(3) NULL
age int(11) NULL
salary double NULL
address varchar(20) NULL
birthday date NULL
*/
private int id ;
private String emp_name ;
private String emp_gender ;
private int age ;
private double salary ;
private String adderss ;
private Date birthday;
//private Dept dept; //员工属于哪个部门的
public Employee() {
}
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 getEmp_name() {
return emp_name;
}
public void setEmp_name(String emp_name) {
this.emp_name = emp_name;
}
public String getEmp_gender() {
return emp_gender;
}
public void setEmp_gender(String emp_gender) {
this.emp_gender = emp_gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String getAdderss() {
return adderss;
}
public void setAdderss(String adderss) {
this.adderss = adderss;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", emp_name='" + emp_name + '\'' +
", emp_gender='" + emp_gender + '\'' +
", age=" + age +
", salary=" + salary +
", adderss='" + adderss + '\'' +
", birthday=" + birthday +
'}';
}
}
package com.qf.dao;
import com.qf.pojo.Employee;
import java.sql.SQLException;
import java.util.List;
/**
* @author Kuke
* @date 2023/4/7 11:12
* 针对员工的数据访问接口(持久层)
*/
public interface EmployeeDao {
/**
* 查询所有员工
* @return 返回员工列表
*/
List<Employee> findAll() throws SQLException;
/**
* 根据员工编号查询员工
* @param id 员工编号
* @return 返回员工实体
*/
Employee findEmpById(int id) throws SQLException;
/**
* 查询总记录数
* @return 返回总条数
*/
int getTotalCount() throws SQLException;
/**
* 添加员工
* @param emp 员工实体
*/
void addEmployee(Employee emp) throws SQLException;
/**
* 根据员工id修改员工信息
* @param emp 员工实体
* @throws SQLException
*/
void updateEmployee(Employee emp) throws SQLException ;
/**
* 根据员工姓名模糊查询
* @param name 关键字员工名称
* @return 返回员工列表
*/
List<Employee> findEmpByName(String name) throws SQLException;
}
package com.qf.dao.impl;
import com.qf.dao.EmployeeDao;
import com.qf.pojo.Employee;
import com.qf.utils.DruidJdbcUtils;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;
import java.sql.SQLException;
import java.util.List;
/**
* @author 高圆圆
* @date 2023/4/7 11:13
*/
public class EmployeeDaoImpl implements EmployeeDao {
/**
* 查询所有员工
* @return 返回员工列表
*/
@Override
public List<Employee> findAll() throws SQLException {
//使用apache提供开源工库实现
//创建QueryRunner
//public QueryRunner(DataSource ds)
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "select * from employee" ;
//将查询的多条记录封装List集合,集合List存储每一个员工对象
//public <T> T query(String sql, ResultSetHandler<T> rsh, Object... params)
//子实现类:BeanListHandler<T> ---将查询的多条记录封装到List集合中,List集合都是当前类对象
List<Employee> list = qr.query(sql, new BeanListHandler<>(Employee.class));
return list;
}
/**
* 根据员工编号查询员工
* @param id 员工编号
* @return 返回员工实体
*/
@Override
public Employee findEmpById(int id) throws SQLException {
//创建执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "select * from employee where id = ?" ;
//执行查询: 将某一条记录封装到实体中
/**
* 它的子实现类:BeanHandler---->将查询的某一条记录封装到实体了中(JavaBean:是具体类,属性私有,对外提供setXXX()/getXXX)
* public BeanHandler(Class<T> type) ---> 参数针对查询的记录--封装到类名.class中
*/
Employee emp = qr.query(sql, new BeanHandler<>(Employee.class), id);
return emp;
}
/**
* 查询总记录数
* @return 返回总条数
*/
@Override
public int getTotalCount() throws SQLException {
//执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "select count(id) from employee" ;
//将查询的结果:当行单例的数据封装到Object中
/**
* ScalarHandler:通过聚合函数(count(列名称),其他max(xx),avg(xxx))查询单行单的列的数据
* 的结果封装到Object类中
* //public ScalarHandler()
*/
Object object = qr.query(sql, new ScalarHandler<>());
//5----取出来
String s = String.valueOf(object);
int totalCount = Integer.parseInt(s);
return totalCount;
}
/**
* 添加员工
* @param emp 员工实体
*/
@Override
public void addEmployee(Employee emp) throws SQLException {
//创建执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "insert into employee(name,gender,age,salary,address,birthday) value(?,?,?,?,?,?)" ;
//public int update(String sql, Object... params):自动提交
int count = qr.update(sql,
// emp.getName(),
//emp.getGender(),
emp.getAge(),
emp.getSalary(),
emp.getAdderss(),
emp.getBirthday()
);
System.out.println("影响了"+count+"行");
}
/**
* 根据员工id修改员工信息
* @param emp 员工实体
* @throws SQLException
*/
@Override
public void updateEmployee(Employee emp) throws SQLException {
//创建执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "update employee set name =?,gender =?,age =?,salary = ?,address=?,birthday=? where id = ?" ;
//public int update(String sql, Object... params):自动提交
int count = qr.update(sql,
// emp.getName(),
//emp.getGender(),
emp.getAge(),
emp.getSalary(),
emp.getAdderss(),
emp.getBirthday(),
emp.getId()
);
System.out.println("影响了"+count+"行");
}
/**
* 根据员工姓名模糊查询
* @param name 关键字员工名称
* @return 返回员工列表
*/
@Override
public List<Employee> findEmpByName(String name) throws SQLException {
//执行对象
QueryRunner qr = new QueryRunner(DruidJdbcUtils.getDataSource()) ;
//sql
String sql = "select name as emp_name,gender as emp_gender,age,salary,address as adderss ,birthday from employee where name like ?" ;
//查询
List<Employee> list = qr.query(sql, new BeanListHandler<>(Employee.class), name);
return list;
}
}
package com.qf.test;
import com.qf.dao.EmployeeDao;
import com.qf.dao.impl.EmployeeDaoImpl;
import com.qf.pojo.Employee;
import org.junit.Before;
import org.junit.Test;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* @author 高圆圆
* @date 2023/4/7 11:13
* 测试
*/
public class EmployeeTest {
private EmployeeDao ed ;
@Before
public void init(){
ed = new EmployeeDaoImpl() ;
}
//查询所有
@Test
public void testFindAll() throws SQLException {
List<Employee> all = ed.findAll();
if(all!=null){
for(Employee emp :all){
System.out.println(emp);
}
}
}
//查询执行员工
@Test
public void testFindEmpById() throws SQLException {
Employee emp = ed.findEmpById(6);
System.out.println(emp);
}
//查询总记录数
@Test
public void testTotalCount() throws SQLException {
int count = ed.getTotalCount();
System.out.println(count);
}
//测试添加员工
@Test
public void testAdd() throws ParseException, SQLException {
//封装员工实体
Employee employee = new Employee() ;
//employee.setName("张三丰") ;
//employee.setGender("男") ;
employee.setAge(45) ;
employee.setSalary(6000.0) ;
employee.setAdderss("西安市") ;
String birthdayDate = "1985-10-20" ;
Date date = new SimpleDateFormat("yyyy-MM-dd").parse(birthdayDate);
employee.setBirthday(new java.sql.Date(date.getTime()));
ed.addEmployee(employee) ;
}
//测试修改
@Test
public void testUpdate() throws ParseException, SQLException {
//封装员工实体
Employee employee = new Employee() ;
employee.setId(7);
//employee.setName("张无忌") ;
//employee.setGender("男") ;
employee.setAge(20) ;
employee.setSalary(8000.0) ;
employee.setAdderss("神木市") ;
String birthdayDate = "1990-05-01" ;
Date date = new SimpleDateFormat("yyyy-MM-dd").parse(birthdayDate);
employee.setBirthday(new java.sql.Date(date.getTime()));
ed.updateEmployee(employee); ;
}
//模糊搜索
@Test
public void testFindEmpByName() throws SQLException {
List<Employee> list = ed.findEmpByName("张%");
if(list!=null){
for(Employee emp:list){
System.out.println(emp);
}
}
}
}
jdbc事务
package com.qf.jdbc_transaction_01;
import com.alibaba.druid.support.json.JSONUtils;
import com.qf.utils.DruidJdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* Jdbc控制事务:
* public void setAutoCommit(boolean auto) ;参数为true,表示自动提交,如果是false,手动提交
* void rollback():事务回滚,撤销之前所有更改,必须释放连接对象,连接对象需要从线程中解绑
* void commit():提交事务,将更改数据永久保存,提交完毕,释放连接对象,需要从线程中解绑
*
* 什么是事务?
* 针对多个sql或者多张表的sql(增删改),要么同时执行成功,要么同时执行失败,应该将整个业务看成一个整体,一块执行!
* 事务的特点:
* 原子性,一致性,隔离性,持久性
*
* 需求:
* 转账业务:
* account账户表中:id为3的用户给id为4的账户进行转账500
*/
public class TranserDemo {
public static void main(String[] args) {
Connection conn = null ;
PreparedStatement ps1= null ;
PreparedStatement ps2= null ;
try {
//获取连接池中的初始化连接数量中的连接对象
conn = DruidJdbcUtils.getConnection();
//开启手动提交模式(开启事务)
//public void setAutoCommit(boolean auto) ;参数为true,表示自动提交,如果是false,手动提交
conn.setAutoCommit(false) ;
//sql语句
String sql1 = "update account set balance = balance - ? where id = ?" ;
String sql2 = "update account set balance = balance + ? where id = ?" ;
//发送参数化的sql获取编译对象
ps1 = conn.prepareStatement(sql1) ;
ps2 = conn.prepareStatement(sql2) ;
//参数赋值
ps1.setInt(1,500) ;
ps1.setInt(2,3) ;
ps2.setInt(1,500) ;
ps2.setInt(2,4) ;
//在预编译对象中执行
int count = ps1.executeUpdate();
int i = 10/0 ; //明显给了异常,实际开发中可能其他系统监控代码出现bug了也会造成 前面sql执行了,后面没有执行1
int count2 = ps2.executeUpdate();
System.out.println(count+"----"+count2) ;
// void commit():提交事务,将更改数据永久保存,提交完毕,释放连接对象,需要从线程中解绑
conn.commit(); //事务提交
} catch (SQLException throwables) {
//事务回滚
// void rollback():事务回滚,撤销之前所有更改,必须释放连接对象,连接对象需要从线程中解绑
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
throwables.printStackTrace();
} finally {
DruidJdbcUtils.close(ps1,conn) ;
DruidJdbcUtils.close(ps2,conn) ;
}
}
}