day01、mysql基础
1、数据库相关概念
-
数据库(DataBase)
- 存储数据的仓库,数据是有组织的进行存储
- 本质是单子化的文件柜
-
数据库管理系统(DataBase Management System)
- 管理数据的大型软件
-
常见的关系型数据库管理系统
•Oracle:收费的大型数据库,Oracle 公司的产品
•MySQL: 开源免费的中小型数据库。后来 Sun公司收购了 MySQL,而 Sun 公司又被 Oracle 收购
•SQL Server:MicroSoft 公司收费的中型的数据库。C#、.net 等语言常使用
•PostgreSQL:开源免费中小型的数据库
•DB2:IBM 公司的大型收费数据库产品
•SQLite:嵌入式的微型数据库。如:作为 Android 内置数据库
•MariaDB:开源免费中小型的数据库
-
MySQL数据模型
-
在MySQL数据管理系统中创建的数据库,在硬盘中对应的是一个文件夹
- mysql是关系型数据库管理系统
- 关系型数据库是由多张能互相连接的 二维表 组成的数据库
- 都是使用表结构,格式一致,易于维护。
- 使用通用的 SQL 语言操作,使用方便,可用于复杂查询。
- 数据存储在磁盘中,安全。
- 关系型数据库是由多张能互相连接的 二维表 组成的数据库
SQL
- 英文:Structured Query Language,简称 SQL,结构化查询语言
- 操作关系型数据库的编程语言
- 一个标准,一个操作所有关系型数据库的统一标准
2、SQL-通用语法和分类
- 通用语法
1.sql可以单行或多行书写,以分好 ";" 结尾
2.不区分大小写,建议关键字大写
3.注释
-- 单行注释
以 -- 注释的内容
以 #注释的内容
-- 多行注释
/*注释的内容*/
- SQL的分类
1.DDL(Data Definition Language) 数据定义语言,用来定义数据库对象:数据库、表、列。。
2.DML(Data Manipulation Language) 数据操作语言,用来对数据库表中的数据进行增删改
3.DQL(Data Query Language) 数据查询语言,用来查询数据库表中的记录
4.DCL(Data Control Language) 数据控制语言,用来定义数据库的访问权限和安全级别,以及创建用户。
3、DDL
3.1、操作数据库
-
查询所并展示所有的数据库 {36D94110-787C-4828-9C1B-0DAFEBC36069}
show databases;
-
创建数据库
create database 数据库名称; /* 创建数据库,进行判断,如果不存在就创建 */ create database if not exists 数据库名称;
- create database 数据库名称;
- 注意:当数据库已经存在,再创建就会报错【Can’t create database ‘book’; database exists】
- create database 数据库名称;
-
删除
drop database 数据库名称; /* 删除数据库,进行判断,如果存在就删除 */ drop database if exists 数据库名称;
- drop database 数据库名称;
- 注意:当数据库已经不存在,再删除就会报错。【Can’t drop database ‘db1’; database doesn’t exist】
- drop database 数据库名称;
-
使用当前数据库
use 数据库名;
-
查看当前使用的是哪一个数据库
select database();
3.2、表-查询(Retrieve)
# 1.进入到某一个数据库
use book;
# 2.查询当前数据库下所有表名称
SHOW TABLES ;
# 3.查询表结构
DESC 表名称;
3.3、表-创建(Create)
创建表的格式:
create table 表名称(
字段1 数据类型1,
字段2 数据类型2,
...
字段n 数据类型n
);
# 注意,最后一个字段的数据类型没有逗号
-
举例:
-
创建表 tb_user
-
create table tb_user( id int, username varchar(10), password varchar(20) );
-
3.4、表-删除、修改
-
删除表
drop table 表名;
-
删除表时,判断表是否存在
drop table if exists 表名;
-
修改表
-- 1. 修改表名 alter table 表名 rename to 新的表名; -- alter table student rename to stu; -- 2. 添加一列字段 alter table 表名 add 列名 数据类型; -- alter table student add address varchar(50); -- 3. 修改字段的数据类型 alter table 表名 modify 字段名 新的数据类型; -- alter table student modify address char(20); -- 4. 修改字段名和数据类型 alter table 表名 change 旧字段名 新的字段名 新的字段的数据类型; -- alter table student change address addr varchar(10); -- 5. 删除字段 alter table 表名 drop 字段名; -- alter table student drop address;
4、表-数据类型
-
MySQL支持多种数据类型,可以分为3类。数值、日期、字符串
-
数值类型
-
-- 整数 tinyint : 小数整值,占一个字节,相当于java中的 byte ------ 【常用】 smallint : 大整数值,占2个字节,相当于java中的 short mediumint : 大整数值,占3个字节 int 或 Integer : 大整数值,占4个字节,相当于java中的 int ------ 【常用】 bigint : 极大整数值,占8个字节,相当于java中的 long -- 浮点数 float : 单精度浮点数,占4个字节,相当于java中的 float ------ 【常用】 double : 双精度浮点数,占8个字节,相当于java中的 double ------ 【常用】 decimal : 小数值,类似于java中BigDecimal工具类,高精度运算。
-
注意 double 的使用格式
-
double(数值的长度 , 保留的小数位数) 分数取值范围 0 - 100,保留两位小数,数值总长度为 5 -- score = 95.50 ----> double(5,2) create table student( scroe double(5,2) );
-
-
-
-
-
日期类型
-
date : 日期值,只能表示年月日,占3个字节 ------ 【常用】 time : 时间值或持续时间,十分秒,占3个字节 year : 表示年份,占1个字节 dateTime : 混合日期和时间值,年月日时分秒,占8个字节 ------ 【常用】 timestamp : 混合日期和时间值,常表示时间戳,占4个字节
-
-
字符串
-
char : 定长字符串,字符存储大小 0 - 255个字节 ------ 【常用】 varchar : 变长字符串,字符存储大小 0 - 65535个字节 ------ 【常用】 text : 长文本数据,字符存储大小 0 - 65535个字节
* **字符串类型中,char 和 varchar 的区别** char : 定长字符串 -- 存储性能高 -- 浪费空间 ---- name char(50) 如果存储的数据字符个数不足50个,也会占用50个的内存空间 varchar : 变长字符串 -- 存储性能低 -- 节约空间 ---- name varchar(50) 如果存储的数据字符个数不足50个,有多少个字符个数,就占用多少内存空间。
-
-
需求:设计一张学生表,请注重数据类型、长度的合理性
1.编号
2.姓名,姓名最长不超过10个汉字
3.性别,因为取值只有两种可能,因此最多一个汉字
4.生日,取值为年月日
5.入学成绩,小数点后保留两位
6.邮件地址,最大长度不超过 64
7.家庭联系电话,不一定是手机号码,可能会出现 - 等字符
8.学生状态(用数字表示,正常、休学、毕业…)
CREATE TABLE IF NOT EXISTS student( id CHAR(10), name VARCHAR(20), gender CHAR(1), birthday DATE, score DOUBLE(5,2), email VARCHAR(64), telephone VARCHAR(20), state TINYINT );
5、DML
5.1、添加数据
-
给指定列添加数据
insert into 表名 (列名1, 列名2,....) values (值1, 值2, .....); -- insert into stu (id,name) values (1,"张三");
-
给全部列添加数据的时候,省略字段名【开发中不推荐】
insert into 表名 values (值1, 值2, ....); -- insert into stu values (2,"李四","男","1998-11-15",95,"2657@qq.com","10086",0);
-
批量添加数据
-- 不省略字段名 insert into stu (id,name,gender,birthday,score,email,telephone,state) values (3,"王五","男","1998-11-15",95,"2657@qq.com","10086",0), (4,"赵六","男","1998-11-15",95,"2657@qq.com","10086",0); -- 省略字段名 insert into stu values (3,"王五","男","1998-11-15",95,"2657@qq.com","10086",0), (4,"赵六","男","1998-11-15",95,"2657@qq.com","10086",0);
5.2、修改、删除数据
-
修改数据
update 表名 set 字段值1 = 值1 , 字段值2 = 值2 , .... where 条件 ;
示例:
-- 修改赵六的性别为 女 update stu set gender = "女" where name = "赵六" ; -- 如果没有where条件限制,会修改所有gender字段的数据为 女 update stu set gender = "女" ;
-
删除数据
delete 表名 where 条件 ;
示例
-- 删除姓名为李四的学生信息 delete from stu where name = "李四" ; -- 如果没有where条件限制,会把表中的所有数据删除。 delete from stu ;
-
注意点:
-
-- 修改: -- 修改表中的数据,如果不添加条件限制,对应的字段的数据值会全部被修改。 -- 删除: -- 删除表中的数据,如果不添加条件限制,会删除表中所有的数据。
-
6、DQL
6.1、基础查询
-
基础查询的语法
select 字段列表 from 表名列表 where 条件列表 group by 分组字段 having 分组后条件 order by 排序字节 limit 分页限定 ;
- 基础查询
- 条件查询(WHERE)
- 分组查询(GROUP BY)
- 排序查询(ORDER BY)
- 分页查询(LIMIT)
-
基础查询
- 查询表中的全部字段信息
-- 查询一个表中的所有字段信息【开发中不能这样写】 select * from 表名 ;
- 查询多个字段信息
-- 查询多个字段的信息 select 字段1 , 字段2 ,.... from 表名 ; -- 查询学生的姓名、性别的信息 select name, gender from stu;
- 对查询到的数据进行去重(distinct)。【联想:Stream流中的元素去重,distinct( ) 方法。】
-- 对象查询到的字段数据信息进行去重,使用distinct关键字,distinct 后面只跟一个字段。 select distinct 字段1 , 字段2 from 表名 ; -- 查询学生的地址,并去掉重复的地址信息 select distinct address FROM stu;
- 给字段起别名 , 其中,as 关键字可以省略不写。
select 字段1 as 名字1 , 字段2 as 名字2 ... from 表名 ; -- 给name、math、english字段起别名 select name as 姓名 , math as 数学 , english as 英语 from stu; -- 省略 as 关键字 select name 姓名 , math 数学 , english 英语 from stu;
6.2、条件查询
-
条件查询语法
select 字段列表 from 表名 where 条件列表 ;
-
条件
> 大于 between....and.... 在某个范围之内(都包含) >= 大于等于 in(......) 其中的任意一个 < 小于 like 占位符 模糊查询, _单个任意字符 %多个任意字符 <= 小于等于 is null 是null = 等于 is not null 不是null <> 或 != 不等于 and 或 && 并且 or 或 || 或者 not 或 ! 非,不是
实例:
-- 查询年龄大于20并且年龄小于50的学生信息 select * from stu where age >= 20 and age <= 50; -- 使用bewteen ... and ... 【注意】 不能写成 between 50 and 20; select * from stu where age between 20 and 50; -- 查询英语成绩是null的学生信息 select * from stu where english = null ; -- 错误示范 /* 注意:null的判断只能是 is null 或者 is not null */ select * from stu where english is null; -- 查询1994年到1998年的学生信息 select * from stu where hire_date >= "1994-01-01" and hire_date <= "1998-12-31"; -- 使用bewteen ... and ... select * from stu where hire_date between "1994-01-01" and "1998-12-31"; -- 查询年龄是18、20、22岁的学生年龄 select * from stu where age = 18 or age = 20 or age =22 ; -- 使用 in(...) select * from stu where age in(18,20,22) ;
6.3、模糊查询
-
模糊查询
select 字段 from 表名 where 字段 like 通配符 ; -- 通配符 _ 匹配单个字符 % 匹配多个字符
实例:
-- 查询姓 "马" 的学生信息 select * from stu where name like "马%" ; -- 查询姓名中的第二个子是 "花" 的学生信息 select * from stu where name like "_花%" ; -- 查询姓名中包含 "德" 的学生信息 select * from stu where name like "%德%" ;
6.4、排序查询
-
排序查询【 order by 】
select 字段列表 from 表名 order by 字段1 排序方式一 , 字段2 排序方式二 ,.... ; -- 排序方式 asc 升序排序【默认排序】 desc 降序排序
实例:
-- 对学生信息按照年龄进行升序排序 select * from stu order by age asc; -- 把学生的信息按照数学成绩进行降序排序 select * from stu order by math desc; -- 把学生信息先按照学生成绩进行降序排序,如果学生的数学成绩相同,再按照英语成绩进行升序排序 select * from stu order by math desc , english asc;
6.5、聚合查询
-
聚合函数(统计函数)
-
概念:
将一列数据作为一个整体,进行纵向计算
-
聚合函数分类
count(列名) 统计数量(不选用含有null值的列) max(列名) 最大值 min(列名) 最小值 sum(列名) 求和 avg(列名) 平均值 ifnull(列名,自定义值) -- 如果列名中有null值,使用自定义值代替
-
聚合函数的语法
select 聚合函数名(列名) from 表 ;
-
注意点:
null 值不参与所有聚合函数运算。
实例:
-- 统计学生人数 【推荐使用 * ,会自动选择非null的列进行统计】 select count(*) from stu; -- 统计学生数学成绩的平均值 select avg(math) from stu ;
-
6.6、分组查询
-
分组查询语法
select 字段列表 from 表名 where 条件 group by 分组字段名 having 分组后条件过滤 ;
- where 和 having 区别
1.执行时机不一样: -- where是分组之前进行限定,不满足where条件,则不参与分组。 -- having是分组之后对结果进行过滤。 2.判断的条件不一样: -- where不能对聚合函数进行判断,having可以 3.执行顺序不一样: -- where > group by > 聚合函数 > having
-
分组之前,聚合函数获取大道的值只有一个值,where条件过滤,对于一个值,过滤没有意义。
-
注意点:
1. 分组之后,查询的字段为聚合函数和分组的字段,查询其他字段无任何意义。 -- 查询男生和女生的数学科目各自的平均分,【插入姓名字段,平均分是整体的平均分,不是个人的平均分,姓名字段没有意义】 select name , sex, avg(math) from stu group by sex ;
实例:
-- 1. 查询男同学和女同学各自的数学平均分 (注意事项) select sex, avg(math) from stu group by sex ; -- 2. 查询男同学和女同学各自的数学平均分,以及各自人数 select sex, avg(math), count(*) from stu group by sex; -- 3. 查询男同学和女同学各自的数学平均分,以及各自人数,要求:分数低于70分的不参与分组 select sex, avg(math), count(*) from stu where math > 70 group by sex; -- 4. 查询男同学和女同学各自的数学平均分,以及各自人数,要求:分数低于70分的不参与分组,分组之后人数大于2个的。 select sex, avg(math), count(*) from stu where math > 70 group by sex having count(*) > 2;
6.7、分页查询
-
分页查询语法
select 字段列表 from 表名 limit 起始索引, 最大的记录行数 ;
-
起始索引从 0 开始
-
计算公式:起始索引 = ( 当前页码 - 1 ) * 每页显示的记录行数
注意点: 分页查询的关键字在不同数据库有不同表现形式。
1. 分页查询 limit 是MySQL数据库的方言 2. Oracle 分页查询使用的是 rownum 3. SQL Server 分页查询使用的是 top
实例:
-- 假如表中有8条记录,每页显示的记录行数为3条 -- 第一页显示3条记录【1 - 3】 select * from stu limit 0 , 3; -- 第二页显示的3条记录【4 - 6】 select * from stu limit 3 , 3; -- 第三页显示的2条记录【7 - 8】 select * from stu limit 6 , 3;
-
day02、mysql高级
1、约束
1.1、概述和分类
-
约束的概念
1. 约束是作用于表中列上的规则,用于限制加入表中的数据 2. 约束的存在保证了数据库中数据的正确性、有效性和完整性
-
约束的分类
not null 非空约束 保证列中所有数据不能有null值 unique 唯一约束 保证列中的所有数据不相同 primary key 主键约束 主键是一行数据的唯一标识,要求非空且唯一 check 检查约束 保证列中的值满足某一条件 default 默认约束 保存数据时,为指定值采用默认值 foreign key 外键约束 外键用来让两个表的数据之间建立链接,保证数据的一致性和完整性
实例
CREATE TABLE emp ( id INT primary key auto_increment, -- 员工id,主键且自增长(最后加) ename VARCHAR(50) unique not null, -- 员工姓名,非空并且唯一 joindate DATE not null, -- 入职日期,非空 salary DOUBLE(7,2) not null, -- 工资,非空 bonus DOUBLE(7,2) default 0 -- 奖金,如果没有奖金默认为0 ); -- 演示默认约束 -- 默认如果有空值,就会使用空值 INSERT INTO emp(id,ename,joindate,salary,bonus) values(2,'王五','1999-11-11',8800,null); -- 不提供默认值字段,会使用默认值 INSERT INTO emp(id,ename,joindate,salary) values(3,'李四','1999-11-11',8800);
-
MySQL没有检查约束
-
自动增长,当列是数值,且唯一时,可以使用 auto_increment,一个表只能有一个自动增长列!
-- 当主键是数值,且是自动增长, -- 主键可以填入null值,它会自动增长,填入数值 INSERT INTO emp(id,ename,joindate,salary,bonus) values(null,'王五','1999-11-11',8800,null); -- 主键字段可以直接省略,它会自动增长,填入数值 INSERT INTO emp(ename,joindate,salary,bonus) values('王五','1999-11-11',8800,null);
非空约束
非空约束用于保证列中所有数据不能有null值
语法
1. 创建表时添加非空约束 create table 表名( name varchar(20) not null ); 2. 创建完表后,再添加非空约束 alter table 表名 modify name varchar(20) not null; 3. 删除非空约束 alter table 表名 modify name varchar(20) ;
唯一约束
唯一约束用于保证列中所有数据各不相同
语法
1. 创建表的时候 -- 1、 create table 表名( name varchar(20) unique ); -- 2、 create table 表名( name varchar(20), [constraint] [约束名 ] unique (列名) ); 2. 建完表后,再添加唯一约束 alter table 表名 modify name varchar(20) unique; 3. 删除唯一约束 alter table 表名 drop index 字段名 ;
主键约束
主键是一行数据的唯一标识,要求非空且唯一,一张表只能有一个主键
语法
1. 创建表的时候添加主键 -- 1、 create table 表名( id int primary key [auto_increment] ); -- 2、 create table 表名( id int, [constaint] [约束名称] primary key (列名) ); 2. 建完表后添加主键 alter table 表名 add primary key (字段名); 3. 删除主键 alter table 表名 drop primary key;
默认约束
保存数据时,未指定值,采用默认值
语法
1. 创建表的时候,添加默认约束 create table 表名( name varchar(20) default "张三" ); 2. 创建完后添加默认约束 alter table 表名 alter 列名 set default 默认值 ; 3. 删除默认约束 alter table 表名 alter 列名 drop default ;
外键约束
外键用来让两个表的数据之间建立链接,保证数据的一致性和完整性
语法
1. 创建表的时候添加外键 create table 表名 ( name varchar(20), [constraint] [外键名称] foreign key (外键列名) references 主表(主表列名) ); 2. 建完表后添加外键约束 alter table 表名 add constraint 外键名 foreign key (外键字段名) references 主表(主表列名) ; 3. 删除外键约束 alter table 表名 drop foreign key 外键名 ;
2、数据库设计
2.1、简介
-
数据库设计概念
- 数据库设计就是根据业务系统的具体需求,结合我们所选用的DBMS,为这个业务系统构造出最优的数据存储模型。
- 建立数据库中的表结构以及表与表之间的关联关系的过程。
- 有哪些表?表里有哪些字段?表和表之间有什么关系?
-
数据库设计的步骤
- 分析有哪些实体(一个实体,就是一个表)
- 分析实体有哪些属性(属性就是表中的列)
- 分析实体之间的关系(就是表之间的关系)
-
表关系
- 一对一:
- 如:用户 和 用户详情
- 一对一关系多用于表拆分,将一个实体中经常使用的字段放一张表,不经常使用的字段放另一张表,用于提升查询性能
- 一对多(多对一):
- 如:部门 和 员工
- 一个部门对应多个员工,一个员工对应一个部门
- 多对多
- 如:商品 和 订单
- 一个商品对应多个订单,一个订单包含多个商品
- 一对一:
2.2、多表关系实现
-
表关系之一对一
- 实现方式:在任意一方加入外键,关联另一方主键,并且设置外键为唯一(UNIQUE)
- 实现方式:在任意一方加入外键,关联另一方主键,并且设置外键为唯一(UNIQUE)
-
表关系之一对多
- 实现方式:在多的一方建立外键,指向一的一方的主键
- 实现方式:在多的一方建立外键,指向一的一方的主键
-
表关系之多对多
- 实现方式:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
- 实现方式:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
3、多表查询
简介
-
从多张表查询数据
-
笛卡尔积现象
-
select * from 表名1 , 表名2 ; -- 没有where条件限制
-
3.1、内连接
-
查询多个表中的交集部分
-
内连接查询语法
-
-- 隐式内连接 SELECT 字段列表 FROM 表1,表2… WHERE 条件; -- 显式内连接 SELECT 字段列表 FROM 表1 [INNER] JOIN 表2 ON 条件;
-
示例
-- 隐式内连接 select * from emp,dept where emp.dep_id = dept.did; -- 显式内连接 select * from emp inner join dept on emp.dep_id = dept.did;
-
3.2、外连接
-
左外连接:相当于查询A表所有数据和交集部分数据
-
-- 左外连接 SELECT 字段列表 FROM 表1 LEFT [OUTER] JOIN 表2 ON 条件;
-
示例:
-- 左外连接 select * from emp LEFT OUTER join dept on emp.dep_id = dept.did;
-
右外连接:相当于查询B表所有数据和交集部分数据
-
-- 右外连接 SELECT 字段列表 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 条件;
-
示例
-- 右外连接 select * from emp right outer join dept on emp.dep_id = dept.did;
3.3、子查询
-
子查询概念:
-
查询中嵌套查询。
-
-- 查询工资高于猪八戒工资的员工信息 -- 1.查询猪八戒的工资 select salary from emp where name = "猪八戒"; -- 2.查询所有员工的工资,要求大于猪八戒的工资 select * from emp where salary > (select salary from emp where name = "猪八戒");
-
-
根据查询结果不同,作用不同,分为3中子查询
-
单行单列:作为条件值,使用 = != > < 等条件进行判断
-
select 字段列表 from 表名 where 字段名 = (子查询);
示例
-- 查询工资高于猪八戒工资的员工信息 -- 1.查询猪八戒的工资 select salary from emp where name = "猪八戒"; -- 2.查询所有员工的工资,要求大于猪八戒的工资 select * from emp where salary > (select salary from emp where name = "猪八戒");
-
-
多行单列:作为条件值,使用 in 等关键字进行条件判断
-
select 字段列表 from 表名 where 字段名 in (子查询);
示例
-- 1.查询 '财务部' 和 '市场部' 所有的员工信息 -- 1.1 查询财务部和市场部的部门编号 select did from dept where dname in ('财务部','市场部'); -- 1.2 查询 '财务部' 和 '市场部' 的员工信息 select * from emp where dep_id in (select did from dept where dname in ('财务部','市场部'));
-
-
多行多列:作为虚拟表
-
select 字段列表 from (子查询) where 条件 ;
示例
-- 2.查询入职日期是 '2011-11-11' 之后的员工信息和部门信息 select * from emp where join_date > '2011-11-11'; -- 2.1查询 入职日期是 '2011-11-11' 之后的员工信息 -- 2.2 将上面的结果与部门表(dept)连接 select t1.*,dept.dname from (select * from emp where join_date > '2011-11-11') as t1 inner join dept on t1.dep_id = dept.did;
-
-
4、事务
简介
-
数据库的事务(Transaction)是一种机制、一个操作序列,包含了一组数据库操作命令
事务把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么同时成功,要么同时失败
事务是一个不可分割的工作逻辑单元
-
-- 开启事务 START TRANSACTION; 或者 BEGIN; -- 提交事务 COMMIT; -- 回滚事务 ROLLBACK;
四大特征
-
原子性(Atomicity): 事务是不可分割的最小操作单位,要么同时成功,要么同时失败
-
一致性(Consistency) :事务完成时,必须使所有的数据都保持一致状态
-
隔离性(Isolation) :多个事务之间,操作的可见性
-
持久性(Durability) :事务一旦提交或回滚,它对数据库中的数据的改变就是永久的
MySQL事务默认自动提交
-- 查看事务的默认提交方式
SELECT @@autocommit;
-- 1 自动提交 0 手动提交
--修改事务提交方式
set @@autocommit = 0;
day03、JDBC
1、简介和快速入门
1.1、简介
- JDBC(Java DataBase Connectivity)的概念
- JDBC就是使用java语言操作关系型数据库的一套API
- JDBC的本质
- 官方定义的一套操作所有关系型数据库的规则,即接口
- 各个数据库厂商去实现这套接口(接口的实现类),提供数据库驱动jar包
- 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
- JDBC的好处
- 各个数据库厂商使用相同的接口,java代码不需要针对不同数据库分别开发
- 可随时替换底层数据库,访问数据库的java代码基本不变
- 同一套java代码,可以操作不同的数据库
1.2、快速入门
0. 导入驱动jar包
1. 注册驱动
Class.forName("com.mysql.jdbc.Driver");
2. 获取连接
Connection con = DriverManager.getConnection(url,username,password);
3. 定义SQL语句
String sql = "...";
4. 获取SQL执行对象
Statement stmt = con.createStatement();
5. 执行SQL语句
int count = stmt.executeUpdate(sql);
6. 处理返回结果
System.out.println("受影响的行数: " + count);
7. 释放资源
stmt.close();
con.close();
实例:
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
String url = "jdbc:mysql://localhost:3306/db1";
String username ="root";
String password = "123456";
Connection con = DriverManager.getConnection(url,username,password);
// 3.定义SQL语句
String sql = "update emp set ename = '孙行者' where id = 1001";
// 4.获取执行SQL对象 Statement
Statement stmt = con.createStatement();
// 5.执行sql语句
int count = stmt.executeUpdate(sql);
// 6.处理结果
System.out.println("受影响的行数:" + count);
// 7.释放资源
stmt.close();
con.close();
2、JDBC - API
1、DriverManager
- 管理一组JDBC驱动程序的基本服务。
-
注册驱动
-
使用DriverManager注册给定的驱动程序。 新加载的驱动程序类应调用方法registerDriver以使其自身为DriverManager
-
public static void registerDriver(Driver driver) throws SQLException
-
参数
- url - 格式为 jdbc:subprotocol:subname的数据库URL
- user - 代表其建立连接的数据库用户
- password - 用户的密码
-
-
建立联系
-
尝试建立与给定数据库URL的连接。
DriverManager
尝试从已注册的JDBC驱动程序集中选择适当的驱动程序。 -
public static Connection getConnection(String url, String user, String password) throws SQLException
-
参数
- url - 格式为 jdbc:subprotocol:subname的数据库URL
- user - 代表其建立连接的数据库用户
- password - 用户的密码
-
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
-
底层源码
-
static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } }
-
-
MySQL 5 版本之后的驱动包,可以省略注册驱动的步骤
-
自动加载jar包中的 META-INF\services\java.sql.Driver 驱动
2、Connection
- 与特定数据库的连接(会话)。 执行SQL语句并在连接的上下文中返回结果。
-
获取执行SQL的对象
-
普通执行SQL对象
Statement createStatement() throws SQLException 创建一个Statement对象,用于将SQL语句发送到数据库。 不带参数的SQL语句通常使用Statement对象执行。 如果多次执行相同的SQL语句,则使用PreparedStatement对象可能更有效。
-
预编译SQL执行SQL对象:防止SQL注入
PreparedStatement prepareStatement(String sql) throws SQLException 创建一个PreparedStatement对象,用于将参数化SQL语句发送到数据库。 可以预编译带有或不带IN参数的SQL语句并将其存储在PreparedStatement对象中。 然后,可以使用此对象多次有效地执行此语句。
-
-
事务处理
-
开启事务
void setAutoCommit(boolean autoCommit); // 设置为 true 为自动提交事务; false 为手动提交事务
-
提交事务
commit();
-
回滚事务
rollback()
示例:
public static void main(String[] args) throws Exception { // 1.注册驱动 Class.forName("com.mysql.jdbc.Driver"); // 2.获取连接 String url = "jdbc:mysql://127.0.0.1:3306/db1"; String username = "root"; String password = "123456"; Connection con = DriverManager.getConnection(url, username, password); Statement stmt = null; try { // 手动提交事务 con.setAutoCommit(false); // 3.定义sql语句 String sql1 = "update emp set ename = '弼马温' where id = 1001"; String sql2 = "update emp set ename = '卢本伟' where id = 1002"; // 4.获取执行sql对象 stmt = con.createStatement(); // 5.执行sql int count1 = stmt.executeUpdate(sql1); // 制造一个异常 int num = 3 / 0; int count2 = stmt.executeUpdate(sql2); // 6.处理结果 System.out.println("收到影响的行数为:" + count1); System.out.println("收到影响的行数为:" + count2); // 提交事务 con.commit(); } catch (SQLException e) { // 回滚事务 con.rollback(); throw new RuntimeException(e); } // 7.释放资源 stmt.close(); con.close(); }
-
3、Statement
-
Statement的作用“
- 执行SQL语句
-
执行SQL语句
-
int executeUpdate(String sql) 返回值: 1. DML语句影响的行数 2. DDL语句执行后,执行成功也可能返回0
-
ResultSet executeQuery(String sql) 1. 执行DQL语句 返回值: ResultSet 结果集对象
-
4、ResultSet
-
封装了DOL查询语句的结果
ResultSet executeQuery(String sql); // 执行DQL语句,返回ResultSet对象
-
获取查询结果
boolean next() 1. 先将光标从当前位置向前移动一行 2. 判断当前行是否为有效行 true : 有效行,当前行有数据 false : 无效行,当前行没有数据
xxx getXxx(参数) ; // 获取数据 参数 : 第一种类型 int : 列的编号,从 1 开水 第二种类型 String : 列的名称
-
示例
public static void main(String[] args) throws Exception { // 1.注册驱动 Class.forName("com.mysql.jdbc.Driver"); // 2.获取连接 String url = "jdbc:mysql://127.0.0.1:3306/db1"; String username = "root"; String password = "123456"; Connection conn = DriverManager.getConnection(url, username, password); // 3.获取执行SQL对象 Statement statement = conn.createStatement(); // 4.定义SQL语句 String sql = "select * from dept;"; // 5.执行SQL ResultSet resultSet = statement.executeQuery(sql); // 6.处理结果 while (resultSet.next()){ int id = resultSet.getInt("id"); String dname = resultSet.getString("dname"); String loc = resultSet.getString("loc"); System.out.println(id + "..." + dname + "..." + loc); System.out.println(); } // 7.释放资源 resultSet.close(); statement.close(); conn.close(); }
5、PreparedStatement-SQL注入
- SQL注入是通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法。
// 4.定义SQL语句
String name = "admin";
// String pwd = "123456";
String pwd = "1' or '1 = 1";
String sql = "select * from account where username = '"+name+"' and password = '"+pwd+"' ";
System.out.println(sql);
// select * from account where username = 'admin' and password = '1' or '1 = 1'
1. username = 'admin' and password = '1' 条件不成立
2. '1 = 1' 条件恒成立
导致了,用户输入特殊的字符,改变了事先定义好的SQL语句。
// 5.执行SQL
ResultSet resultSet = statement.executeQuery(sql);
// 6.处理结果
if (resultSet.next()){
System.out.println("登录成功!");
}else {
System.out.println("登录失败!");
}
6、PreparedStatement
- PreparedStatement的作用:
- 预编译SQL并执行SQL语句 ,可以防止SQL注入的安全问题
-
获取PreparedStatement对
1. SQL语句中的参数值,使用 ? 占位符替代 String sql = "select * from emp where username = ? and password = ? "; 2. 获取PreparedStatement对象 PreparedStatement pst = conn.prepareStatement(sql);
-
设置参数
PreparedStatement 对象.setXxx(参数列表) ; 给 ? 占位符赋值 参数 参数1 : ? 的位置编号,从 1 卡=开始 参数2 : ? 的实际值
-
执行SQL
executeQuery() 或者 executeUpdate()
示例 preparedStatement对象底层会判断,会把 ’ 单引号进行转义 ’ 也能识别SQL关键字
// 3.定义SQL语句
String sql = "select * from account where username = ? and password = ?";
// 4.获取PreparedStatement对象,预编译SQL语句
PreparedStatement preparedStatement = con.prepareStatement(sql);
// 5.设置参数
preparedStatement.setString(1,"admin");
preparedStatement.setString(2,"1' or '1 = 1");
// 6.执行SQL
ResultSet resultSet = preparedStatement.executeQuery();
// 7.处理结果
if (resultSet.next()){
System.out.println("登录成功!");
}else {
System.out.println("登录失败!");
}
-
PreparedStatement的原理
- 在获取PreparedStatement对象时,将sql语句发送给mysql服务器进行检查,编译【很耗时】
- 执行sql时就不用再进行这些步骤,提高效率
- 如果sql模板一样,则只需要进行一次检查,编译
-
需要手动开启预编译功能 : useServerPrepStmts = true
7、配置MySQL执行日志
- 在my.ini配置文件中添加
log-output = FILE
general-log = 1
general_log_file = "D:\mySQL\daily_record\mysql.log"
slow-query-log = 1
slow_query_log_file = "D:\mySQL\daily_record\mysql_slow.log"
long_query_time = 2
3、数据库连接池
3.1、简介
-
数据库连接池是一个容器,负责分配,管理数据库连接(Connection)
-
应用程序可以重复使用一个现有的数据库连接,而不是再重新建立一个
-
释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
-
好处
- 资源复用
- 提升系统响应速度
- 避免数据库连接遗漏
-
数据库连接池的本质是什么?
- 连接池的本质就是一个装有Connection对象的集合。
3.2、Druid使用
-
Druid(德鲁伊)
- Druid连接池是阿里巴巴开源的数据库连接池项目。
- 功能强大,性能优秀,是Java语言最好的数据库连接池之一。
-
使用步骤
-
创建DruidDataSource对象
DruidDataSource dataSource = new DruidDataSource();
-
设置连接参数
// 1.驱动 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); // 2.地址 + 数据库名 dataSource.setUrl("jdbc:mysql://localhost:3306/brand_demo"); // 3.用户名 dataSource.setUsername("root"); // 4.密码 dataSource.setPassword("123456");
-
获取连接
// 5.从连接池中获取连接对象 DruidPooledConnection conn = dataSource.getConnection();
-
其他处理类似
// 6.定义SQL语句 String sql = "select * from tb_brand"; // 7.获取执行SQL对象来执行SQL PreparedStatement preparedStatement = conn.prepareStatement(sql); // 8.执行SQL ResultSet rs = preparedStatement.executeQuery(); // 9.处理结果 while (rs.next()){ int id = rs.getInt("id"); String brandName = rs.getString("brand_name"); String companyName = rs.getString("company_name"); int ordered = rs.getInt("ordered"); String description = rs.getString("description"); int status = rs.getInt("status"); System.out.println(id + ", " +brandName+ ", " +companyName+ ", " +ordered+ ", " +description+ ", " +status); } // 10.释放资源 rs.close(); preparedStatement.close(); // 11.归还连接对象,手动调用close方法,并不是释放资源 conn.close();
-
归还连接对象
conn.close();
-
3.3、API
1.获取连接池的最大连接数【默认是8个】 dataSource.getMaxActive() 2.设置连接池的最大连接数 dataSource.setMaxActive(int num); 3.获取连接池连接的最大等待时间【当连接Connection对象不够用的时候,默认一直等待】 dataSource.getMaxWait() 4.设置连接池Connection对象的最大等待时间【单位/毫秒值】 dataSource.setMaxWait(long time);
3.4、优化
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true
username=root
password=1234
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000
public static void main(String[] args) throws Exception {
InputStream in = new FileInputStream(new File("D:\\code\\javaWeb\\day03-jdbc\\durid.properties"));
Properties pro = new Properties();
pro.load(in);
DataSource dataSource = DruidDataSourceFactory.createDataSource(pro);
Connection conn = dataSource.getConnection();
// 3.获取PrepareStatement执行SQL对象
PreparedStatement preparedStatement = conn.prepareStatement("select * from account");
// 4.设置参数,
// 5.执行sql
ResultSet rs = preparedStatement.executeQuery();
// 6.处理结果
while (rs.next()){
System.out.println(rs.getInt("id") + "..." + rs.getDouble("money") + "..." + rs.getString("username") );
}
// 7.释放资源
rs.close();
preparedStatement.close();
conn.close();
}
4、JDBC练习
4.1、环境准备
create database if not exists brand_demo;
use brand_demo;
-- 删除tb_brand表
drop table if exists tb_brand;
-- 创建tb_brand表
create table tb_brand
(
-- id 主键
id int primary key auto_increment,
-- 品牌名称
brand_name varchar(20),
-- 企业名称
company_name varchar(20),
-- 排序字段
ordered int,
-- 描述信息
description varchar(100),
-- 状态:0:禁用 1:启用
status int
);
-- 添加数据
insert into tb_brand (brand_name, company_name, ordered, description, status)
values ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1);
SELECT * FROM tb_brand;
4.2、查询所有
public static List<Brand> selectAll() throws Exception {
//1 注册驱动,获取连接对象 Connection
//2 定义SQL:select * from tb_brand;
//3 获取 PreparedStatement对象,传递SQL并且预编译SQL
//4 设置参数:
//5 执行SQL,不需要传递SQL
//6 处理结果:封装成List<Brand>
Brand brand = null;
ArrayList<Brand> list = new ArrayList<>();
while (rs.next()){
int id = rs.getInt("id");
String brandName = rs.getString("brand_name");
String companyName = rs.getString("company_name");
int ordered = rs.getInt("ordered");
String description = rs.getString("description");
int status = rs.getInt("status");
// 封装数据
brand = new Brand(id,brandName,companyName,ordered,description,status);
list.add(brand);
}
//7 释放资源,返回结果
return list;
}
4.3、添加
public static boolean add(Brand brand) throws Exception {
//1 注册驱动,获取连接对象 Connection
//2 定义SQL
String sql="insert into tb_brand(brand_name,company_name,ordered,description,status)values (?,?,?,?,?)" ;
//3 获取 PreparedStatement对象,传递SQL并且预编译SQL
//4 设置参数:
ps.setString(1,brand.getBrandName());
ps.setString(2,brand.getCompanyName());
ps.setInt(3,brand.getOrdered());
ps.setString(4,brand.getDescription());
ps.setInt(5,brand.getStatus());
//5 执行SQL,不需要传递SQL
int count = ps.executeUpdate();
//6 释放资源,返回结果
return count > 0;
}
4.4、修改
public static boolean update(Brand brand) throws Exception {
//1 注册驱动,获取连接对象 Connection
//2 定义SQL
String sql="update tb_brand set brand_name = ? where id = ?";
//3 获取 PreparedStatement对象,传递SQL并且预编译SQL
//4 设置参数:
ps.setString(1,brand.getBrandName());
ps.setInt(2,brand.getId());
//5 执行SQL,不需要传递SQL
int count = ps.executeUpdate();
//6 释放资源,返回结果
return count > 0;
}
4.5、删除
public static boolean delete(int id) throws Exception {
//1 注册驱动,获取连接对象 Connection
//2 定义SQL
String sql="delete from tb_brand where id =?";
//3 获取 PreparedStatement对象,传递SQL并且预编译SQL
//4 设置参数:
ps.setInt(1,id);
//5 执行SQL,不需要传递SQL
//6 释放资源,返回结果
return count>0;
}
day04、mybatis
1、简介
-
什么是MyBatis?
- mybatis是一个持久层的框架,用于简化JDBC
- mybatis本是 Apache 的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github
-
持久层
- 持久层是将数据保存到数据库的,对数据库的信息进行CRUD操作
- (三层架构:表现层,业务层,持久层)
-
JDBC 和 MyBatis框架 对比
---- JDBC 1.注册驱动 Class.forName(); 2.获取连接 Connection conn = DriverManager.getConnection(); 3.定义SQL语句 String sql = ""; 4.获取PreparedStatement对象,预编译SQL语句 PrepareStatement pstmt = conn.prepareStatement(sql); 5.设置参数值 pstmt.setString(1 ,"张三"); 5.执行SQL语句 int count = pstmt.executeUpdate(); 6.处理结果 ...... 7.释放资源 pstmt.close(); conn.close(); ---- MyBatis 1. 加载核心配置文件,获取 SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 2. 获取 SqlSession 对象 SqlSession sqlSession = sqlSessionFactory.openSession(); 3. 获取Mapper接口代理对象。 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 4. 执行方法,获取结果 List<User> list = userMapper.selectAll(); 5. 处理结果 .... 6. 释放资源 sqlSession.close();
-
JDBC
-
硬编码
-
注册驱动、获取连接
上图标1的代码有很多字符串,而这些是连接数据库的四个基本信息,以后如果要将Mysql数据库换成其他的关系型数据库的话,这四个地方都需要修改,如果放在此处就意味着要修改我们的源代码。
-
SQL语句
如果表结构发生变化,SQL语句就要进行更改。这也不方便后期的维护。
-
-
操作繁琐
- 手动设置参数
- 手动封装结果集
-
-
Mybatis
- 硬编码可以配置到 -------------------> 配置文件
- 操作繁琐的地方mybatis,以及结果集的封装 -------------------------> 代理对象自动完成
- MyBatis 几乎免除了所有的 JDBC 代码以及设置参数或获取结果集的工资。
- MyBatis 框架就是用来简化 JDBC 的,底层还是JDBC实现。解决了原生jdbc硬编码和手动封装结果集的问题。
-
2、快速入门
-
环境搭建
-
数据库数据准备
create database if not exists mybatis; use mybatis; drop table if exists tb_user; create table tb_user( id int primary key auto_increment, username varchar(20), password varchar(20), gender char(1), addr varchar(30) ); INSERT INTO tb_user VALUES (1, 'zhangsan', '123', '男', '北京'); INSERT INTO tb_user VALUES (2, '李四', '234', '女', '天津'); INSERT INTO tb_user VALUES (3, '王五', '11', '男', '西安');
-
准备与数据库表中对应的实体类
public class User { private Integer id; private String username; private String password; private String gender; private String addr; }
-
-
核心配置文件
-
pom.xml中配置依赖环境
<!--mybatis 依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!--mysql 驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <!--junit 单元测试--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
-
配置MyBatis的核心配置文件 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <!--配置连接数据库的连接参数--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!--2 加载SQL语句,将给的例子注释掉。后面第三大步中配置--> <mappers> <!--<mapper resource="org/mybatis/example/BlogMapper.xml"/>--> <package name="com.itheima.mapper"/> </mappers> </configuration>
-
-
编写mapper接口和SQL语句
-
mapper接口, 编写 UserMapper接口
public interface UserMapper2 { // 使用注解 查询所有用户的信息 @Select("select * from tb_user") public ArrayList<User> selectAll(); }
-
-
单元测试
-
在test包下编写 UserTest类,进行单元测试
public class UserTest { // 测试,查询所有用户的信息 @Test public void testSelectAll() throws IOException { // 1. 加载核心配置文件,获取 SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 2. 获取 SqlSession 对象 org.apache.ibatis.session.SqlSession sqlSession = sqlSessionFactory.openSession(); // 3. 获取Mapper接口代理对象。 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 4.使用接口代理对象调用方法 ArrayList<User> list = userMapper.selectAll(); // 5.打印结果 System.out.println(list); // 6.释放资源 sqlSession.close(); } }
-
3、配置文件
-
MyBatis的核心配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!-- 标签 : configuratio 配置 配置文件的根标签,根标签只能有一个 --> <configuration> <!-- 标签 :typeAliases 用于给javabeen实体类起别名,在xml映射SQL语句,参数和返回值类型不用写全类名,直接写别名 --> <typeAliases> <!-- 标签 :typeAlias : 给具体的实体类起别名,但是当实体类很多,【不建议使用】 属性 : type : javabeen的全类名 alias : 使用别名代替全类名 --> <typeAlias type="com.itheima.pojo.User" alias="user"/> <!-- 标签 :package :包 属性 :name :指定到包名,该包下的所有实体类,都可以使用类名,作用等同于全类名。【推荐】 --> <package name="com.itheima.pojo"/> </typeAliases> <!-- 标签 :environments 环境配置,用于配置多种开发环境,可以配置多个环境 属性 :default default= "development" :表示使用下面id="development"的环境配置 default= "abc" : 表示使用下面id="abc"的环境配置 -- 根据值的不同,使用不同的环境,即切换数据库的链接。 --> <environments default="development"> <!-- 标签 :environment :定义连接数据库的环境,这个标签可以出现多次 属性 :id 表示该配置的唯一标识。它的父标签的 default 属性,可以选择使用当前 id 属性的属性值 --> <environment id="development"> <!-- 标签 :transactionManager 事务管理器 属性 :type 事务的管理方式 属性值 : JDBC :开启事务管理,推荐使用 MANAGED :不使用事务管理 --> <transactionManager type="JDBC"/> <!-- 标签 :dataSource 数据连接池 属性 :type 是否使用连接池 属性值 : POOLED 使用数据库连接池 UNPOOLED 不使用数据库连接池 --> <dataSource type="POOLED"> <!-- 标签 :property 数据库连接池的属性配置 属性 :name driver 数据库驱动 属性值 :value com.mysql.jdbc.Driver 驱动类 --> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!-- 标签 :mappers 加载映Mapper接口和SQL语句 --> <mappers> <!-- 标签 :mapper 加载 mapper接口文件,匹配接口中的方法和映射文件中的SQL语句 -- mapper包下有多少个接口,就得写多少个 mapper 标签,这很费人力,【不推荐】 --> <!-- <mapper class="com.itheima.mapper.UserMapper"/> --> <!-- 标签 :package 加载该包下所有Mapper接口中的SQL语句并存储,后期调用Mapper中方法时会执行对应SQL【推荐】 --> <package name="com.itheima.mapper"/> </mappers> </configuration>
4、练习
-
在快速入门的基础上,使用注解进行增删改查
-
抽取一个方法,返回 sqlSession 对象
public SqlSession getSqlSession(){ SqlSession sqlSession = null; try { //1 加载核心配置文件,获取 SqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); //2 获取 SqlSession 对象 sqlSession = sqlSessionFactory.openSession(); } catch (IOException e) { throw new RuntimeException(e); } return sqlSession; }
-
根据用户的 id 查询用户
- 接口
/** * 根据 id 查询用户 * @param id * @return 返回一个用户对象 */ @Select("select * from tb_user where id = #{id}") User selectById(int id);
- 测试
@Test public void testSelectById(){ // 1.获取SqlSession会话对象 SqlSession sqlSession = getSqlSession(); // 2.获取UserMapper接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 3.使用代理对象调用方法 User user = userMapper.selectById(2); // 4.打印结果 System.out.println(user); // 5.释放资源 sqlSession.close(); }
-
添加一个用户
- 接口
/** * 添加一个用户 * @param user 实体类 * @return true / false */ @Insert("insert into tb_user (username,password,gender,addr) values (#{username},#{password},#{gender},#{addr})") boolean addUser(User user);
- 测试
@Test public void testAddUser(){ // 1.获取SqlSession会话对象 SqlSession sqlSession = getSqlSession(); // 2.获取UserMapper接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 3.创建一个User对象 User user = new User("诸葛亮","78963","男","躬耕南阳诸葛庐"); // 4.使用代理对象调用方法 boolean flag = userMapper.addUser(user); // 5.手动提交事务 sqlSession.commit(); // 6.打印回执 System.out.println(flag? "添加成功" : "添加失败"); // 7.释放资源 sqlSession.close(); }
-
根据 id 修改用户
- 接口
/** * 根据ID修改用户 * @param user * @return */ @Update("update tb_user set " + "username = #{username}," + "password = #{password}," + "gender = #{gender}," + "addr = #{addr}" + "where id = #{id}") boolean updateUserById(User user);
- 测试
@Test public void testUpdateUserById(){ // 1.获取SqlSession会话对象 SqlSession sqlSession = getSqlSession(); // 2.获取UserMapper接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 3.创建一个User对象 User user = new User("张三","78963","男","法外狂徒"); // 4.设置对象的 id 值,进行修改 user.setId(1); // 5.代理对象调用方法 boolean flag = userMapper.updateUserById(user); // 6.提交事务 sqlSession.commit(); // 7.打印回执 System.out.println(flag? "修改成功" : "修改失败"); // 8.释放资源 sqlSession.close(); }
-
根据 id 删除用户
- 接口
/** * 根据id删除用户 * @param id * @return */ @Delete("delete from tb_user where id = #{id}") boolean deleteUserById(int id);
- 测试
@Test public void testDeleteUserById(){ // 1.获取SqlSession会话对象 SqlSession sqlSession = getSqlSession(); // 2.获取UserMapper接口的代理对象 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // 3.代理对象调用方法,删除 ID为3的用户 boolean flag = userMapper.deleteUserById(3); // 4.提交事务 sqlSession.commit(); // 5.打印回执 System.out.println(flag? "修改成功" : "修改失败"); // 6.释放资源 sqlSession.close(); }
-
5、XML方式配置SQL语句
-
XML映射sql语句配置
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 标签 :mapper表示SQL语句映射关系 属性 :namespace="com.itheima.mapper.UserMapper :名称空间,用于区分每个映射配置文件,和mapper接口的全类名一致。 --> <mapper namespace="com.itheima.mapper.UserMapper"> </mapper>
-
在练习的基础上,测试类不变,修改UserMapper接口,添加UserMapper.xml配置
-
根据 id 查询一个用户信息
-
接口
User selectById(int id);
-
UserMapper.xml
<!--根据id 查询一个用户信息--> <select id="selectById" resultType="com.itheima.pojo.User"> select * from tb_user where id = #{id} </select>
-
-
添加一个用户
-
接口
boolean addUser(User user);
-
UserMapper.xml
<!--添加一个用户--> <insert id="addUser" parameterType="com.itheima.pojo.User"> insert into tb_user (username,password,gender,addr) values (#{username},#{password},#{gender},#{addr}); </insert>
-
-
根据 id 修改一个用户信息
-
接口
boolean updateUserById(User user);
-
UserMapper.xml
<!--根据id修改用户--> <update id="updateUserById" parameterType="com.itheima.pojo.User"> update tb_user set username = #{username}, password = #{password}, gender = #{gender}, addr = #{addr} where id = #{id}; </update>
-
-
根据 id 删除一个用户信息
-
接口
boolean deleteUserById(int id);
-
UserMapper.xml
<!--根据 id 删除用户信息--> <delete id="deleteUserById" parameterType="int"> delete from tb_user where id = #{id}; </delete>
-
-
6、注意事项
1. #{}:执行SQL时,会将#{}占位符替换为?,将来自动设置参数值。
2. openSession():默认开启事务,进行增删改操作后需要使用 sqlSession.commit(); 手动提交事务
openSession(true):可以设置为自动提交事务(关闭事务)
3. 映射配置文件的名称和Mapper接口名名称一致,并且将SQL映射文件和Mapper接口放置在同一目录下
4. 设置SQL映射文件的namespace属性为Mapper接口全限定名
5. 映射文件中sql语句的id要和mapper接口的方法名一致,并保持参数类型和返回值类型一致
6. 映射配置文件和Mapper接口中定义的抽象方法不能有重载 方法名就是SQL的唯一标识id,不能重复
7. parameterType:表示参数类型,可以省略不写;
8. resultType:表示查询的每一行数据要封装的数据类型,引用类型要写全类名,只有<select>标签才有此属性。
day05、mybatis(二)
1、属性名与字段名问题
1.1、问题引入
-
当 Javabeen 的属性名和数据库的字段名不一致,进行查询
-
查询结果实体类中,不一致的属性值,mybatis没有进行赋值,所有默认值是 null
1.2、3种解决方式
1.2.1、为字段起别名
-
在查询SQL语句中,为字段起别名
<!-- 查询所有 --> <select id="selectAll" resultType="com.itheima.pojo.Brand"> select id,brand_name as brandName,company_name as companyName, ordered,description,status from tb_brand </select>
1.2.2、使用resultMap映射
-
在Select 查询,使用resultMap把结果集,进行手动映射, 在映射配置文件中使用resultMap定义 字段 和 属性 的映射关系
<!-- 查询所有 --> <select id="selectAll" resultMap="bMap"> select * from tb_brand </select> <resultMap id="bMap" type="com.itheima.pojo.Brand"> <result column="brand_name" property="brandName"/> <result column="company_name" property="companyName"/> </resultMap> <!-- -------------------------------------------------------------------------------------------- 标签 : resultMap 一般使用在多表查询,进行结果集的映射处理,也可以映射字段和属性的关系 子标签 : <id column="id" property="id"/> 用于映射主键 id:完成主键字段的映射 column:表的列名 property:实体类的属性名 子标签 : <result column="company_name" property="companyName"/> result:完成一般字段的映射 column:表的列名 property:实体类的属性名 -->
1.2.3、开启驼峰命名自动映射
-
在mybatis的核心配置文件中,添加设置
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!-- mapUnderscoreToCamelCase : 默认值 false 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名aColumn 的类似映射。 数据库表中的字段 -----> 自动转为java中的驼峰规则命名 brand_name -----> brandName company_name -----> companyName -->
2、占位符 #{} 和 ${}
2.1、使用 # { } 占位符
-
<select id="selectById" resultType="com.itheima.pojo.Brand"> select * from tb_brand where id = #{id}; </select>
-
效果
-
底层是 PreparedStatement实现,会在SQL预编译的时候,使用 ? 当占位符,再进行设置实际的值
能够防止SQL注入的安全问题。
2.2、使用 $ { } 做占位符
-
<select id="selectById" resultType="com.itheima.pojo.Brand"> select * from tb_brand where id = ${id}; </select>
-
效果
-
底层是 Statement 实现,没有预编译处理,直接把参数进行拼接,会有SQL注入的安全问题。
-
使用场景
- 如果要对表名、列名进行动态设置,只能使用${}进行sql拼接。
3、特殊字符处理
-
在 xml 文件中 < 和 > 含有特殊的含义,不能在SQL语句中直接使用
-
使用 < 代替 小于号 “ < ” , 使用 > 代替大于号 " > "
<select id="selectById" resultType="com.itheima.pojo.Brand"> select * from tb_brand where id < ${id}; </select>
-
使用xml文件标签中特有的标签 <![CDATA[内容]]> ,里面的内容会原本的输出
<select id="selectById" resultType="com.itheima.pojo.Brand"> select * from tb_brand where id <![CDATA[<]]> #{id}; </select>
4、添加-主键返回
- 在数据添加成功后,有时候需要获取插入数据库数据的主键(主键是自增长)。
- 指定获取数据库的主键,封装到 实体类的的 id 成员变量中
-
在映射xml文件中配置
-
<insert id="add" useGeneratedKeys="true" keyProperty="id"> insert into tb_brand (id,brand_name,company_name,ordered,description,status) values (null,#{brandName},#{companyName},#{ordered},#{description},#{status}) </insert> <!-- useGeneratedKeys : 够获取自动增长的主键值。true表示获取 keyProperty : 指定将获取到的主键值封装到哪儿个属性里 -->
-
-
在mapper接口中使用注解
-
@Options(useGeneratedKeys = true,keyProperty = "id") @Insert(".........") int add(Brand brand);
-
5、动态SQL
5.1 多条件查询
我们经常会遇到如上图所示的多条件查询,将多条件查询的结果展示在下方的数据列表中。而我们做这个功能需要分析最终的SQL语句应该是什么样,思考两个问题
- 条件表达式
- 如何连接
条件字段 企业名称
和 品牌名称
需要进行模糊查询,所以条件应该是:
简单的分析后,我们来看功能实现的步骤:
-
编写接口方法
- 参数:所有查询条件
- 结果:List
-
在映射配置文件中编写SQL语句
-
编写测试方法并执行
5.1.1 编写接口方法
在 BrandMapper
接口中定义多条件查询的方法。
而该功能有三个参数,我们就需要考虑定义接口时,参数应该如何定义。Mybatis针对多参数有多种实现
-
使用
@Param("参数名称")
标记每一个参数,在映射配置文件中就需要使用#{参数名称}
进行占位List<Brand> selectByCondition(@Param("status") int status, @Param("companyName") String companyName,@Param("brandName") String brandName);
-
将多个参数封装成一个 实体对象 ,将该实体对象作为接口的方法参数。该方式要求在映射配置文件的SQL中使用
#{内容}
时,里面的内容必须和实体类属性名保持一致。List<Brand> selectByCondition(Brand brand);
-
将多个参数封装到map集合中,将map集合作为接口的方法参数。该方式要求在映射配置文件的SQL中使用
#{内容}
时,里面的内容必须和map集合中键的名称一致。List<Brand> selectByCondition(Map map);
5.1.2 编写SQL语句
在 BrandMapper.xml
映射配置文件中编写 statement
,使用 resultMap
而不是使用 resultType
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
where status = #{status}
and company_name like concat('%',#{companyName},'%')
and brand_name like concat('%',#{brandName},'%')
</select>
5.1.3 编写测试方法
在 test/java
下的 com.itheima.mapper
包下的 MybatisTest类中
定义测试方法
@Test
public void testSelectByCondition() throws IOException {
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
//方式一 :接口方法参数使用 @Param 方式调用的方法
//List<Brand> brands = brandMapper.selectByCondition(1, "华为", "华为");
//方式二 :接口方法参数是 实体类对象 方式调用的方法
//封装对象
/* Brand brand = new Brand();
brand.setStatus(1);
brand.setCompanyName("华为");
brand.setBrandName("华为");*/
//List<Brand> brands = brandMapper.selectByCondition(brand);
//方式三 :接口方法参数是 map集合对象 方式调用的方法
Map map = new HashMap();
map.put("status" , 1);
map.put("companyName", "华为");
map.put("brandName" , "华为");
List<Brand> brands = brandMapper.selectByCondition(map);
System.out.println(brands);
//5. 释放资源
sqlSession.close();
}
5.1.4 动态SQL
上述功能实现存在很大的问题。用户在输入条件时,肯定不会所有的条件都填写,这个时候我们的SQL语句就不能那样写的
例如用户只输入 当前状态 时,SQL语句就是
select * from tb_brand where status = #{status}
而用户如果只输入企业名称时,SQL语句就是
select * from tb_brand where company_name like #{companName}
而用户如果输入了 当前状态
和 企业名称
时,SQL语句又不一样
select * from tb_brand where status = #{status} and company_name like #{companName}
针对上述的需要,Mybatis对动态SQL有很强大的支撑:
if
choose (when, otherwise)
trim (where, set)
foreach
我们先学习 if 标签和 where 标签:
-
if 标签:条件判断
- test 属性:逻辑表达式
<select id="selectByCondition" resultMap="brandResultMap"> select * from tb_brand where <if test="status != null"> and status = #{status} </if> <if test="companyName != null and companyName != '' "> and company_name like concat('%',#{companyName},'%') </if> <if test="brandName != null and brandName != '' "> and brand_name like concat('%',#{brandName},'%') </if> </select>
如上的这种SQL语句就会根据传递的参数值进行动态的拼接。如果此时status和companyName有值那么就会值拼接这两个条件。
执行结果如下:
但是它也存在问题,如果此时给的参数值是
Map map = new HashMap(); // map.put("status" , 1); map.put("companyName", "华为"); map.put("brandName" , "华为");
拼接的SQL语句就变成了
select * from tb_brand where and company_name like ? and brand_name like ?
而上面的语句中 where 关键后直接跟 and 关键字,这就是一条错误的SQL语句。这个就可以使用 where 标签解决
-
where 标签
- 作用:
- 替换where关键字
- 会动态的去掉第一个条件前的 and
- 如果所有的参数没有值则不加where关键字
- 作用:
<select id="selectByCondition" resultMap="brandResultMap">
select *
from tb_brand
<where>
<if test="status != null">
and status = #{status}
</if>
<if test="companyName != null and companyName != '' ">
and company_name like concat('%',#{companyName},'%')
</if>
<if test="brandName != null and brandName != '' ">
and brand_name like concat('%',#{brandName},'%')
</if>
</where>
</select>
注意:需要给每个条件前都加上 and 关键字。
如果用注解使用的话对应的代码如下:在SQL的外面定义一组
/**
* 多条件查询-动态SQL
* @return
*/
@Select("<script>select * from tb_brand\n" +
" <where>\n" +
" <if test=\"status !=null\">\n" +
" status=#{status}\n" +
" </if>\n" +
" <if test=\"companyName!=null and companyName!=''\">\n" +
" and company_name like concat('%',#{companyName},'%')\n" +
" </if>\n" +
" <if test=\"brandName!=null and brandName!=''\">\n" +
" and brand_name like concat('%',#{brandName},'%')\n" +
" </if>\n" +
" </where></script>")
List<Brand> selectByCondition(Brand brand);
上述代码的可读性大幅度降低,所以对于复杂SQL建议大家还是在映射配置文件中定义。
5.2 单个条件(动态SQL)
如上图所示,在查询时只能选择 品牌名称
、当前状态
、企业名称
这三个条件中的一个,但是用户到底选择哪儿一个,我们并不能确定。这种就属于单个条件的动态SQL语句。
这种需求需要使用到 choose(when,otherwise)标签
实现, 而 choose
标签类似于Java 中的switch语句。
通过一个案例来使用这些标签
5.2.1 编写接口方法
在 BrandMapper
接口中定义单条件查询的方法。
/**
* 单条件动态查询
* @param brand
* @return
*/
List<Brand> selectByConditionSingle(Brand brand);
5.2.2 编写SQL语句
在 BrandMapper.xml
映射配置文件中编写 statement
,使用 resultMap
而不是使用 resultType
<select id="selectByConditionSingle" resultMap="brandResultMap">
select *
from tb_brand
<where>
<choose><!--相当于switch-->
<when test="status != null"><!--相当于case-->
status = #{status}
</when>
<when test="companyName != null and companyName != '' "><!--相当于case-->
company_name like concat('%',#{companyName},'%')
</when>
<when test="brandName != null and brandName != ''"><!--相当于case-->
brand_name like concat('%',#{brandName},'%')
</when>
</choose>
</where>
</select>
5.2.3 编写测试方法
在 test/java
下的 com.itheima.mapper
包下的 MybatisTest类中
定义测试方法
@Test
public void testSelectByConditionSingle() throws IOException {
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//封装参数对象
Brand brand = new Brand();
//brand.setStatus(1);
brand.setCompanyName("华为");
//brand.setBrandName("华为");
//4. 执行方法
List<Brand> brands = brandMapper.selectByConditionSingle(brand);
System.out.println(brands);
//5. 释放资源
sqlSession.close();
}
执行测试方法
结果如下:
5.3 动态修改部分定字段
如图所示是修改页面,用户在该页面书写需要修改的数据,点击 提交
按钮,就会将数据库中对应的数据进行修改。注意一点,如果哪儿个输入框没有输入内容,我们是将表中数据对应字段值替换为空白还是保留字段之前的值?答案肯定是保留之前的数据。
接下来我们就具体来实现
5.3.1 编写接口方法
在 BrandMapper
接口中定义修改方法。
/**
* 修改
*/
void updatePart(Brand brand);
上述方法参数 Brand 就是封装了需要修改的数据,而id肯定是有数据的,这也是和添加方法的区别。
5.3.2 编写SQL语句
在 BrandMapper.xml
映射配置文件中编写修改数据的 statement
。
<update id="updatePart">
update tb_brand
<set>
<if test="brandName != null and brandName != ''">
brand_name = #{brandName},
</if>
<if test="companyName != null and companyName != ''">
company_name = #{companyName},
</if>
<if test="ordered != null">
ordered = #{ordered},
</if>
<if test="description != null and description != ''">
description = #{description},
</if>
<if test="status != null">
status = #{status}
</if>
</set>
where id = #{id};
</update>
set 标签可以用于动态包含需要更新的列,忽略其它不更新的列。
5.3.3 编写测试方法
在 test/java
下的 com.itheima.mapper
包下的 MybatisTest类中
定义测试方法
/**
* 修改部分字段-动态SQL
*/
@Test
public void testUpdatePart() throws IOException {
//1 加载mybatis-config.xml核心配置文件,获取SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2 获取SqlSession对象,参数true表示要自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3 通过SqlSession获取BrandMapper对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4 调用方法
Brand brand=new Brand();
brand.setId(13);
brand.setBrandName("海澜之家666");
brand.setCompanyName("海澜之家");
//brand.setOrdered(100);
//brand.setDescription("男人的衣柜");
//brand.setStatus(0);
brandMapper.updatePart(brand);
//5 处理结果,释放资源
//提交事务
//sqlSession.commit();
sqlSession.close();
}
执行测试方法结果如下:
从结果中SQL语句可以看出,只修改了 status
字段值,因为我们给的数据中只给Brand实体对象的 status
属性设置值了。这就是 set
标签的作用。
5.4 批量删除
如上图所示,用户可以选择多条数据,然后点击上面的 删除
按钮,就会删除数据库中对应的多行数据。
5.4.1 编写接口方法
在 BrandMapper
接口中定义删除多行数据的方法。
/**
* 批量删除
*/
void deleteByIds(int[] ids);
参数是一个数组,数组中存储的是多条数据的id
5.4.2 编写SQL语句
在 BrandMapper.xml
映射配置文件中编写删除多条数据的 statement
。
编写SQL时需要遍历数组来拼接SQL语句。Mybatis 提供了 foreach
标签供我们使用
foreach 标签
用来迭代任何可迭代的对象(如数组,集合)。
- collection 属性:
- mybatis会将数组参数,封装为一个Map集合。
- 默认:array = 数组
- 使用@Param注解改变map集合的默认key的名称
- mybatis会将数组参数,封装为一个Map集合。
- item 属性:本次迭代获取到的元素。
- separator 属性:集合项迭代之间的分隔符。
foreach
标签不会错误地添加多余的分隔符。也就是最后一次迭代不会加分隔符。 - open 属性:该属性值是在拼接SQL语句之前拼接的语句,只会拼接一次
- close 属性:该属性值是在拼接SQL语句拼接后拼接的语句,只会拼接一次
<delete id="deleteByIds">
delete from tb_brand where id in
<foreach collection="array" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
假如数组中的id数据是{1,2,3},那么拼接后的sql语句就是:
delete from tb_brand where id in (1,2,3);
5.4.3 编写测试方法
在 test/java
下的 com.itheima.mapper
包下的 MybatisTest类中
定义测试方法
@Test
public void testDeleteByIds() throws IOException {
//1. 获取SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2. 获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//SqlSession sqlSession = sqlSessionFactory.openSession(true);
//3. 获取Mapper接口的代理对象
BrandMapper brandMapper = sqlSession.getMapper(BrandMapper.class);
//4. 执行方法
int[] ids = {5,7,8};
brandMapper.deleteByIds(ids);
//提交事务
sqlSession.commit();
//5. 释放资源
sqlSession.close();
}
大家能够发现<foreach collection="array" item="id" separator="," open="(" close=")">
中的collection="array"
是mybatis内部默认针对数组参数设置的一个固定名称,我们可以使用@Param注解自己设置名称。
6、动态SQL总结
6.1、标签的总结
<where>
1. 能替换SQL语句中的关键字where
2. 会动态地去掉第一个条件前的 and
3. 如果所有<if test = "name != null and name != ''"> 判断都不成立,则不加where关键字。
<if>
1. 用于条件控制进行判断,成立则执行SQL
<if test = "name != null and name != ''"> SQL语句 </if>
<set>
1. 用于动态包含需要更新的列,忽略其它不更新的列。配合 <if> 标签使用
<set>
<if test = "name != null and name != ''">
name = #{name},
</if>
</set>
----------------------------------------------------------
<where>
<choose>
<when test = "name != null and name != ''"> SQL语句 </when>
<when test = "gender != null and gender != ''"> SQL语句 </when>
<otherwise>
SQL语句
</otherwise>
</choose>
</where>
1. 应用场景 : 多选一,单选的下拉框。【联想java中 switch 语句】
2. 当 <when> 没有一个能匹配上,默认执行 <otherwise>
----------------------------------------------------------
<foreach collection="ids" item="id" open="(" separator="," close=")">
用于遍历参数值,适用于参数值类型数组/集合类型。用来迭代任何可迭代的对象(如数组,集合)
1. collection="ids"
---- mybatis会将数组参数,封装为一个Map集合
默认 : array = 数组
使用 @Param 注解可以改变Map集合的默认key的名称
2. item="id"
本次迭代获取到的元素
3. open="("
在拼接SQL语句之前拼接的语句,只会拼接一次
4. separator=","
集合迭代项之间的分隔符
5. close=")"
在拼接SQL语句拼接之后拼接的语句,之后拼接一次
6.2、注意事项
6.2.1、SQL语句通配符
-
在进行多个条件模糊查询中,使用SQL中的 CONCAT(…)函数进行字符串的拼接
<select id="selectByCondition" resultType="com.itheima.pojo.Brand"> select * from tb_brand <where> <if test="companyName != null and companyName !=''"> and company_name like concat("%",#{companyName},"%") </if> </where> </select>
6.2.3、mapper接口中的参数
6.2.3.1、散装参数
-
mapper接口
List<Brand> selectByCondition(Integer status,String companyName,String brandName);
-
xml映射
<select id="selectByCondition" resultType="com.itheima.pojo.Brand"> select * from tb_brand <where> <if test="status != null"> status = #{status} </if> <if test="companyName != null and companyName !=''"> and company_name like concat("%",#{companyName},"%") </if> <if test="brandName != null and brandName != ''"> and brand_name like concat("%",#{brandName},"%") </if> </where> </select>
1. 如果参数是多个,mybatis底层会将这些散的参数放到集合中,名字默认是arg0/param1、arg1/param2....
List<Brand> selectByCondition(Integer status,String companyName,String brandName);
xml中应该对应对修改成
<if test="arg0 != null">
status = #{arg0}
</if>
<if test="arg1 != null and arg1 !=''">
and company_name like concat("%",#{arg1},"%")
</if>
<if test="arg2 != null and arg2 != ''">
and brand_name like concat("%",#{arg2},"%")
</if>
-------------------------- 以上当然是不推荐的做法 -----------------------------------------
-
解决方式
-
使用 @Param注解,映射指定参数,代替 arg0/param1、arg1/param2…
1. mybatis提供了一个@Param注解,专门用来给参数取名字的,将来占位符就是@Param注解的属性值 List<Brand> selectByCondition(@Param("status") Integer status, @Param("companyName") String companyName, @Param("brandName") String brandName);
<if test="status != null"> status = #{status} </if> <if test="companyName != null and companyName !=''"> and company_name like concat("%",#{companyName},"%") </if> <if test="brandName != null and brandName != ''"> and brand_name like concat("%",#{brandName},"%") </if>
-
6.2.3.2、Javabeen 对象
int add(Brand brand);
<insert id="add" useGeneratedKeys="true" keyProperty="id">
insert into tb_brand
(id,brand_name,company_name,ordered,description,status) values
(null,#{brandName},#{companyName},#{ordered},#{description},#{status})
</insert>
注意点:
1. xml映射文件中SQL语句后面的values值,必须是该对象的属性值,mybatis才能自动读取。
6.2.3.3、数组、集合
-
数组
-
接口
void deleteByIds(int[] ids);
-
xml映射
<delete id="deleteByIds"> delete from tb_brand <where> id in <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach> </where> </delete>
-
异常PersistenceException
Caused by: org.apache.ibatis.binding.BindingException: Parameter 'ids' not found. Available parameters are [array, arg0] 1. 如果传入的参数是一个数组, mybatis底层默认会有对应的两个参数 array, arg0 修改 xml <foreach collection="array" item="id" open="(" separator="," close=")"> #{id} </foreach> ------------------------------- 不推荐 ------------------------------------
-
解决方式
1. 使用 @Param注解 void deleteByIds(@Param("ids") int[] ids); ---------------------------------------------------------------------------------- <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach>
-
-
集合
-
接口
void deleteByIds(List<Integer> ids);
-
xml映射
<delete id="deleteByIds"> delete from tb_brand <where> id in <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach> </where> </delete>
-
异常PersistenceException
Cause: org.apache.ibatis.binding.BindingException: Parameter 'ids' not found. Available parameters are [arg0, collection, list] 1. 如果传入的参数是一个集合, mybatis底层默认会有对应的两个参数 arg0, collection, list 修改 xml <foreach collection="list" item="id" open="(" separator="," close=")"> #{id} </foreach> ------------------------------- 不推荐 ------------------------------------
-
解决方式
1. 使用 @Param注解 void deleteByIds(@Param("ids") List<Integer> ids); ---------------------------------------------------------------------------------- <foreach collection="ids" item="id" open="(" separator="," close=")"> #{id} </foreach>
-
-
Map集合当做参数
void add(Map map);
<if test="status != null"> status = #{status} </if> <if test="companyName != null and companyName !=''"> and company_name like concat("%",#{companyName},"%") </if> <if test="brandName != null and brandName != ''"> and brand_name like concat("%",#{brandName},"%") </if>
-
test测试
HashMap<String , Object> map = new HashMap(); map.put("status",1): map.put("companyName","小米"): map.put("brandName","红米手机"): brandMapper.add(map);
-
注意点:map集合的键必须是SQL后面拼接的value值,mybatis才能自动读取map集合中的value值。
day06、HTML 和 css
1、概念
-
HTML 是一门语言,所有的网页都是用HTML 这门语言编写出来的
- HTML(HyperText Markup Language):超文本标记语言
- 超文本:超越了文本的限制,比普通文本更强大。除了文字信息,还可以定义图片、音频、视频等内容
- 标记语言:由标签构成的语言
- HTML 运行在浏览器上,HTML 标签由浏览器来解析
- HTML 标签都是预定义好的。例如:使用 展示图片
- W3C 标准:网页主要由三部分组成
-
组成部分
结构:HTML
表现:CSS
行为:JavaScript
-
html5
<!DOCTYPE html> <!--文档声明--> <html lang="en"> <!--定义html文档--> <head> <!--定义文档的头部相关信息--> <meta charset="UTF-8"> <!--字符集--> <title>Title</title> <!--文档标题--> </head> <body> <!--定义文档的主体--> </body> </html>
-
html标签不区分大小写
-
html标签属性值,单双引号都可以
2、HTML常用的标签
2.1、基础标签
h1 标题 、front 字体、b 粗体、i 斜体、u 下划线、p 段落、br 换行。。。
2.2、媒体相关标签
1. img : 定义图片标签
<img src = "" height ="" width = ""/>
-- src : 图片的路径url,统一资源定位符
-- height : 定义图片的高度
-- weight : 定义图片的宽度
2. audio : 定义音频
<audio src = "" controls = ""></audio>
-- src : 音频的url
-- controls : 显示播放器控件
3. video : 定义视频【支持格式:MP4、WebM、OGG】
<video src ="" controls></video>
-- src : 视频的url
-- controls : 显示播放器控件
2.2.1、URL资源定位符注意点
1. 引用路径,使用外网路径
<img src = "https://gimg3.baidu.com/search/src=http%3A%2F%2Fpics7.jpg"/>
2. 使用本地路径
2.1. 绝对路径,带盘符
<img src = "D:\images\a.jpg"/>
2.2 相对路径
相对当前项目
同一个目录
<img src = "a.jpg"/> 或者 <img src = "./a.jpg"/>
不同目录
访问上一个文件夹 assets 下的 a.jpg
<img src = "../assets/a.jpg"/>
2.3、超链接 | 锚点链接
- 超链接
<a href = "" target = ""></a>
href : 指定访问资源的URL
target : 指定打开资源的方式
-- : _self 默认值,在浏览器当前窗口打开
-- : _blank 新建一个窗口打开
& : 在新的窗口中打开百度网站
<a href = "https://www.baidu.com" target = "_blank"></a>
- 锚点链接
1. 锚点链接能跳转到指定的文本位置。
<a href = "#reademe">感情经历</a>
<p>....</p>
.....
...
<p id = "reademe">风停了,雨顿了,你一定要走,我还站在。。。</p>
.....
2.4、列表
1. 有序列表
<ol>
<li></li>
...
<li></li>
</ol>
2. 无序列表
<ul>
<li></li>
...
<li></li>
</ul>
3. 自定义列表
<dl>
<dt></dt>
<dd></dd>
...
<dd></dd>
</dl>
<ol>
<li>周润发</li>
<li>刘德华</li>
<li>张学友</li>
<li>黄家驹</li>
</ol>
<ul>
<li>周润发</li>
<li>刘德华</li>
<li>张学友</li>
<li>黄家驹</li>
</ul>
<dl>
<dt>标题</dt>
<dd>周润发</dd>
<dd>刘德华</dd>
<dd>张学友</dd>
<dd>黄家驹</dd>
</dl>
2.5、表格
<table border="1" width="300" cellspacing="0">
<thead>
<th>人物名称</th>
<th>性别</th>
<th>年龄</th>
</thead>
<tbody>
<tr>
<td>刘德华</td>
<td>男</td>
<td>55</td>
</tr>
<tr>
<td>张学友</td>
<td>男</td>
<td>40</td>
</tr>
<tr>
<td>黄家驹</td>
<td>男</td>
<td>28</td>
</tr>
</tbody>
</table>
1. <table> : 定义表格
属性 :
width : 表格的宽度
border : 表格的边框的宽度
cellspacing : 单元格之间的空白
2. <trhead> : 表头
<th> : 表头标题
3. <tbody> : 表格主体
<tr> : 表格中的一行
属性 : align 定义对齐方式
<td> : 最小单位,单元格
属性 :
rowspan : 单元格横向跨越的行数
colspan : 单元格纵向跨越的列数
2.6、表单
- 表单:在网页中主要负责数据采集功能,使用 标签定义
- 表单项,不同类型的input表单项、下拉列表、文本域
<!--定义表单-->
<from action = "url地址" method = "get / post">
<!--定义表单项,通过type属性控制输入形式-->
<!--隐藏域-->
<input type = "hidden" name = "id">
<!--文本输入框-->
<input type = "text" name = "username" placeholde = "请输入用户名">
<!--密码输入框-->
<input type = "passowrd" name = "password">
<!--单选按钮-->
<intpu type = "radio" name = "gender" value = "man"/> 男
<!--多选按钮-->
<input type = "checkbox" name = "hobby" value = "basketball"/> 篮球
<input type = "checkbox" name = "hobby" value = "sing"/> 唱歌
<!--文本-->
<label></label>
<!--下拉框-->
<select>
<!--下拉选项-->
<option></option>
</select>
<!--文本域-->
<textarea></textarea>
<!--提交按钮-->
<input type = "submit">
<!--重置按钮-->
<input type = "reset">
<!--普通按钮-->
<input type = "button" value = "普通按钮">
</from>
- 注意事项
1. form :
-- action : 表单提交的路径URL
-- method : 提交的方式
get : 数据直接附在表单的URL之后,数据大小有限制
post : 数据放到http请求体重,数据大小无限制
2. input :
-- type : 定义表单项类型
-- name : 表单想要提交必须有name属性。单选按钮 / 复选框 name 属性要相同
-- value : 普通表单的value表示默认值。单选按钮 / 复选框必须有 value属性,否则提交给服务器就是 on
-- checked : 单选按钮 / 复选框 表示默认选中状态
3. select : 下拉列表
-- select 标签上必须定义 name 属性。使用 option 子标签定义下拉表项
-- 在 option 标签上定义 value 属性,否则提交 option 标签的文本内容
4. label : 文本
-- 用户交互,label标签的for 属性要和 input 标签的 id 属性一致,点击文本,input能获得焦点。
3、css
-
CSS 是一门语言,用于控制网页表现CSS(Cascading Style Sheet):层叠样式表
-
CSS 导入 HTML有三种方式:
day07、JavaScript
-
什么是JavaScript?
-
JavaScript (简称:JS)是一门跨平台、面向对象的脚本语言,来控制网页行为的,它能使网页可交互。
脚本语言:不能单独运行,需要嵌套到其他语言中执行。另外脚本语言不需要编译,直接解析执行。
W3C 标准:网页主要由三部分组成
结构:HTML
表现:CSS
行为:JavaScript
JavaScript 和 Java 是完全不同的语言,不论是概念还是设计。但是基础语法类似。
JavaScript 在 1995 年由 布兰登·艾奇(Brendan Eich)发明,并于 1997 年成为一部 ECMA 标准。
ECMAScript 6 (ES6) 是最新的 JavaScript 版本(发布于 2015 年)。
-
01-javascript引入方式
1. 在html页面中,嵌入<script>...js...</script>
2. 从外部引入JavaScript文件
<script src = "../js/index.js"></script>
02-书写语法和输出语句
1 使用window.alert()弹框输出
window.alert("hello javascript"); //window对象可以省略不写
2 使用document.write()输出到浏览器页面上
document.write("hello javascript"); // document是文档对象
3 使用console.log()输出到浏览器控制台中,需要F12或者Fn+F12--->console中查看
console.log("hello javascript");
03-变量
1. var a = 10;
-- var 声明的变量,作用域外可以访问
{
var num1 = 50;
}
console.log(num1); // 50
2. let b = 10;
-- let 声明的变量,存在作用域
{
let num2 = 50;
}
console.log(num2); // undefined
3. const c = 10;
//c = 20;
console.log(c);
-- const 定义的是常量,常量一旦定义,不能改变。
04-数据类型
- JavaScript 中提供了两类数据类型:原始类型 和 引用类型。
-- 原始类型
1. number : 数字(整数、小数、NaN) NaN ---> not a nambuer
2. string : 字符串,单双引号都行
3. boolean : 布尔 。 true | false
4. null : 对象为空
5. undefined : 当声明的变量未初始化,该变量的默认值是 undefined
-- 引用类型
1. 对象就是引用类型 new Date()
1. 使用 typeof 运算符可以获取数据类型
let a = "hello";
console.log(typeof a); // string
05-类型转换
1. String 字符串类型转换为 Number 数字类型
let num = parseInt("123321");
console.log(num); //123321
let num2 = parseInt(123a321);
console.log(num2); //123
-- 转换规则
从字符串的第一个字符开始,一个字符一个字符的进行转换,遇到无法转换的字符就停止转换,并且保留已经转换的结果
---------------------------------------------------------------------------------------------------------
2. 非 boolean 类型转换boolean
2.1、 number : 数字类型的 0 和 NaN 自动转换成 false,其他数字自动转换成 true
let b = 0;
let flag = false;
console.log(b==flag) // true
2.2、 string : "" 空字符串自动转换 false,其他字符串自动转成 true
2.3、 null / undefined 类型会自动转换成 false
06-运算符
1. JavaScript中的运算符与java中的基本一致
2. java 中 没有 ===
javascript
=== 是全等于,只有数据类型和字面值都相同的时候,才返回true
== 是等于,字面值相等,就返回true
let n = 100;
let str = "100";
console.log(n == str); // true
console.log(n === str);// false
07-流程控制语句
-- 流程控制语句和 java 中的基本一样
1. switch
2. if
3. for
4. while
5. do{}while();
08-函数
- 函数(方法)是被设计为执行特定任务的代码块,相当于java中的方法
- JavaScript 函数通过 function 关键词进行定义。
定义格式
函数定义格式有两种:
-
方式1
function 函数名(参数1,参数2..){ 要执行的代码 }
-
方式2
var 函数名 = function (参数列表){ 要执行的代码 }
注意:
形式参数不需要类型。因为JavaScript是弱类型语言
function add(a, b){ return a + b; }
上述函数的参数 a 和 b 不需要定义数据类型,因为在每个参数前加上 var 也没有任何意义。
返回值也不需要定义类型,可以在函数内部直接使用return返回即可
函数调用
函数调用函数:
函数名称(实际参数列表);
let result = add(10,20);
注意:
JS中,函数调用可以传递任意个数参数
例如
let result = add(1,2,3);
它是将数据 1 传递给了变量a,将数据 2 传递给了变量 b,而数据 3 没有变量接收。
09-Array对象
- JavaScript使用 Array 对象用于定义数组
1. let arr = new Array(5); // 定义一长度为3的数组
2. let arr = new Array(15,"hello",true,3.14); // 定义一个数组,初始值是 15,"hello",true,3.14
3. let arr = [15,"hello",true,3.14]; // 定义一个数组,初始值是 15,"hello",true,3.14
1. 数组的访问、遍历、赋值,和java中基本一样
-- 特殊遍历方式
for(let 变量 of 数组对象){
}
2. 特点: JavaScript中的数组类似java中的集合,长度可以改变,并且可以存储任意类似数据
10-String对象
1. 定义
let str1 = new String("hello");
let str2 = 'hello';
2. 使用模板字符串
let str3 = `hello`;
3. 字符串的拼接
// let str4 = "系统时间,今年是2022年7月27日,是个好日子。";
-- 普通的字符串
let str4 = "系统时间,今年是 " + year + " 年" + month + "月" + day + "日,是个好日子。";
-- 模板字符串
let str5 = "系统时间,今年是${year}年${month}月${day}日,是个好日子。";
11-RegExp正则对象
- 概念:正则表达式定义了字符串组成的规则,一般用于校验
- test(str):判断指定字符串是否符合规则,返回 true或 false
1. 正则表达式,专门用来校验字符串
-- 创建RegExp对象
-- let regExp = /^[\\d]{6,12}$/;
--
let regExp = new RegExp("^[\\d]{6,12}$");
-- RegExp对象调用 test(str) 方法进行校验字符串
boolean flag = regExp.test("12345");
console.log(flag); // false
- 语法
^ : 表示开始
& : 表示结束
[] : 代表某个范围内的单个字符
.: : 代表任意单个字符,除了换行和行结束符
12-自定义对象
- 先定义类,后创建对象
class Student{
name ;
age ;
gender ;
constructor(name,age,gender) {
this.name = name;
this.age = age;
this.gender = gender;
};
eat = function (){
console.log("认真干饭~");
};
study(){
console.log("好好学习~");
}
}
let student = new Student("张益达",18,"男");
console.log(student.name); // 张益达
student.study(); //好好学习~
- 直接量方式定义对象【常用】
let Person = {
name,
age,
eat(){}
}
// 可以自动扩展属性
Person.address = "湖北武汉";
13-BOM-Window对象
- Browser Object Model 浏览器对象模型
- JavaScript将浏览器的各个组成部分封装为对象
1. Window : 浏览器窗口对象
2. Navigator : 浏览器对象
3. Screen : 屏幕对象
4. History : 历史记录对象
5. Location : 地址栏对象
- window 浏览器对象,其中window可以省略不写
1. 弹出框
window.alert();
2. 询问框,确认
window.confirm();
3. 到时计时器
window.setTimeOut();
4. 周期计时器
window.setInterval()
14-BOM-Location对象
1. 浏览器跳转
location.href = url;
15-DOM-获取Element对象
- ES5
1. getElementById:根据id属性值获取,返回一个Element对象
2. getElementsByTagName:根据标签名称获取,返回Element对象数组
3. getElementsByName:根据name属性值获取,返回Element对象数组
4. getElementsByClassName:根据class属性值获取,返回Element对象数组
- ES6
1. document.querySelector(“css选择器”):根据css选择器获取一个Element对象
2. document.querySelectorAll(“css选择器”) :根据css选择器获取一个Element对象数组
16-事件监听
-
•事件:HTML 事件是发生在 HTML 元素上的“事情”。比如:
•按钮被点击
•鼠标移动到元素之上
•按下键盘按键
•事件监听:JavaScript 可以在事件被侦测到时要的执行代码
-
事件绑定有两种方式:
-
•方式一:通过 HTML标签中的事件属性进行绑定
-
如下面代码,有一个按钮元素,我们是在该标签上定义
事件属性
,在事件属性中绑定函数。onclick
就是单击事件
的事件属性。onclick='on()'
表示该点击事件绑定了一个名为on()
的函数<input type="button" οnclick='on()’>
下面是点击事件绑定的
on()
函数function on(){ alert("我被点了"); }
-
方式二:通过 DOM 元素属性绑定
-
如下面代码是按钮标签,在该标签上我们并没有使用
事件属性
,绑定事件的操作需要在 js 代码中实现<input type="button" id="btn">
下面 js 代码是获取了
id='btn'
的元素对象,然后将onclick
作为该对象的属性,并且绑定匿名函数。该函数是在事件触发后自动执行document.getElementById("btn").onclick = function (){ alert("我被点了"); }
-
17-案例-表单验证
-
HTML结构
<form id="reg-form" action="19-DOM-事件概念和事件绑定-方式二.html" method="post"> <table> <tr> <td>用户名</td> <td class="inputs"> <input name="username" type="text" id="username" placeholder="请输入6~12位字母、数字、下划线"> <br> <span id="username_err" class="err_msg" style="display: none">用户名不符合要求</span> </td> </tr> <!--------------------其余略------------------------------------>
-
js
/* 分析:表单验证 失去焦点,使用正则校验 -- 成功,则不现实提示信息 -- 失败,则提示错误信息 对象 : input标签,span标签 校验方法 : checkRegExp */ // 封装成一个对象 class CheckRegex{ objInput; // 输入框 objSpan; // span regex; // 正则表达式 flag; // 校验的结果 constructor(objInput,objSpan,regex) { this.objInput = objInput; this.objSpan = objSpan; this.regex = regex; }; //校验方法 : checkRegExp, 返回校验成功或者失败 checkRegExp(){ // 校验输入的value值是否合法 this.flag = this.regex.test(this.objInput.value); if(this.flag){ // 校验合法,不展示提示信息 this.objSpan.style.display = "none"; }else { // 校验不合法,展示提示信息 this.objSpan.style.display = "block"; } } } // 获取dom对象 let username = document.querySelector("#username"); let username_err = document.getElementById("username_err"); // 正则校验表达式 let regex = new RegExp("^[\\w]{6,12}$"); // 创建用于校验用户名对象 let obj1 = new CheckRegex(username,username_err,regex); // username输入框失去焦点 username.onblur = function (){ obj1.checkRegExp(); } // ----------------------------------------------------------------------------- // 获取dom对象 let password = document.querySelector("#password"); let password_err = document.getElementById("password_err"); // 正则校验表达式 // 创建用于校验密码对象 let obj2 = new CheckRegex(password,password_err,regex); // password输入框失去焦点 password.onblur = function (){ obj2.checkRegExp(); } // ----------------------------------------------------------------------------- // 获取dom对象 let tel = document.querySelector("#tel"); let tel_err = document.getElementById("tel_err"); // 正则校验表达式 let regex2 =new RegExp("^[1][\\d]{10}$"); // 创建用于校验电话对象 let obj3 = new CheckRegex(tel,tel_err,regex2); // tel输入框失去焦点 tel.onblur = function (){ obj3.checkRegExp(); } // 页面跳转,表单提交 let formSubmit = document.getElementById("reg-form"); formSubmit.onsubmit = function (){ return obj1.flag && obj2.flag && obj3.flag; }
----------------------榆木脑袋-炼气期三层-------------2024-03-27 13:55:23---