文章目录
- 数据库
- Mysql基础
- 一、数据库
- 1.数据库
- 2.数据库管理系统
- 3.SQL
- 4.Mysql目录结构
- 5.关系型数据库
- 6.SQL基础概念
- mysql高级
- 一、数据库备份和还原
- 1.图形化界面备份与还原
- 二、约束
- 1.分类:
- 2.主键约束
- 3.唯一约束
- 4.非空约束
- 5.默认值约束
- 6.外键约束
- 三、表关系
- 1.概述
- 2.一对多
- 3.多对多
- 4.一对一
- 四、多表查询
- 1.笛卡尔积
- 2.内连接查询
- 3.外连接查询
- 4.子查询
- 五、事务
- 1.简介
- 2.基本使用
- 3.事务的四大特性
- 4.事务的隔离级别
- 六、函数
- 1.日期函数
- 2.判断函数
- 3.字符函数
- 4.数学函数
- 七、MySQL性能
- 1.提高操作数据库性能
- 2.演示-执行次数比较多的语句
- 3.查看未优化SQL语句执行效率
- 4.索引
- 5.MySQL索引语法
- 6.索引的数据结构
- 7.索引创建原则
- 8.避免索引失效
- (1)全值匹配,对索引中所有列都指定具体值
- (2)最左前缀法则
- (3)范围查询右边的列,不能使用索引
- (4)不要在索引列上进行运算操作,索引将失效
- (5)字符串不加单引号,造成索引失效
- (6)用or分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到
- (7)以%开头的Like模糊查询,索引失效
- (8)如果MySQL评估使用索引比全表更慢,则不使用索引。
- (9)in 走索引, not in 索引失效
- 八、JDBC
- 1.JDBC介绍
- 2.DriverManager API详解
- 3.Connection API详解
- 3.Statement API详解
- 4.ResultSet API详解
- 5.使用PreparedStatement解决SQL注入
- 九、三层架构模型
- 1.介绍
- 十、数据库连接池
- 1.简介
- 2.实现
- 3.数据库连接池技术解决什么问题?
- 4.数据库连接池的使用
- 十一、类的加载概述
- 1.类加载器的作用:
- 2.类加载器的分类:
- 3.类加载器的方式:
- 4.双亲委派模型
- 5.使用类加载器加载配置文件的方式
- 十二、反射
- 0.使用反射技术
- 1.概述
- 2.反射的关键:
- 3.获取Class对象的三种方式
- 4.Constructor对象
- 5.Method对象
- 6.Field对象
- 十三、注解
- 1.注解概述
- 2.注解的作用
- 3.自定义注解
- 4.特殊属性
- 5.元注解
- 6.注解的解析
- 十四.动态代理
- 1.动态代理实现的关键步骤
- 2.动态代理的特点:
- 3.动态代理的实现:
- 4.动态代理的优点
- 十五、XML
- 1.XML是什么?
- 2.XML组成:
- 3.XML约束:DTD、Schema
- 4.XML解析
- 5.DOM解析原理和DOM树模型
- 6.Dom4j常用API介绍
- 7.Dom4j结合XPath解析XML(了解)
- 十六、Mybatis
- 1.Maven
- 2.安装配置步骤
- 3.Maven坐标
- 4.Maven基本使用
- 5.MyBatis
- 6.MyBatis核心配置文件
- 7.MyBatis映射配置文件
- 8.编写会话工具类
- 9.实体类属性名和数据库列名不一致,不能自动封装数据
- 10.在Mybatis中使用的占位符:
- 11.SQL语句设置多个参数的几种方式:
- 12.动态SQL
- 13.主键回填
- 14.MyBatis高级查询
- (1)mybatis多表查询套路
- (2)一对一映射语法格式:
- (3)一对多映射语法格式
- 15.mybatis注解开发
- (1)概述
- (2)CURD相关注解
- (3)主键回填
- (4)注解实现别名映射
- (5)模糊查询
- 面试题:
数据库
Mysql基础
一、数据库
1.数据库
- 存储数据的仓库,数据是有组织的进行存储;
2.数据库管理系统
- 管理数据库的大型软件
- 英文:DataBase Management System ,简称DBMS
3.SQL
- 英文: Structured Query Language,简称SQL,结构化查询语言
- 操作关系型数据库的编程语言
- 定义操作所有关系型数据库的统一标准
4.Mysql目录结构
5.关系型数据库
- 关系型数据库是建立在关系模型基础上的数据库,简单说,关系型数据库是由多张能互相连接的二维表组成的数据库
- 优点:
- 都是实用表结构,格式一致,易 于维护
- 使用通用的SQL语言操作,使用方便,可用于复杂查询
- 数据存储在磁盘中,安全
6.SQL基础概念
-
通用语法:
- SQL语句可以单行或多行书写,以分号结尾;
- MySQL数据库的SQL语句不区分大小写,关键字建议用大写
- 注释:
- 单行注释:–注释内容 或 #注释内容(MySql特有)
- 多行注释:/* 注释 */
-
分类:
- DDL(Data Definition Language)数据定义语言,用来定义数据库对象:数据库、表、列等;
- DML(Data Manipulation Language)数据操作语言,用来对数据库中表的数据进行增删改;
- DQL(Data Query Language)数据查询语言,用来查询数据库表中的记录(数据);
- DCL(Data Control Language)数据控制语言,用来定义数据库的访问权限和安全级别,及创建用户;
-
DDL 操作数据库
- 查询
SHOW DATABASES;
- 创建
# 创建数据库 CREATE DATABASE 数据库名称; # 创建数据库(判断,如果不存在则创建) CREATE DATABASE IF NOT EXISTS 数据库名称;
- 删除
# 删除数据库 DROP DATABASE 数据库名称; # 删除数据库(判断,如果存在则删除) DROP DATABASE IF EXISTS 数据库名称;
- 使用数据库
# 查看当前使用的数据库 SELECT DATABASE # 切换数据库 USE 数据库名称
- 数据表的创建:
CREATE TABLE 表名( 字段1 数据类型1, 字段2 数据类型2, 字段3 数据类型3, )
- 数据类型
- 数值
- 日期
- 字符串
类型 描述 int 整型 double 浮点型 varchar 字符串型 date 日期,格式为 yyyy-MM-dd,只有年月日,没有时分秒; -
数据表的删除
#删除表 DROP TABLE 表名; #删除表时判断表是否存在 DROP TABLE IF EXISTS 表名;
-
修改表
#修改表名 ALTER TABLE 表名 RENAME TO 新的表名; #添加一列 ALTER TABLE 表名 ADD 列名 数据类型; #修改数据类型 ALTER TABLE 表名 MODIFY 列名 数据类型 #修改列名和数据类型 ALTER TABLE 表名 CHANGE 列名 新列名 新数据类型; #删除列 ALTER TABLE 表名 DROP 列名;
-
DML语言
-
insert操作
# 给指定列添加数据 INSERT INTO 表名(列名1,列名2,...) VALUES(值1,值2,...); # 给全部列添加数据 INSERT INTO 表名(全部列名) VALUES(值1,值2,...); #批量添加数据 INSERT INTO 表名(全部列名) VALUES(值1,值2,...), (值1,值2,...),(值1,值2,...);
-
update操作
# 修改表数据 UPDATE 表名 SET 列名1=值1,列名2=值2,...[WHERE 条件];
-
delete操作
# 删除数据 DELETE FROM 表名 [WHERE 条件]; # truncate 删除表记录 属于DDL TRUNCATE TABLE 表名; /* delete 和 truncate 区别: 1.delete是逐行删除表中的记录(删除所有的记录:一行一行删除) truncate是先执行:drop table 表名,再执行:create table 表名 (先删除表,再创建一张相同结构的表) */
-
-
DQL语言
-
基础查询
# 查询语法 SELECT 字段列表 FROM 表名列表 WHERE 条件列表 GROUP BY 分组字段 HAVING 分组后条件 ORDER BY 排序字段 LIMIT 分页限定
# 查询多个字段 SELECT 字段列表 FROM 表名; SELECT * FROM 表名; # 查询所有数据 # 去除重复记录 SELECT DISTINCT 字段名,字段名,... FROM 表名; # 查询时给列、表指定别名需要使用AS关键字 SELECT 字段名1 AS 别名1,字段名2 AS 别名2 ,... FROM 表名; SELECT 字段名1 AS 别名1,字段名2 AS 别名2 ,... FROM 表名 AS 表别名; # AS也可以省略
-
-
DQL语言
-
条件查询1
SELECT 字段列表 FROM 表名 WHERE 条件列表;
-
条件
-
模糊查询
SELECT * FROM 表名 WHERE 字段名 LIKE '通配符字符串'; /* 满足通配符字符串规则的数据就会显示出来,所谓的通配符字符串就是含有通配符的字符串 %:表示任意多个字符 _: 表示一个字符 */
-
排序查询
SELECT 字段列表 FROM 表名 [where 条件] ORDER BY 排序字段名1 [排序方式1],排序字段名2 [排序方式2] /* 排序方式: ASC:升序(默认值) DESC:降序排列 */
-
聚合函数
-
概念:将一列数据作为一个整体,进行纵向计算
-
分类:
函数名 功能 count(列名) 统计数量(一般选用不为null的列) max(列名) 最大值 min(列名) 最小值 sum(列名) 求和 avg(列名) 平均值 -
语法:
SELECT 聚合函数名(列名) FROM 表 /* 在mysql中,null值和任意类型的数据值,相加后的结果还是:null null值 不参与 聚合函数中的运算 可以使用mysql中的函数:ifnull(列名,默认值) 解决null值的问题 */ select sum(ifnull(math,0) +(ifnull(english,0) ) as "英数总分" from student;
-
-
分组查询
- 分组就是按照某一列或者某几列。把相同的数据,进行合并输出;
SELECT 字段列表 FROM 表名 [WHERE 分组前条件限定] GROUP BY 分组字段名[HAVING 分组后条件过滤] /* 注意: 1.分组之后,查询的字段为聚合函数和分组字段,查询其他字段无任何意义。 2.group by 后边的字段值相同才能划分为一组; */ /* 语法要求: 1.在进行分组查询时,书写在select关键字后面的别名,要么出现在group by 关键字后,要么使用聚合函数包含 例子: select age from student group by age 查询的age字段出现在group by 关键字后 select age,count(id) from student group by age 查询的id字段没有出现在group by 关键字后,要使用聚合函数包含 */
- where 和 having 的区别
- 执行时机不一样:where是分组之前进行限定,不满足where条件,则不参与分组,而having’是分组之后对结果进行过滤;
- 可判断的条件不一样:where不能对聚合函数进行判断,having可以;
-
分页查询
SELECT 字段列表 FROM 表名 limit 起始索引,查询条数 # 起始索引=(当前页码-1) * 每页显示的条数
- 为什么使用分页查询:
- 随着业务增长,数据表中的体量会越来越大,查询时就不能把所有数据全部查询出库(效率低);
- 解决方案:查询一部分数据,在查一部分数据
- 为什么使用分页查询:
-
执行顺序
# 查询语法 SELECT 字段列表 FROM 表名列表 WHERE 条件列表 GROUP BY 分组字段 HAVING 分组后条件 ORDER BY 排序字段 LIMIT 分页限定 /* 1.form 2.where 3.group by 4.having 5.select 6.order by 7.limit */
-
mysql高级
一、数据库备份和还原
1.图形化界面备份与还原
在服务器进行数据传输、数据存储和数据交换,就有可能产生数据故障。比如发生意外停机或存储介质损坏。这时,如果没有采取数据备份和数据恢复手段与措施,就会导致数据的丢失,造成的损失也是无法弥补与估量的。
- 可视化客户端操作数据库的备份及还原(存储为sql文件)
二、约束
约束:定义在表中的列上,用于针对列中所存储的数据进行限制(目的:保证数据的合法性、有效性)
1.分类:
- 主键约束(特点:唯一+非空)
- 唯一约束(特点:唯一)
- 非空约束(特点:不能为null)
- 默认值约束(特点:没有指定具体数据时给默认值)
- 外键约束(特点:建立表与表之间的关系)
2.主键约束
-
主键概述:
设定表中某一字段为主键,那么该字段所在列的数据就能够唯一的标识表中的每一行数据
设定为主键的字段一般和业务无关列有这些特点:唯一、非空、非业务相关
-
主键的定义及删除语法
-
在建表时进行指定
主键是给数据库和程序使用的,不是给最终的客户使用的。所以主键字段与业务没有关系,只要不重复,非空就行。通常会单独给每张表设计一个id的字段,把id作为主键,如下:
create table 表名 (id int primary key,其他字段,...); #根据查询的结果,创建新表(表结构和要查询的数据表结果一模一样) create table db4.student AS select * from db1.student
-
在已有表中进行指定(原表没有主键)
alter table 表名 add primary key(字段);
-
删除主键语法
alter table 表名 drop primary key(字段);
-
-
主键自增
- 注意细节:
- 主键列下的数据由mysql维护(从1开始,每次增长1)
- mysql维护过的主键值,不能继续使用
- 当程序员干预主键,就会拿插入的主键值,覆盖mysql管理的主键值
- delete :不会影响 auto_increment维护的主键值
- trucate:先删除表在创建表(会重置auto_increment维护的主键值,重置为0)
字段名 字段类型 primary key auto_increment #在自增主键中,可以设置主键的起始值 alter table 表名 auto_increment = 起始值;
- 注意细节:
3.唯一约束
-
作用:
-
被唯一约束的字段,本列数据不允许出现重复数据,null除外,null可以出现多个;
-
语法:unique
create table 表名(字段名 字段类型 unique,....) alter table 表名 add unique(字段);
-
4.非空约束
-
作用:
-
被非空约束的字段,本列数据不允许出现null(即空)数据。插入数据如果该字段为空会报错
-
语法:not null
create table 表名( 字段名 字段类型 not null, ); alter table 表名 modify 字段名 类型 not null
-
5.默认值约束
-
被默认值约束的字段,相当于给字段添加默认值,插入数据时如果字段没有赋值,则使用默认值。
-
语法:default
create table 表名( 字段名 字段类型 default 默认值, 其他字段 ); alter table 表名 modify 字段名 字段类型 default 默认值;
6.外键约束
-
外键就是从表中设定一个字段,用来保存主表中的主键值。外键约束了从表的外键值只能是主表的主键值。
-
先有外键字段,再有外键约束
create table 表名( 其他字段, 外键字段 类型 , constraint 外键名 foreign key (当前表外键字段名) references 主表名(主表主键) ); alter table 表名 add constraint 外键名 foreign key(当前表外键字段名) references 主表名(主表主键) #删除外键语法 alter table 表名 drop foreign key 外键名称
-
注意事项:
-
添加数据时:先添加主表中的数据,再添加从表中的数据。
-
删除数据时:先删从表中的数据,再删主表中的数据。
-
修改数据时:如果主表中的主键被从表引用了,不能修改此主键的值。
-
外键的级联:级联操作就是在修改或者删除主键时可以同时对从表的外键进行修改删除。
#表示主表进行更新操作后,从表对应的数据也要进行更新操作; on update cascade #表示主表进行主键删除后,从表外键对应的数据也要删除 on delete cascade
-
三、表关系
1.概述
数据库中表描述的事物,一般称为实体。现实生活中事物与事物之间是存在关系,映射到数据库就是实体与实体之间存在关系。
- 表和表之间的关系分为三种:
- 一对一(1:1)
- 一对多(1:n)
- 多对多(m:n)
2.一对多
- 在多表上新增字段(外键),外键字段下存储的数据全部来自主表中的主键;
- 外键存在一种约束,只能是主表中主键存在的值。这样能保证主从表数据之间对应的一致性和完整性;
- 外键约束的作用:
- 建立表与表之间的关系
- 约束外键字段下的数据和主表主键下的数据保持一致(一致性、完整性)
3.多对多
- 创建第三张中间表,由中间表承担外键字段(降低多对多的复杂关系,变为一对多)
4.一对一
在实际的开发中应用不多,因为一对一可以创建成一张表。若一定要分表有两种建表原则:
- 外键唯一:
- 任选一方为从表建立唯一约束的外键
- 主外键一体:
- 主表的主键和从表的主键,形成主外键关系
四、多表查询
1.笛卡尔积
多张表查询时每张表的每条数据组合成的数据结果集,叫笛卡尔积。
2.内连接查询
内连接操作目的是把多张表中相互关联的数据查询出来。
-
隐式内连接
select 列名 from 主表,右=从表 where 从表.外键=主表.主键
-
显示内连接
select 列名 from 主表 inner join 从表 on 从表.外键=主表.主键 # on 的执行级高于where # where用于查询条件 on 用于消除笛卡尔积
-
区别:(结果一样)
- 隐式内连接先进行笛卡尔积,然后筛选数据
- 显示内连接在查询时就对数据进行过滤
-
多表查询技巧:
- 明确要查询哪些数据
- 明确所查询数据分别归属哪张表
- 明确表示表和表之间的关系(寻找主键、外键)
3.外连接查询
-
左外连接:左表中所有的记录都出现在结果中,如果右表没有匹配的记录,使用NULL填充。
select 列名 from 左表 left join 右表 on 从表.外键= 主表.外键
-
右外连接:右表中所有的记录都出现在结果中,如果左表没有对应的记录,使用NULL填充。
select 列名 from 左表 right join 右表 on 从表.外键=主表.外键
4.子查询
- 一个查询的结果作为另一个查询语句的一部分
根据子查询的结果,可以将子查询分成三类,结果为单行单列、结果为多行单列、结果为多行多列。
-
单行单列:
SELECT 查询字段 FROM 表 WHERE 字段=(子查询); #例 SELECT AVG(salary) FROM emp;
-
多行单列
SELECT 查询字段 FROM 表 WHERE 字段 IN (子查询); -- in表示在数值中 # 例: SELECT dept.name FROM dept WHERE dept.id IN (SELECT dept_id FROM emp WHERE salary > 5000);
-
多行多列:
如果子查询是多行多列,子查询可以认为它是一张虚拟表,可以使用表连接再次进行多表查询。
SELECT 查询字段 FROM (子查询) 表别名 WHERE 条件; #例: SELECT * FROM dept d, (SELECT * FROM emp WHERE join_date > '2011-1-1') e WHERE e.dept_id = d.id;
-
总结:
- 单行单列:作为父查询的条件;
- 多行单列:作为父查询的条件,通常使用IN;
- 多行多列:作为父查询的一张表(虚拟表),起别名
五、事务
1.简介
- 数据库的事务是一种机制、一个操作序列,包含了一组数据库操作命令。
- 简单理解:如果一个包含多个步骤的业务操作,被事务管理,要么这些操作同时操作成功,要么同时失败。
- 事务是一个不可分割的工作逻辑单元。
2.基本使用
# 事务的使用操作机制
# 1.开启事务
START TRANSACTION;
# 2.执行多条SQL语句(业务操作)
COMMIT;
# 3.提交事务/回滚事务
ROLLBACK;
# MySQL数据库的事务机制
1.自动提交(默认)
每执行一行SQL语句,就开启一个事务,SQL提交完成,事务提交(一行SQL一个事务)
2.手动提交(写代码)
先开启事务,再执行SQL语句(可以多行),提交事务(回滚)
# 修改事务的默认提交方式:
#查看事务的默认提交方式:SELECT @@autocommit;
0代表手动提交
1代表自动提交
# 修改默认提交方式:set @@autocommit = 0;
#注意:如果修改了手动提交任务,增删之后需要执行commit
3.事务的四大特性
数据库的事务必须具备ACID特性,ACID是指 Atomicity(原子性)、Consistensy(一致性)、Isolation(隔离性)和Durability(持久性)的英文缩写。
-
隔离性
-
多个用户并发的访问数据库时,一个用户的事务不能被其他用户的事务干扰,多个并发的事务之间要相互隔离。
-
-
持久性
-
当事务提交或回滚后,数据库会持久化的保存数据。
-
-
原子性
- 原子是不可分割的最小操作单位,事务要么同时成功,要么同时失败
-
一致性
-
事务操作前后,数据总量不变
-
4.事务的隔离级别
事务的并发访问引发的三个问题:
事务在操作时的理想状态:多个事务之间互不影响,如果隔离级别设置不当就可能引发并发访问问题。
-
脏读:一个事务读取到了另一个事务中尚未提交的数据。
-
不可重复读:一个事务中多次读取时数据是不一致的,这是事务update时引发的问题;
-
幻读(虚读):一个事务内读取到了别的事务插入或者删除的数据,导致前后读取记录行数不同,这是insert或delete时引发的问题。
- 隔离级别
#查询隔离级别
show variables like '%isolation%';
#或
select @@tx_isolation;
#修改数据库隔离级别
set global transaction isolation level read uncommitted;
六、函数
1.日期函数
# 获取学生的生日日期(不需要年)
select name ,month(birthday) , day(birthday) from student;
# 将 月 和 日拼接
select name ,concat(month(birthday),'月',day(birthday),'日')
from student;
2.判断函数
判断函数_Case When
-
介绍
- case when 语句,用于计算条件列表并返回多个可能结果表达式之一。
- CASE 具有两个格式:
- 简单CASE函数将某个表达式与一组简单表达式进行比较以确定结果。
- CASE 搜索函数计算一组布尔表达式以确定结果。
-
格式:
-
格式一:简单Case 函数
CASE 表达式1 WHEN 表达式2 THEN 表达式3 WHEN 表达式4 THEN 表达式5 ...... ELSE 表达式6 END as 别名 #例子 CASE sex WHEN '1' THEN '男' WHEN '2' THEN '女' ELSE '其他' END as 别名
-
格式二:CASE搜索函数
CASE WHEN 条件表达式1 THEN 表达式2 WHEN 条件表达式3 THEN 表达式4 ...... ELSE 表达式 5 END as 别名 #例子 CASE WHEN sex = 1 then '男' WHEN sex = 2 then '女' else '保密'; END as 别名
-
3.字符函数
针对字符(varchar)类型的数据进行操作
# 获取字符串长度
select char_length('itheima');
select char_length(name) from user;
# 拼接字符串
select concat('I','lOVE','Java');
# 转大写、转小写
select lower('Itheima');
select upper('Itheima');
# 字符串截取,索引从1开始
select substr('itheima',3,5); #heima
# 去除字符串前后空格
# 应用场景:数据表中char类型数据,存储不足长度时补充空格,
# 但是取出char类型数据时不能带有空格
select trim(' hei ma ');
4.数学函数
针对数字类型的数据提供功能
# 获取随机数字(范围: 0~1)
select rand();
# 四舍五入
select round('3.1415926',4); #3.1416
# 截取数字
select truncate('3.1415926',4); #3.1415
# 获取最小值
select least(11,2,4,7,19,3); #2
七、MySQL性能
1.提高操作数据库性能
-
软优化:
在操作和设计数据库方面上进行优化(学习)(表结构:可以将数据分开多个表,常用的一张表,不常用的一张表和sql语句)
-
硬优化:
在软优化之后性能还是很低,只能采取硬优化,就是公司花钱购买服务器,在硬件上进行优化
2.演示-执行次数比较多的语句
- 执行次数比较多的语句分类
- 查询密集型(DQL)
- 修改密集型(DML)
- 查询累计插入和返回数据条数,即查看当前数据库属于查询密集型还是修改密集型
# 查看mysql数据库执行性能(DML操作多 or DQL操作多)
# 查询累计插入和返回数据条数
show global status like 'Innodb_rows%';
3.查看未优化SQL语句执行效率
针对数据查询效率低的问题,进行优化:索引
4.索引
Mysql官方对索引的定义为:索引是帮助Mysql高效获取数据的数据结构。
索引的本质:索引就是数据结构;(B+Tree)
- 主键(约束)索引
- 主键约束(唯一+非空)+ 提高查询效率
- 唯一(约束)索引
- 唯一约束 + 提高查询效率
- 普通索引
- 仅提高查询效率
- 组合(联合)索引
- 多个字段组成索引
- [联合主键索引、联合唯一索引、联合普通索引]
- 全文索引 TEXT BIGTEXT
- Mysql全文索引使用较少,基本针对文档类数据会选择solr、es等文档搜索类做数据库;
- hash索引
- 根据key-value等值查询效率非常高,但是不适合范围查询;
5.MySQL索引语法
- 创建索引
# 创建普通索引
create index 索引名 on 表名(字段);
# 创建唯一索引
create unique index 索引名 on 表名(字段);
# 创建普通组合索引
create index 索引名 on 表名(字段1,字段2,...);
# 创建唯一组合索引
create unique index 索引名 on 表名(字段1,字段2,...);
- 注意:
- 如果在同一张表中创建多个索引,要保证索引名是不能重复;
- 主键索引primary key 无法通过此方式进行创建;
- 在已有表的字段上修改表时指定
# 添加一个主键,这意味着索引值必须是唯一的,且不能为NULL
alter table 表名 add primary key(字段); #默认索引名:primary
# 添加唯一索引(除了NULL外,NULL可能会出现多次)
alter table 表名 add unique(字段); #默认索引名:字段名
# 添加普通索引,索引值可以出现多次
alter table 表名 add index(字段); #默认索引名:字段名
6.索引的数据结构
B + tree,将树分为叶子节点和非叶子节点,其中非叶子节点只存储索引+指针,不存储数据,而叶子节点存储索引+数据+指针;
(1)B+Tree 的非叶子节点只存储索引和指针域,不保存数据,这样降低树的高度,也就意味着减少了数据查询所需的io次数;
- BTree 是每个节点都进行存储数据,而数据会消耗内存,每个节点最多14K,每个节点存储的数据就少了,这样的话就会让树的高度增加,从而增加了IO次数,效率也就降低了。
- B + Tree 是非叶子节点不存储数据,这样的话节省了内存可以存储多个索引+指针,从而降低了树的高度,减少了IO次数,效率随之提高;
(2)叶子节点按照索引排好序,更好的支持范围查找,速度会很快;(使用了双向链表)
(3)mysql将根节点都加载到内存中,每张表有一个根节点,大小是16KB。那么这样的好处,按照上述如果是千万条数据,那么只有2次磁盘IO。这就是为什么我们加完索引之后能瞬间查到数据的原因;
# 查看mysql索引节点大小
show global status like 'innodb_page_size'; #16kb
7.索引创建原则
索引的设计可以遵循一些有已有的原则,创建索引的时候请尽量考虑符合这些原则,便于提升索引的使用效率,更高效的使用索引。
1.字段内容可识别度不能低于70%,字段内数据唯一值的个数不能低于70%
例:一个表数据只有50行,那么性别和年龄哪个字段适合创建索引,明显是年龄,
因为年龄的唯一值个数比较多,性别只有两个选项。性别的识别度50%。男 女
2.经常使用 where 条件搜索的字段,例如user表的id name 等字段。
3.经常使用表连接的字段(内连接、外连接),可以加快连接的速度。
4.经常排序的字段 order by,因为索引已经是排过序的,这样一来可以利用索引的排序,
加快排序查询速度。
* 注意:是不是在数据库表字段中尽量多建索引呢?
不是。因为索引的建立和维护都是需要耗时的。
创建表时,需要通过数据库去维护索引,添加记录、更新、修改时,也需要更新索引,
会间接影响数据库的效率。
8.避免索引失效
(1)全值匹配,对索引中所有列都指定具体值
该情况下,索引生效,执行效率高。
explain select * from tb_seller where name = '小米' and status='1'
and address = '西安';
(2)最左前缀法则
如果索引了多列,这里指的是复合索引(联合索引),要遵守最左前缀法则。指的是查询从索引的最左前列开始,并且不跳过索引中的列。
==注意:==如果条件中包含了复合索引的全部字段,那么可以不考虑前后顺序。
# 创建索引
create index idx_seller_name_sta_addr on tb_seller(name,status,address);
匹配最左前缀法则,走索引:
1.explain select * from tb_seller where name = '小米科技有限公司';
# key_len 表示索引字段的长度即占字节个数,不同的编码表计算方式不一致
2.explain select * from tb_seller where name = '小米科技有限公司' and
status = '1';
3.explain select * from tb_seller where name = '小米科技有限公司' and
status = '1' and address = '上海市';
(3)范围查询右边的列,不能使用索引
1.explain select * from tb_seller where name='小米科技有限公司'
and status='1' and address='上海市';
2.explain select * from tb_seller where name='小米科技有限公司'
and status>'1' and address='上海市';
# 只有name和status索引生效
根据前面的两个字段 name、status查询是走索引的,但是最后一个条件address没有用到索引。
(4)不要在索引列上进行运算操作,索引将失效
1. # 3 表示索引 2 表示截取2个字符
select * from tb_seller where substring(name,3,2) = '科技';
2. explain select * from tb_seller where substring(name,3,2)='科技';
(5)字符串不加单引号,造成索引失效
1.explain select * from tb_seller where name='小米科技有限公司' and
status='1';
2.explain select * from tb_seller where name='小米科技有限公司' and
status = 1;
# 这里name索引字段生效,status索引字段是无效的
(6)用or分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到
示例,name字段是索引列,而createtime不是索引列,中间是or进行连接是不走索引的
1.explain select * from tb_seller where name = '传智' and
createtime = '2088-01-01 12:00:00';
2.explain select * from tb_seller where name = '传智' or
createtime = '2088-01-01 12:00:00';
(7)以%开头的Like模糊查询,索引失效
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
1.explain select * from tb_seller where name like '传智播客%';
2.explain select * from tb_seller where name like '%传智播客';
3.explain select * from tb_seller where name like '%传智播客%';
参考网站:https://ask.csdn.net/questions/1095456?utm_medium=distribute.pc_aggpage_search_result.none-task-ask_topic-2~aggregatepage~first_rank_ecpm_v1~rank_v31_ecpm-1-1095456.pc_agg_new_rank&utm_term=%E4%B8%BA%E4%BB%80%E4%B9%88%E4%BD%BF%E7%94%A8like+%E7%B4%A2%E5%BC%95%E4%BC%9A%E5%A4%B1%E6%95%88&spm=1000.2123.3001.4430
解决方案 :通过覆盖索引来解决.
1.explain select sellerid from tb_seller where name like '%传智播客%';
2.explain select sellerid,name from tb_seller where name like '%传智播客%';
3.explain select sellerid,name,status,address from tb_seller where name like '%传智播客%';
4.explain select sellerid,name,status,address,password from tb_seller where name like '%传智播客%';
说明:解决上述sql语句索引失效情况需要使用覆盖索引,而password子字段无索引,所以索引失效。
(8)如果MySQL评估使用索引比全表更慢,则不使用索引。
1.show index from tb_seller; -- 查看下索引
2.create index idx_address on tb_seller(address); -- 单独创建 address字段为索引
3.explain select * from tb_seller where address='北京市'; -- 走索引,反而效率更低,全表扫描
4.explain select * from tb_seller where address='上海市';
(9)in 走索引, not in 索引失效
1.explain select * from tb_seller where sellerid in('baidu','huawei','xiaomi');
2.explain select * from tb_seller where sellerid not in('baidu','huawei','xiaomi');
八、JDBC
1.JDBC介绍
JDBC(java数据库连接技术)就是使用Java语言操作关系型数据库的一套API
全称:Java DataBase Connectivity
JDBC本质:
- 官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口
- 各个数据库厂商去实现这套接口,提供数据库驱动jar包
- 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接对象
String url = "jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";
String username = "root";
String password = "1234";
Connection conn = DriverManager.getConnection(url,username,password);
// 3.定义sql
String sql = "update account set many = 2000 where id = 1";
// 4.获取执行sql的对象
Statement stmt = conn.createStatement(); //创建数据库操作对象
//使用数据库操作对象,发送SQL语句
// 5.执行sql
int count = stmt.executeUpdate(sql);//执行DML、DDL语句
//执行DML语句时,执行后返回受影响的行数 受影响行数为0表示增、删、改没有成功
//执行DDL语句,执行后返回0 表示成功
ResultSet rs = stmt.executeQuery(sql);//执行DQL语句(查询)
//执行DQL语句时,执行返回的是结果集对象(封装查询的数据表结果)
// 6.处理结果
System.out.println(count);
// 7.释放资源
stmt.close();
conn.close();
2.DriverManager API详解
DriverManager(驱动管理类)作用:
注册驱动;获取数据库连接
3.Connection API详解
- Connection(数据库连接对象)作用:
- 获取执行SQL的对象;
- 管理事务 conn.setAutoCommit(false); // 相当于是MySql中的:start transaction; conn.commit()、conn.rollback()
- 是一个接口
- 作为数据库连接对象
3.Statement API详解
- Statement 作用
- 执行SQL语句
4.ResultSet API详解
- ResultSet 原理
- ResultSet 内部有一个指针,刚开始记录开始位置;
- 调用next方法,ResultSet内部指 针会移动到下一行数据;
- 我们可以通过ResultSet得到一行数据getXxx得到某列数据;
-
使用步骤:
-
游标向下移动一行,并判断该行是否有数据:next()
-
获取数据:getXxx(参数)
//循环判断游标是否是最后一行末尾 while(rs.next()){ //获取数据 rs.getXxx(参数); }
-
5.使用PreparedStatement解决SQL注入
-
SQL注入:
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加以下SQL关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。
简单来说就是:用户在页面提交数据的时候人为的添加一些特殊字符,使得sql语句的结构发生了变化,最终可以在没有用户名或者密码的情况下进行登录。
-
解决Statment对象中存在的SQL注入风险:使用Statement子接口,PreparedStatement
//创建PreparedStatement对象 【预编译SQL对象】
PreparedStatement pstmt = 数据库连接对象.prepareStatement(sq语句)
//java程序: ?是占位符(先占个位置,没有值)
String sql = "select * from user where username=? and password=?"
//把sql语句先发给数据库
给?占位符,赋值 : 1,"aaa" 2,"aaa' or 1=1--"
//数据库:
接收到java程序发送的sql语句:
String sql = "select * from user where username=? and password=?"
预编译:检查sql的基本语法
第一个?占位符:select * from user where username="aaa"
第二个?占位符:select * from user where username="aaa"
and password="aaa\\' or 1=1\\--"
作为字符串(关键字屏蔽)
九、三层架构模型
1.介绍
- web层:接收客户端发送的数据 -> 把接收的数据封装为对象 -> 调用service层方法(并传递数据对象) -> 根据service 层方法执行结果,给客户端回馈
- service层:处理业务逻辑(会调用dao层中的方法)
- dao层:和数据库交互(底层利用jdbc技术)
分层的目的是:
- 解耦:降低代码之间的依赖关系;
- 可维护性:哪一层出现问题,直接维护哪一层
- 可扩展性:哪一层需要添加代码,直接添加即可
- 可重用性:一个方法可以被其它层重复调用
十、数据库连接池
1.简介
- 数据库连接池是个容器,负责分配、管理数据库连接。
- 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;
- 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏
- 好处:资源利用、提升系统响应速度
2.实现
- 标准接口:DataSource
- 官方(SUN)提供的数据库连接池标准接口,由第三方组织实现此接口。
- 功能:获取连接
Connection getConnection()
- 常见的数据库连接池:
- DBCP
- C3P0
- Druid(德鲁伊)
- Druid连接池是阿里巴巴开源的数据库连接池项目;
- 功能强大,性能优秀,是Java语言最好的数据库连接池之一;
- Druid是阿里巴巴开发的号称为监控而生的数据库连接池(也可以监控访问数据库的性能),==Druid是目前最好的数据库连接池。==在功能、性能、扩展性方面,都超过其他数据库连接池。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。如:双11
3.数据库连接池技术解决什么问题?
-
提高程序的执行效率
-
节省资源(不用频繁的创建和销毁Connection)
-
java语言基于连接池,制定标准:java.sql.DataSource接口
第三方组织,按照DataSource接口中制定的标准,实现数据库连接池功能;
4.数据库连接池的使用
- 创建数据库连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(Properites对象)
- 从数据库连接池,获取一个Connection
Connection conn = ds.getConnection();
- 基于Connection对象,创建数据库执行对象
- 执行sql语句
- …
十一、类的加载概述
当程序在运行后,第一次使用某个类的时候,会将此类的class文件读取到内存,并将此类的所有信息存储到一个class对象中。
1.类加载器的作用:
- 加载磁盘上的.class文件到内存中,并创建Class对象(基于.class文件创建)
- 加载器是Java运行时环境的一部分,负责加载字节码文件,即将磁盘上的某个class文件读取到内存并生产class对象
- Student.class文件中都包含什么内容?
- 构造方法、成员变量、成员方法
- 在JVM执行某个类时,如果该类是第一次执行:
- 先把该类的.class文件读取到内存中
- 基于.class文件创建一个Class对象(方法区)
- Class对象中存储的是.class文件中的内容:构造方法、成员变量、成员方法
- Class对象中存储的构造方法:构造对象 Constructor对象
- Class对象中存储的成员变量:字段对象Field对象
- Class对象中存储的成员方法:方法对象Method对象
2.类加载器的分类:
- 启动类加载器:加载java运行环境中核心类库(例:Object)
- 扩展类加载器:加载java运行环境中提供的扩展类库下内容(扩展类:lib/ext目录)
- 应用程序类加载器:加载程序员自己定义的类(例:Student、Teacher)
3.类加载器的方式:
- 来自Class类型获取类加载器的方法:
public ClassLoader getClassLoader() //返回该类的类加载器
//有些实现可能使用null来表示引导类加载器(启动类加载器 )
4.双亲委派模型
- 3种类加载器的关系
从图中可知,三种类加载器存在一定的关系:
(1)应用程序类加载器的父级类加载器是扩展类加载器
(2)扩展类加载器的父级加载器是启动类加载器
加载器结论:这种关系称为类加载器的"双亲委派模型"
- “双亲委派模型”的工作机制:
- 某个"类加载器"收到类加载的请求,它首先不会尝试自己去加载这个类,而是把请求交给父级类加载器
- 因此,所有的类加载的请求最终都会传送到顶层的"启动类加载器"中
- 如果"父级类加载器"无法加载这个类,然后子类加载器再去加载
5.使用类加载器加载配置文件的方式
-
需求:用类加载器加载加载配置文件
-
步骤:
-
在src下面新建一个stu.ini文件,并在其中输入:
name = zhangsan age = 19
-
创建Properties类的集合对象p;
-
使用当前类ClassLoaderDemo2获得Class对象并调用Class类中的getClassLoder()函数;
-
使用类加载器对象loader调用ClassLoader类中的InputStream getResourceAsStream(String name) 返回读取指定资源的输入流
InputStream in = loader.getResourceAsStream("stu.ini") //注意:这里的name是文件的路径:这个路径如果使用相对路径,相对的是src目录
-
十二、反射
0.使用反射技术
- 核心点:Class类
- Class类是什么呢?
- JVM中只能执行.class字节码文件(java程序中会存在大量的.class文件)
- .class文件是通过类加载器读取到内存中,并基于这个.class文件创建出:Class对象
- Class对象,就是指一个.class文件
- Class类的作用:
- 通过Class对象,获取"构造器"、“成员方法”、“成员变量”
- Class类是什么呢?
- 步骤:
- 获取Class对象
- 基于Class对象,可以抽取:构造器、成员方法、成员变量
- 构造器:Constructor类
- 成员方法:Method类
- 成员变量:Field类
- 使用构造器,调用 newInstance( )方法,来实例化对象
- 使用成员方法,调用 invoke( )方法,来调用方法入栈执行
- 使用成员变量,调用 set( )方法、get( )方法,对变量进行赋值、取值
1.概述
- 反射是指对于任何一个Class类,在"运行的时候"都可以直接得到这个类全部成分。
- 在运行时,可以直接得到这个类的构造器对象:Constructor(创建对象)
- 在运行时,可以直接得到这个类的成员变量对象:Filed(赋值、取值)
- 在运行时,可以直接得到这个类的成员方法对象:Method(调用方法)
- 这种运行时动态获取类信息以及动态调用类中成分的能力成为Java语言的反射机制。
- 是针对Class对象进行操作的
- 大白话:不使用new关键字,可以实例化对象,可以访问对象中的成员
2.反射的关键:
- 反射的第一步都是先得到编译后的Class对象,然后就可以得到Class的全部成分
HelloWorld.java -> javac -> HelloWorld.class
Class c = HelloWorld.class;
3.获取Class对象的三种方式
//第一种
Class cls = 类名.class; //当在方法区创建了.class文化的class对象后,就可以使用
//第二种
Class cls = Class.forName("类的全限定名[带有包名的类]");
//com.mysql.jdbc.Driver
//第三种
Class cls = 对象名.getClass() //在创建具体的对象后,通常对象名获取Class对象
//读取配置文件:Class.forName()
public void testGetClass() throws IOException,ClassNotFoundException{
Properties properties = new Properties();
properties.load(ClassDemo2.class.getClassLoader()
.getResourceAsStream("student.ini"));
String className = properties.getProperty("className");
Class cls = Class.forName(className);
System.out.println(cls);
}
//参数为对象时: 对象名.getClass()
public void method(Student stu){
Class stuClass = stu.getClass();
System.out.println(stuClass);
}
//明确类名后:类名.class
public void testGetClass3(){
Class<Student> studentClass = Student.class;
System.out.println(studentClass);
}
4.Constructor对象
- 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象
- Class类中用于获取构造器的方法
Constructor c = Class对象.getConstructor();//无参构造器
Constructor c = Class对象.getConstructor(String.class);//有参构造器
方法 | 说明 |
---|---|
Constructor<?>[] getConstructors() | 返回所有构造器对象的数组(只能拿public的) |
Constructor<?>[] getDeclaredConstructors() | 返回所有构造器对象的数组,存在就能拿到 |
Constructor getConstructor(Class<?>… parameterTypes) | 返回单个构造器对象(只能拿public的) |
Constructor getDeclaredConstructor(Class<?>… parameterTypes) | 返回单个构造器对象,存在就能拿到 |
- 获取构造器的作用依然是初始化一个对象返回
- Constructor类中用于创建对象的方法
符号 | 说明 |
---|---|
T newInstance(Object… initargs) | 根据指定的构造器创建对象 |
public void setAccessible(boolean flag) | 设置为true,表示取消访问检查,进行暴力反射 |
继承中,父类私有的内容是 可以继承的,但是由于java语言有权限检查过滤,故:不允许权限外的内容(私有内容,子类不能访问)。
Class对象中存储有private构造器,由于java语言有权限检查过滤,故:不允许访问私有构造器。
5.Method对象
反射技术:成员方法
获取Class对象;
基于Class对象,获取成员方法对象(Method);
Method m = Class对象.getMethod("方法名",方法参数类型 );
通过Method对象,运行方法;
Object returnResult = Method对象.invoke(实例对象,方法的实参...)
//如果是静态方法,不用实例对象,在"实例对象的位置"变成null
Method对象.invoke(null,方法的实参...)
- Class类中用于获取成员方法的方法
方法 | 说明 |
---|---|
Method[] getMethods() | 返回所有成员方法对象的数组(只能拿public的) 获取本类中的public修饰的方法,以及父类中的方法 |
Method[] getDeclaredMethods() | 基于Class对象,获取到本来所有的方法(包含私有) |
Method getMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象(只能拿public的) |
Method getDeclaredMethod(String name, Class<?>… parameterTypes) | 返回单个成员方法对象,存在就能拿到 |
- Method类中用于触发执行的方法
方法 | 说明 |
---|---|
Object invoke(Object obj, Object… args) | 运行方法 参数一:用obj对象调用该方法,如果是静态方法写null 参数二:调用方法的传递的参数(如果没有就不写) 返回值:方法的返回值(如果没有就不写) |
6.Field对象
反射技术:成员变量
获取Class对象;基于Class对象获得Field对象;赋值或者获取值
- 使用反射技术获取成员变量对象并使用
- 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
- Class类中用于获取成员变量的方法
Field f = Class对象.getDeclaredField("属性名")
//针对私有成员:成员变量、成员方法、构造器,需要取消权限检查
f.setAccessible(true); //true表示关闭本次权限检查
方法 | 说明 |
---|---|
Field[] getFields() | 返回所有成员变量对象的数组(只能拿public的) |
Field[] getDeclaredFields() | 返回所有成员变量对象的数组,存在就能拿到 |
Field getField(String name) | 返回单个成员变量对象(只能拿public的) |
Field getDeclaredField(String name) | 返回单个成员变量对象,存在就能拿到 |
- 获取成员变量的作用依然是在某个对象中取值、赋值
方法 | 说明 |
---|---|
void set(Object obj,Object value); | 赋值 |
Object get(Object obj) | 获取值 |
十三、注解
单独使用注解没有任何意义,通常会结合反射技术一起使用;
1.注解概述
- Java注解(Annotation)又称Java标注,是JDK5.0引入的一种注释机制。
- Java语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。
2.注解的作用
- 对Java中类、方法、成员变量做标记、然后进行特殊处理,至于到底做何种处理由业务需求来决定。
- 例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。
- 注解可以书写在:
- 类、接口、自定义注解、构造器、成员方法、成员变量、参数
3.自定义注解
自定义注解就是自己做一个注解来使用
public @interface 注解名称{
public 属性类型 属性名() default 默认值;
}
//属性类型:八种数据类型、String、Class、注解类型、枚举类
// 以上所有类型的一维数组形式。例:String[]、Class[]
4.特殊属性
- value属性,如果只有一个value属性的情况下,使用value属性的时候可以省略value名称不写
- 但是如果有多个属性,且多个属性没有默认值,那么value名称是不能省略的
5.元注解
-
元注解:就是修饰注解的注解,书写在自定义注解上的注解(JDK提供的)
-
常见的元 注解有两个:
- @Target:限定自定义注解书写的位置; @Target中可使用的值定义在ElementType枚举类中,常用值如下:
- TYPE,类,接口
- FIELD,成员变量
- METHOD,成员方法
- PARAMETER,方法参数
- CONSTRUCTOR,构造器
- LOCAL_VARIABLE,局部变量
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Test{ }
- @Retention:设置自定义注解的生命周期; @Retention中可使用的值定义在RetentionPolicy枚举类中,常用值如下:
- SOURCE:注解只作用在源码阶段,生成的字节码文件中不存
- CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在(默认值)
- RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用 )
- @Target:限定自定义注解书写的位置; @Target中可使用的值定义在ElementType枚举类中,常用值如下:
6.注解的解析
- 判断某个位置上是否存在自定义注解
- 存在:对自定义注解进行解析
- 注解的操作中经常需要解析解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
- 与注解解 析相关的接口
- AnnotatedElement:该接口定义了与注解解析相关的解析方法;
方法 | 说明 |
---|---|
Annotation[] getDeclaredAnnotations() | 获得当前对象上使用的所有注解,返回注解数组 |
T getDeclaredAnnotation(Class< T > annotationClass) | 根据注解类型获得对应注解对象 |
boolean isAnnotationPresent(Class annotationClass) | 判断当前对象是否使用了指定的注解,如果使用了则返回true,否则返回false |
//构造器对象
Constructor cons = null;
//成员方法对象
Method method = null;
//成员变量对象
Field field = null;
//以上三个对象都有实现:AnnotatedElement接口,并重写相关方法
//判断某个对象上是否有注解
isAnnotationPresent(注解类.class)
//获取某个对象上的注解对象
getDeclaredAnnotation(注解类.class)
十四.动态代理
代理就是被代理者没有能力或者不愿意去完成某件事情,需要找个人代替自己去完成这件事,动态代理就是用来对业务功能(方法)进行代理的。
代理程序中的某个类中的功能,为该功能进行增强。
动态代理,提供了一个代理的对象,有了代理对象后,当访问某个方法时,会被代理对象拦截(拦截后可以对方法进行前增强、后增强【代理对象不会破坏原有方法的代码】)
1.动态代理实现的关键步骤
- 被代理类(例:UserServiceImpl),必须有实现接口
- 创建被代理类对象(例:new UserServiceImpl),交给代理对象使用
2.动态代理的特点:
- 动态的创建.class文件(创建一个和被代理类实现相同父接口的子类【代理类】)
- 动态的加载.class文件到内存中(创建Class对象)
- Proxy类需要一个"类加载器"
- 对程序员来讲,不需要书写代理类
3.动态代理的实现:
代理对象 = Proxy.newProxyInstance(类加载器、父接口、处理器)
/*
类加载器:动态的加载.class文件
父接口: 代理类和被代理类需要拥有共同的父接口
处理器:代理对象拦截了方法后,对方法进行前增强、后增强,是由处理器来书写逻辑
*/
- JDK已经提供了现成的代理对象的生成
- Proxy类
- 静态方法:newProxyInstance(类加载器、接口数组、处理器)
- Proxy类
package com.itheima.demo2;
import org.junit.Before;
import org.junit.Test;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
//处理器
class MyInvocationHander implements InvocationHandler{
private UserServiceImpl userService;
public MyInvocationHander(UserServiceImpl userService) {
this.userService = userService;
}
public MyInvocationHander() {
}
/**
*
* @param proxy 代理对象(不使用)
* @param method 方法对象 (被代理类中所书写的每一个成员方法)
* @param args 成员方法中的参数
* @return 方法方法执行后的结果
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//记录开始时间
long beginTime = System.currentTimeMillis();
//使用Method对象,执行原有方法(被代理类)功能
Object result = method.invoke( userService , args);
//记录结束时间
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+ "执行消耗:"+(endTime-beginTime)+"毫秒");
return result;
}
}
//测试类
public class UserServiceImplTest {
@Test
public void testDeleteUserById(){
//创建: 被代理对象
UserServiceImpl userService = new UserServiceImpl();
//创建一个和UserServiceImpl类实现相同父接口的子类,并生成子类对象
//创建:代理对象
UserService proxyObj = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),//类加载器
userService.getClass().getInterfaces(), //父接口(数组)
//处理器(拦截方法的执行)
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//记录开始时间
long beginTime = System.currentTimeMillis();
//执行被代理对象中的方法(原方法执行)
Object result = method.invoke(userService, args);
//记录结束时间
long endTime = System.currentTimeMillis();
System.out.println(method.getName()+ "执行消耗:"+(endTime-beginTime)+"毫秒");
return result;
}
}
);
//使用代理对象,调用方法执行(方法执行时:会被代理对象中的处理器拦截)
proxyObj.deleteUserById(1);
}
@Test
public void testProxy(){
//前置 : UserServiceImpl有实现接口
//创建: 被代理类对象
UserServiceImpl userService = new UserServiceImpl();
//被代理类实现的所有接口
//Class[] interfaces = { UserService.class };
//注意:当一个类实现的接口比较多时,使用以上方式,书写代码就繁琐了
//简化代码: 获取被代理类对象的Class对象 , 基于Class对象获取所实现的所有接口
//Class[] interfaces = userService.getClass().getInterfaces();
//使用JDK提供Proxy创建代理对象
UserService proxyObj = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(), //参数1:类加载器
//参数2:接口数组(被代理类所实现的所有的接口)
userService.getClass().getInterfaces(),
//参数3:处理器(关键)
new MyInvocationHander( userService )
);
//使用代理对象,调用方法
List<User> userList = proxyObj.findAllUser();
for (User user : userList) {
System.out.println(user);
}
// proxyObj.deleteUserById(1);
}
}
- Proxy创建一个子类对象
- 子类:必须和被代理类实现相同的父接口
- 子类编译后是一个.class文件,需要类加载器,加载.class文件到内存中
4.动态代理的优点
- 非常灵活,支持任意接口类型的实现类对象做代理,也可以直接为接口本身做代理
- 可以为被代理对象的所有方法做代理
- 可以在不改变方法源码的情况下,实现对方法功能的增强
- 不仅简化了编程工程、提高了软件系统的可扩展性,同时也提高了开发效率
十五、XML
1.XML是什么?
- 了扩展标记语言
- 标记:标签。例:
- xml文件是由N多个标签组成的
- 注意作用:
- 存储数据
- 配置文件(主流)
2.XML组成:
- 注释
- XML的注释与HTML相同,以 结束。不能嵌套
<!--注释内容-->
<!--
注释内容1
注释内容2
-->
-
声明:第1行的固定格式 <?xml version="1.0" encoding = "UTF-8"?>
-
元素(标签、标记)
-
空元素:只有标签,而没有结束标签,但元素必须自己闭合 例:
-
有且只能有一个根元素
-
元素命名:
-
区分大小写
-
不能使用空格,不能使用冒号
-
不建议以XML、Xml、xml开头
-
标签名不能数字开头,可以没有数字
-
可以使用下划线
可以保持与Java命名标识符一样的规则
-
-
格式1:<person> 标签体 </person> 有标签体的标签 (双标签)
格式2: <person/> 没有标签体的标签 (单标签)
元素体:可以是元素也可以是文本
<person>
标签中可以包含另一个标签
<name>张三</name>
</person>
- 属性
- 属性是元素的一部分,它必须出现在元素的开始标签中
- 属性的定义格式:属性名= “属性值”,其中属性值必须使用单引或双引号括起来
- 一个元素可以有0~N个属性,但一个元素中不能出现同名属性
- 属性名不能使用空格,建议不要使用冒号等特殊字符,且必须以字母开头,建议以Java的标识符定义规则做参考
<person id ="110">
- 转义字符【实体字符】
- 注意:严格地讲,在 XML 中仅有字符 “<“和”&” 是非法的。省略号、引号和大于号是合法的,但是把它们替换为实体引用是个好的习惯。
字符 | 预定义的转义字符 | 说明 |
---|---|---|
< | < | 小于(less than) |
> | > | 大于(greater than) |
" | " | 双引号(quotation) |
’ | ' | 单引号(apostrophe) |
& | & | 和号(ampersand ) |
转义字符应用示例:
假如您在 XML 文档中放置了一个类似 “<” 字符,那么这个文档会产生一个错误,这是因为解析器会把它解释为新元素的开始。因此你不能这样写:
<message>if salary < 1000 then </message>
为了避免此类错误,需要把字符 “<” 替换为实体引用,就像这样:
<message>if salary < 1000 then</message>
- CDATA字符区
- CDATA指的是不应由XML解析器进行解析的文本数据(Unparsed Character Data)
- CDATA部分由<![CDATA[ 开始, ]]>结束;
- 当大量的转义字符出现在xml文档中时,会使XML文档的可读性大幅度降低。这时如果使用CDATA段就会好一些。CDATA(Character Data)字符数据区,格式如下:
<![CDATA[
文本数据 < > & ; ""
]]>
3.XML约束:DTD、Schema
限制XML文件中可以书写的内容(必须按照限制书写内容,不能随意编写)
- DTD
(1)新建dtd文件
<!ELEMENT 书架 (书+)> <!-- 声明了根元素,并指定了根元素下必须有的一个子元素 -->
<!-- 声明子元素 书 -->
<!ELEMENT 书 (书名,作者,售价)><!--约束元素书的子元素必须为书名、作者、售价-->
<!-- , 表示必须按照元素的顺序出现 -->
<!ELEMENT 书名 (#PCDATA)> <!-- #PCDATA : 表示文本内容 -->
<!ELEMENT 作者 (#PCDATA)>
<!ELEMENT 售价 (#PCDATA)>
(2)在xml中引入dtd文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE 书架 SYSTEM "bookshelf.dtd"><!--指定使用bookshelf.dtd文件约束当前xml文档-->
<书架>
<书>
<书名>JavaWeb开发教程</书名>
<作者>张孝祥</作者>
<售价>100.00元</售价>
</书>
<书>
<书名>三国演义</书名>
<作者>罗贯中</作者>
<售价>100.00元</售价>
<测试>hello</测试><!--不符合约束,书的子元素必须为书名、作者、售价-->
</书>
</书架>
-
Schema
-
Schema 语言也也叫做 XSD(XML Schema Definition)。
-
其本身也是XML格式文档,但Schema文档扩展名为xsd,而不是xml。
-
Schema 功能更强大,数据类型约束更完善。 比DTD强大,是DTD代替者。
-
Schema为了防止相同的元素名或属性名,提供了"名称空间"来区分不同名称空间下的元素或属性 例:xmlns : 别名 = “域名”
<别名 : 元素名 > </别名 : 元素名 >
-
-
Schema约束体验
体验效果说明:体验schema约束XML文档中对元素体数据类型的约束。
效果如下:
DTD约束无法对具体数据类型进行约束,所以开发工具没有任何错误提示,如下效果:
- Schema实现步骤
步骤1:复制schema约束文件bookshelf.xsd,其中已对售价约束了数据类型,代码如下
<?xml version="1.0" encoding="UTF-8" ?>
<!--
传智播客schema教学实例文档.将注释中的以下内容复制到要编写的xml的声明下面
复制内容如下:
<书架 xmlns="http://www.itcast.cn"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.itcast.cn bookshelf.xsd"
>
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.itcast.cn"
elementFormDefault="qualified">
<xs:element name='书架' >
<!-- complexType : 复杂元素
xml文档中的某个标签,有子标签或有属性,该标签就为:复杂元素
例: <书架> <书>
简单元素:
没有子标签、没有属性。 例:书名、作者、售价
-->
<xs:complexType>
<xs:sequence maxOccurs='unbounded' >
<xs:element name='书' >
<xs:complexType>
<xs:sequence>
<xs:element name='书名' type='xs:string' />
<xs:element name='作者' type='xs:string' />
<xs:element name='售价' type='xs:double' />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
步骤2:新建books2.xml使用schema约束文件bookshelf.xsd,代码如下
<?xml version="1.0" encoding="UTF-8"?>
<书架
xmlns="http://www.itcast.cn"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.itcast.cn bookshelf.xsd"
><!--指定schema文档约束当前XML文档-->
<书>
<书名>JavaScript网页开发</书名>
<作者>张孝祥</作者>
<售价>abc</售价>
</书>
</书架>
步骤3:开发工具提示效果
- Schema约束名称空间:
一个XML文档最多可以使用一个DTD文件,但一个XML文档中使用多个Schema文件,若这些Schema文件定义了相同名称的元素时,使用的时候就会出现名字冲突。就像一个Java文件中使用了==import java.util.和import java.sql.==时,在使用Date类时,那么就不明确Date是哪个包下的Date了。
同理,在XML文档中就需要通过名称空间(namespace)来区分元素和属性是来源于哪个约束中的。
名称空间就在根元素后面的内容,使用xmlns到引入约束。
当一个XML文档中需要使用多个Schema文件的时候,有且仅有一个使用缺省的,其他的名称空间都需要起别名。
参考资料中的applicationContext.xml文件(spring框架的配置文件)
xmlns="http://www.itcast.cn"
<!-- 缺省的名称空间,使用此约束中的元素的时候只需要写元素名即可 例如:<书></书>-->
xmlns:aa="http://java.sun.com"
<!-- aa就是此约束的别名,使用此约束中的元素的时候就需要加上别名 例如:<aa:书></aa:书>-->
总之名称空间就是用来处理元素和属性的名称冲突问题,与Java中的包是同一用途。如果每个元素和属性都有自己的名称空间,那么就不会出现名字冲突问题,就像是每个类都有自己所在的包一样,那么类名就不会出现冲突。
4.XML解析
- 解析概述:
- 当将数据存储在XML后,我们希望通过程序获取XML的内容。我们使用Java基础所学的IO知识是可以完成的,不过需要非常繁琐的操作才可以完成,且开发中会遇到不同问题(只读、读写)
- 人们为不同问题提供不同的解析方式,使用不同的解析器进行解析,方便开发人员操作XML。
- 解析方式:
- DOM:要求解析器把整个XML文档加载到内存,并解析成一个Document对象
- 优点:元素与元素之间保留结构关系,故可以进行增删改查操作。
- 缺点:XML文档过大,可能出现内存溢出
- SAX:是一种速度更快,更有效的方法。它逐行扫码文档,一边扫描一边解析。并以事件驱动的方式进行具体解析,每执行一行,都触发对应事件。
- 优点:处理速度更快,可以处理大文件
- 缺点:只能读,逐行后将释放资源,解析操作繁琐
- PULL:Android内置的XML解析方式,类似SAX(了解)
- DOM:要求解析器把整个XML文档加载到内存,并解析成一个Document对象
- 解析器:
- 解释根据不同的解析方式提供具体实现。有的解析器操作过于繁琐,为了方便开发人员,有提供易于操作的解析开发包。
5.DOM解析原理和DOM树模型
- Dom4j工作原理
- 把整个xml文件读取到内存中,并基于xml文件结构生成一个树结构(dom树),在生成dom树后会创建Document对象,利用Document对象就可以对dom树中的内容进行操作(读、写)
6.Dom4j常用API介绍
Dom4j必须使用核心类SaxReader加载xml文档获得Document,通过Document对象获得文档的根元素,然后就可以操作了。
-
Dom4j的使用步骤:
-
把Dom4j.jar文件导入到项目工程下,并添加到项目库中
-
使用Dom4j提供的核心类:SaxReader,加载xml文件并创建Document对象(dom树模型就存在了)
-
利用Document对象,获取到Dom树模型中的根元素,
Element rootElement = document.getRootElement()
-
从根元素,就可以获取其下的子元素;通过子元素,可以获取其下的子元素;通过子元素,可以获取其下的文本内容。
-
SAXReader对象
方法 | 作用 |
---|---|
SAXReader sr = new SAXReader(); | 构造器 |
Document read(String url) | 加载执行xml文档 |
Document对象
方法 | 作用 |
---|---|
Element getRootElement() | 获得根元素 |
Element对象
方法 | 作用 |
---|---|
List elements(String ele) | 获得指定名称的所有子元素,可以不指定名称 |
Element element(String ele) | 获得指定名称第一个子元素 |
String getName() | 获得当前元素的元素名 |
String attributeValue(String attrName) | 获得指定属性名的属性值 |
String elementText(Sring ele) | 获得指定名称子元素的文本值 |
String getText() | 获得当前元素的文本内容 |
String text = element对象.getText()//获取当前元素对象下的文本内容
String text = 父元素对象.elementText("子元素名字")
7.Dom4j结合XPath解析XML(了解)
底层还是基于Dom4j(把Dom4j进行二次封装);
使用"路径表达式"来解析XML文件
- Xpath使用步骤
- 导入jar包(dom4j和jaxen-1.1-beta-6.jar)
- 通过dom4j的SaxReader获取Document对象
- 利用Xpath提供的api,结合xpath的语法完成选取XML文档元素节点进行解析操作。
Node接口中存在以下方法:
方法 | 作用 |
---|---|
List selectNodes(“路径表达式”) | 获取符合表达式的元素集合 |
Element selectSingleNode(“路径表达式”) | 获取符合表达式的唯一元素 |
- Xpath表达式,就是用于选取XML文档中节点的表达式字符串。获取XML文档节点元素一共有如下4种XPath语法方式:
- 绝对路径表达式:/根元素/子元素/子子元素/…
- 相对路径表达式:./子元素/子子元素/…(拿某个元素作为参照)
- 全文搜索路径表达式://元素/子元素 (在整个xml文档中检索) 例://name
- 谓语(条件筛选)表达式://元素[@属性=属性值] 例: //book[@id=0001
十六、Mybatis
1.Maven
-
Maven是专门用于管理和构建Java项目的工具,它的主要功能有:
- 提供了一套标准化的项目结构
- 提供了一套标准化的构建流程(编译、测试、打包、发布…)
- 提供了一套依赖管理机制
-
Apache Maven 是一个项目管理和构建工具,它基于项目对象模型(POM)的概念,通过一小段描述信息来管理项目的构建、报告和文档。
-
Maven模型
- 仓库的分类:
- 本地仓库:自己计算机上的一个目录
- 中央仓库:由Maven团队维护的全球唯一的仓库
- 远程仓库(私服):一般由公司团队搭建的私有仓库
- 当项目中使用坐标引入对应依赖jar包后,首先会查找本地仓库中是否有对应的jar包;
- 如果有,则在项目直接引用;
- 如果没有,则去中央仓库中下载对应的jar包到本地仓库。
- 还可以搭建远程仓库,将jar包的查找顺序变为:
- 本地仓库 -> 远程仓库->中央仓库
2.安装配置步骤
- 解压 apache-maven-3.6.1.rar即完整完成
- 配置环境变量MAVEB_HOME为安装路径的bin目录
- 配置本地仓库:修改conf/setting.xml中的 为一个指定目录
- 配置阿里云私服:修改conf/setting.xml中的< mirrors > 标签,为其添加如下子标签
<mirror>
<id>nexus-aliyun</id>
<mirrorOf> * </mirrorOf>
<name> Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
3.Maven坐标
- 什么是坐标?
- Maven中的坐标是资源的唯一标识
- 使用坐标来定义项目或引入项目中需要的依赖
- Maven坐标主要组成
- groupId:定义当前Maven项目隶属组织名称(通常是域名反写,例如:com.itheima)
- artifactId:定义当前Maven项目名称(通常是模块名称,例如:order-service、goods-service)
- version:定义当前项目的版本号
4.Maven基本使用
- 常用命令
- compile:编译
- clean:清理
- test:测试
- package:打包
- install:安装
- 生命周期
- Maven构建项目生命周期描述的是一次构建过程经历多少个事件
- Maven对项目构建的生命周期划分为3套
- clean:清理工作
- default:核心工作、例如,编译、测试、打包、安装等
- site:产生报告,发布站点等
-
注意事项:
-
清理Maven仓库:
初始情况下,我们的本地仓库没有任何jar包,此时会从私服去下载(如果没有配置,就直接从中央仓库去下载),可能由于网络的原因,jar包下载不完全,这些不完整的jar包都是以lastUpdated结尾。此时,maven不会在重新帮你下载,需要你删除这些以lastUpdated结尾的文件。如果本地仓库中有很多这样的以lastUpdated结尾的文件,可以知晓如下脚本来删除:(Maven清理插件)
-
5.MyBatis
- 什么是Mybatis?
Mybatis是一款优秀的持久层框架,用于简化JDBC开发
- 持久层:
- 负责将数据保存到数据库的那一层代码
- JavaEE三层架构:表现层、业务层、持久层
- 框架:
- 框架就是一个半成品软件,是一套可重用的、通用的、软件基础代码模型
- 在框架的基础之上构建软件编写更加高效、规范、通用、可扩展
-
MyBatis整体架构
- MyBatisConfig.xml:属于mybatis的核心配置文件,一个mybatis项目只能有一个核心配置文件;文件名随意;文件必须放到resources下面;在核心配置文件中主要有连接数据库的四大参数以及加载mybatis框架的映射文件。
- Mapper.xml :表示mybatis的映射文件;一个mybatis项目中可以有多个映射文件;映射文件中书写的是sql语句;
- sqlSessionFactory:会话对象,使用工厂创造类SqlSessionFactoryBuilder创建工厂创造类对象;SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(字节流); 获取会话对象
- SqlSession:表示会话对象,底层封装的是Connection连接对象
- Executor(执行器):使用SqlSession会话对象调用执行器Executor,使用执行器执行sql语句
-
MyBatis使用步骤:
- 创建user表,添加数据;
- 创建maven工程,导入坐标;
- 编写MyBatis核心配置文件 --> 替换连接信息,解决硬编码问题;
- 定义与SQL映射文件同名的Mapper接口,并且将Mapper接口和SQL映射文件放置在同一目录下;
- 编写SQL映射文件,设置SQL映射文件的namespqce属性为Mapper接口全限定名;
- 在Mapper接口中定义方法,方法名就是SQL映射文件中sql语句的id,并保持参数类型和返回值类型一致;
- 编码
6.MyBatis核心配置文件
-
properties标签:加载外部的资源配置文件
- 属性:resource 指定要引入的外部配置文件路径
- 通过 ${key} 方式引入外部配置文件中的数据
-
settings标签:mybatis中的核心设置标签(功能比较多,例:缓存、驼峰映射)
-
子标签:setting
-
属性:name 设置名
value 设置的开关键
-
-
-
typeAliases标签 :起别名(长名字变为短名字)
-
子标签:package (要扫哪个包) 包下的类全部起个别名。 格式:类名写或保持原类名
- 属性:name:指定要扫描的包
-
-
typeHandlers(类型处理器)【了解】
-
MyBatis在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时,都会采用类型处理器将获取到的值以合适的方式转换成java类型。
-
-
environments【了解】
- MyBatis可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置;尽管可以配置多个环境,每个SqlSessionFactory实例只能选择其一。虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境分离。
-
mappers(映射器)
- Mappers标签作用:提供了关联加载xml映射文件的配置功能;
- 使用方式:
- 加载XML映射文件,关联UserMapper.java接口
- 【1】从resource下加载映射文件;说明:如果项目采用基于xml的开发模式,建议使用方式【1】开发;
- 加载接口,关联映射文件
- 条件:1.接口名和映射文件名保持一致;2.路径保值一致;
- 【2】批量加载class: 说明:如果基于注解开发的话,推荐使用方式【2】开发
- 加载XML映射文件,关联UserMapper.java接口
7.MyBatis映射配置文件
用于编写SQL代码
-
select标签
属性名 说明 是否必须 id 当前sql语句的唯一标记(和Mapper接口的方法名一致) 是 parameterType 入参类型 否 resultType/resultMap(只能存在一个) 返回值类型 是 -
insert标签
属性 说明 是否必须 id 这条SQL语句的唯一标识,和接口的方法名一致 是 - 说明:#{username},#{birthday},#{sex},#{address}大括号里面的值必须和pojo的实体类User类中的属性名一致,否则会报错。
- 注意:MyBatis默认事务手动提交,可设置事务自动提交:
- SqlSession sqlSession = sqlSessionFactory.openSession(true);
- 或者手动提交,sqlSession.commit();
-
update标签
属性 说明 是否必须 id SQL语句的唯一标识,和接口的方法名一致 是 -
delete标签
属性 说明 是否必须 id SQL语句的唯一标识,和接口的方法名一致 是
8.编写会话工具类
- 步骤:
- 在静态代码块中创建会话工厂对象
- 编写静态方法得到会话对象,设置自动提交事务
- 编写静态方法得到会话对象,方法接收调用者传递的布尔类型,决定是否自动提交事务
- 编写静态方法接收会话对象,手动提交事务并且关闭会话
- 编写静态方法接收会话对象,回滚事务并且关闭会话
9.实体类属性名和数据库列名不一致,不能自动封装数据
- 起别名:在SQL语句中,对不一样的列名起别名,别名和实体类名属性名一样
- 可以定义 片段,提升复用性
- resultMap:定义 完成不一致的属性名和列名的映射
10.在Mybatis中使用的占位符:
#{key} :在映射文件中作为sql的占位符参数使用
${key} :在核心配置文件中获取外部配置文件中的数据;
在映射配置文件中作为sql的占位符参数使用
(注意细节:需腰使用@Param("参数")声明参数名)
#{key} 和 ${key} 的区别:
#{key}:底层是使用PreparedStatement对象,对sql进行预编译,使用占位符。
好处:防止sql注入。
${key}:底层是使用Statement对象,对SQL进行拼接。 会有SQL注入风险
11.SQL语句设置多个参数的几种方式:
- 散装参数:需要使用@Param(“SQL中的参数名称”) 使用麻烦
- 实体类封装参数:只需要保证SQL中的参数名和实体类属性名对应上,即可设置成功
- map集合:只需要保证SQL中的参数名和map集合的键的名称对应上。即可成功
12.动态SQL
-
where标签:代替sql语句中的where关键字;可以自动去除sql语句中where条件里面多余的or或and关键字
-
多条件查询
<select> .... <where> <if test="...!=NULL"> AND ...=#{...} </if> <if test="...!=NULL"> AND ...=#{...} </if> <if test="...!=NULL"> AND ...=#{...} </if> </where> </select>
-
单条件查询
<select> .... where <chose> <when test="..!=NULL">...=#{...}</when> <when test="..!=NULL">...=#{...}</when> <otherwise> 1=1 </otherwise> </chose> </select>
-
-
set标签:代码sql语句中的set关键字(针对update操作);可以自动除sql语句中多余的逗号;
<!--使用set元素动态修改一个网站记录 --> <update id="updateWebsite" parameterType="net.biancheng.po.Website"> UPDATE website <set> <if test="name!=null">name=#{name},</if> <if test="url!=null">url=#{url},</if> </set> WHERE id=#{id} </update>
-
foreach标签:遍历容器,获取容器中的元素,拼接为sql
<foreach collection="集合/数组" item="从容器中取出的每一个元素" separator="元素之间的分隔符号" open="以x作为开始" close="以x作为结束"> </foreach>
<!--删除数据:批量删除--> <delete id="deleteBrandByIds"> delete from tb_brand where id in <foreach collection="ids" item="id" separator="," open="(" close=")"> #{id} </foreach> </delete>
13.主键回填
向表中插入一行记录后,会自动把当前行的主键列下的数据值获取;
<insert id= "",useGeneratedKeys="true", keyProperty="id">
insert into ...
</insert>
14.MyBatis高级查询
(1)mybatis多表查询套路
- 基于需求编写SQL语句
- 基于SQL语句的查询结果,分析类与类之间关联(建立实体类和实体类的关联)
- 1对1查询结果:在Order类中添加新属性:User对象
- 1对多查询结果:在User类中添加新属性:List集合
- 在映射文件中,基于SQL查询结果,配置映射关联
(2)一对一映射语法格式:
<resultMap id="映射ID" type="主表实体名称" autoMapping="true">
<!--添加主表与主表实体映射-->
......
<!--association: 配置关联对象(User)的映射关系,一般与resultMap标签联合适应-->
<association property="主表实体中对应从表的属性名称" javaType="从表实体类型" autoMapping="true">
<!--添加从表中字段与实体属性映射关系-->
<id column="user_id" property="id"></id>
<result column="" property=""></result>
</association>
</resultMap>
(3)一对多映射语法格式
<resultMap id="映射ID" type="主表实体名称" autoMapping="true">
<!--添加主表与主表实体映射-->
......
<!--collection:映射实体类中List<Order>集合使用功能Collection标签-->
<collection property="主表实体中对应从表的属性名称" javaType="list" ofType="Order" autoMapping="true">
<!--添加从表中字段与实体属性映射关系-->
<id column="user_id" property="id"></id>
<result column="" property=""></result>
</collection>
</resultMap>
15.mybatis注解开发
(1)概述
注解配置的方式在很多情况下能够取代mybatis的映射文件,提高开发效率。
(2)CURD相关注解
-
@insert:插入
-
Value:sql语句(和xml的配置方式一模一样)
public interface UserMapper { @Insert("insert into tb_user values(null,#{userName},#{password},#{name}," +"#{age},#{sex})") Integer addUser(User user);}
-
-
@Update:更新
-
Value:sql语句
@Update("update tb_user set user_name=#{userName},password=#{password}," +"name=#{name},age=#{age},sex=#{sex} where id=#{id}") Integer updateUser(User user);
-
-
@Delete:删除
-
Value:sql语句
@Delete("delete from tb_user where id=#{id}") Integer deleteByUserId(@Param("id") Integer id);
-
-
@Select:查询
-
Value:sql语句
@Select("select * from tb_user") List<User> findAll();
-
-
@Options:可选配置(获取主键)
- userGenerateKeys:开关,值为true表示可以获取主键 相当于select last_insert_id()
- keyProperty:对象属性
- keyColumn:列名
(3)主键回填
@Insert("insert into tb_user values(null,#{userName},#{password},#{name},"
+"#{age},#{sex})")
@Options(useGeneratedKeys = true,keyColumn = "id" ,keyProperty = "id")
Integer addUserAndGetFk(User user);
(4)注解实现别名映射
public @interface Result {
//对应数据表的列
String column() default "";
//对应pojo类的属性
String property() default "";
//javaType:返回的对象类型
}
@Select("select * from tb_user where id=#{id}")
@Results(id="userMap",
value = {
@Result(column = "id",property = "id",id=true),
@Result(column = "user_name",property = "userName")
})
User findById(Long id);
(5)模糊查询
使用 @SelectProvider 注解,注解中的type 参数是提供构建 SQL 的类,method 是构建 SQL 的方法。构建 SQL 的方法的参数要和接口的参数一致,并且多个参数要使用@Param命名参数。
@SelectProvider(type = SqlProvider.class,method = "findUserByName")
List<User> findUserByName2(@Param("uName") String name);
public class SqlProvider {
public String findUserByName(@Param("uName") String name){
String sql="select * from tb_user where sex=1";
if(name!=null){
sql+=" and user_name like concat('%',#{uName},'%')";
}
return sql;
}
}
面试题:
1.非空约束 + 唯一约束与主键约束的区别?
- 主键约束在表中只能存在一个,但是非空+唯一约束可以存在多个
- 主键可以添加自增约束,但是非空+ 唯一约束不能;
- 主键约束底层维护了一个主键索引,而唯一约束底层维护的是唯一索引;
2.事务的并发访问引发的三个问题:
事务在操作时的理想状态:多个事务之间互不影响,如果隔离级别设置不当就可能引发并发访问问题。
- 脏读:一个事务读取到了另一个事务中尚未提交的数据。
- 不可重复读:一个事务中多次读取时数据是不一致的,这是事务update时引发的问题;
- 幻读(虚读):一个事务内读取到了别的事务插入或者删除的数据,导致前后读取记录行数不同,这是insert或delete时引发的问题。
3.数据库的事务分类?
手动事务(oracle默认)、自动事务(mysql默认)
4.索引的设计可以遵循一些有已有的原则,创建索引的时候请尽量考虑符合这些原则,便于提升索引的使用效率,更高效的使用索引。
1.字段内容可识别度不能低于70%,字段内数据唯一值的个数不能低于70%
例:一个表数据只有50行,那么性别和年龄哪个字段适合创建索引,明显是年龄,
因为年龄的唯一值个数比较多,性别只有两个选项。性别的识别度50%。男 女
2.经常使用 where 条件搜索的字段,例如user表的id name 等字段。
3.经常使用表连接的字段(内连接、外连接),可以加快连接的速度。
4.经常排序的字段 order by,因为索引已经是排过序的,这样一来可以利用索引的排序,
加快排序查询速度。
* 注意:是不是在数据库表字段中尽量多建索引呢?
不是。因为索引的建立和维护都是需要耗时的。
创建表时,需要通过数据库去维护索引,添加记录、更新、修改时,也需要更新索引,
会间接影响数据库的效率。