MySQL基础语法与JDBC

文章目录

  • 前言
    • (一)什么是SQL
    • (二)什么是MySQL
    • (三)MySQL的体系结构
      • ①连接层
      • ②服务层
      • ③可插拔存储引擎层
      • ④存储层
    • (四)MySQL的启动、停止、连接
  • 一、基础知识
    • (一)基本术语
    • (二)数据类型
    • (三)约束与字段属性
  • 二、SQL语法
    • (一)DDL
      • 数据库操作
      • 表操作
    • (二)DML
      • 添加数据
      • 更新数据
      • 删除数据
    • (三)DQL
      • 基本语法
      • 常用函数
      • 聚合函数
      • 普通查询
      • 条件查询
      • 模糊查询
      • 分组查询
      • 连接查询
      • 子查询
      • 分页查询
      • 排序
    • (四)DCL
  • 三、三大范式、BCNF、反范式与依赖关系
    • (一)三大范式案例
    • (二)BCNF案例
    • (三)范式化的优缺点
    • (四)反范式化的优缺点
  • 四、引擎
    • (一)引擎操作
    • (二)引擎分类
    • (三)存储引擎的选择
  • 五、视图
  • 六、存储过程
    • (一)基础语法
    • (二)IF ELSE
    • (三)CASE
    • (四)while
    • (五)repeat until
    • (六)LOOP
    • (七)后端调用
  • 七、游标
  • 八、事务
    • (一)基本概念
    • (二)基本操作
    • (三)隔离级别
  • 九、JDBC
    • (一)概述
    • (二)常用API
      • ①Driver
      • ②DriverManager
      • ③Connection
      • ④Statement、PreparedStatement、CallableStatement与SQL注入
        • 1.Statement
        • 2.PreparedStatement
      • 3.CallableStatement
        • 4.SQL注入
      • ⑤ResultSet
    • (三)数据库连接池Druid
  • 十、索引与源码
  • 十一、常用数据库案例(含数据)


前言

(一)什么是SQL

  SQL (Structured Query Language) ,是高级的非过程化编程语言,是用于数据操纵和数据定义等多种功能的数据库语言,允许用户在高层数据结构上工作。它不要求用户指定对数据的存放方法,也不需要用户了解具体的数据存放方式,所以具有完全不同底层结构的不同数据库系统可以使用相同的SQL语言作为数据输入与管理的接口。
SQL的分类
1、DDL(Data Definition Language) 数据定义语言,用来操作数据库、表、列等; 常用语句:CREATE、 ALTER、DROP
2、DML(Data Manipulation Language) 数据操作语言,用来操作数据库中表里的数据;常用语句:INSERT、 UPDATE、 DELETE
3、DCL(Data Control Language) 数据控制语言,用来操作访问权限和安全级别; 常用语句:GRANT、DENY
4、DQL(Data Query Language) 数据查询语言,用来查询数据 常用语句:SELECT

(二)什么是MySQL

MySQL基础语法与JDBC_第1张图片
数据库的分类

  • 关系型数据库:SQL(Structured Query Language)
    • 常用关系型数据库:MySQL、Oracle、Sql Server。
    • 特点:通过表与表之间、行和列之间进行数据的存储。
  • 非关系型数据库:NoSQL(Not Only SQL)
    • 常用非关系型数据库:Redis、MongoDB
    • 数据以对象的存储存储在数据库中,并没有表关系的存在。

  MySQL是一个关系型数据库管理系统(DBMS,Database Management System),由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。
  MySQL所使用的 SQL 语言是用于访问数据库的最常用标准化语言。MySQL 软件采用了双授权政策,分为社区版和商业版,由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,一般中小型和大型网站的开发都选择 MySQL 作为网站数据库。
MySQL基础语法与JDBC_第2张图片总结

  • 数据库(DB):数据存储的仓库。
  • 数据库管理系统(DBMS):操纵和管理数据库的大型软件。
  • SQL:操纵关系型数据库的编程语言,是一套标准。关系型数据库,是指采用了关系模型来组织数据的数据库,以行和列的形式存储数据,关系数据库中,这一系列的行和列被称为表,一组表组成了数据库。如:MySQL、Oracle、SQL Server等。
  • NoSQL:非关系型数据库数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定。如:Redis、MongoDB等。
  • MySQL:现在流行的、开源的、免费的关系型数据库。

(三)MySQL的体系结构

MySQL基础语法与JDBC_第3张图片
MySQL自顶向下可分为连接层、服务层、可插拔存储引擎、存储层四层。

①连接层

  网络连接层位于MySQL体系架构的最上层,主要担任客户端服务器的角色,主要完成一些类似于连接处理、授权认证、校验用户和密码等工作,如:Java、C、C++、Python等,各种语言都是通过各自的API接口与MySQL建立连接。

②服务层

  数据库服务层是整个数据库服务器的核心,包括系统管理和控制工具、连接池、提供实现SQL语句的接口、解析器、查询优化器等部分。所有跨存储引擎的功能也在这一层实现,如,过程、函数等。

  • 连接池:负责存储和管理客户端与数据库的连接信息,连接池里的一个线程负责管理一个客户端到数据库的连接信息。
  • 系统管理和控制工具:提供数据库系统的管理和控制功能,如,对数据库中的数据进行备份和恢复,保证整个数据库的安全性,提供安全管理,对整个数据库的集群进行协调和管理。
  • SQL接口:主要负责接收客户端发来的各种SQL命令,并将SQL命令发送到其他部分,并接收其他部分返回的结果数据,再返回给客户端。
  • 解析器:用来验证和解析SQL语言,自身是一个很长的脚本,可将将SQL语句分解为数据结构,并将这个结构传递给后序的步骤。若在分解构成中遇到错误,就说明这个SQL语句是不合理的。
  • 优化器:查询优化器,SQL语句再查询前会使用查询优化器进行优化。
  • 缓存器:MySQL的缓存是由一系列的小缓存组成的。例如:MySQL的表缓存,记录缓存,MySQL中的权限缓存,引擎缓存等。MySQL中的缓存能够提高数据的查询性能,如果查询的结果能够命中缓存,则MySQL会直接返回缓存中的结果信息。

③可插拔存储引擎层

  真正负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信,不同的存储引擎具有不同的功能,我们可以根据自己的需求,来选取合适的存储引擎。

④存储层

  主要将数据存储在文件系统之上,并完成与存储引擎的交互。其存储的文件主要有:日志文件、数据文件、配置文件、MySQL的进行pid文件和socket文件等。

  • 日志文件:错误日志、通用查询日志、二进制日志、慢查询日志等。
      错误日志:存储MySQL运行过程中产生的错误信息,可以使用以下语句来查看MySQL中的错误日志:
show variables like '%log_error%';

MySQL基础语法与JDBC_第4张图片
  通用查询日志:记录MySQL运行过程中的一般查询信息:

show variables like '%general%';

在这里插入图片描述
  二进制日志:记录对MySQL数据库执行的插入、修改和删除操作,并且会记录SQL语句执行的时间、时长,但不记录select、show等不修改数据库的SQL,主要用于恢复数据库的数据和实现MySQL主从复制。

show variables like '%log_bin%';

MySQL基础语法与JDBC_第5张图片
  慢查询日志:记录执行时间超过指定时间的SQL语句,这个时间默认是10s。
查看是否开启慢查询日志

show variables like '%slow_query%';

查看慢查询设置的时长

show variables like '%long_query_time%'
  • 数据文件:主要包括,db.opt文件,frm文件,MYD文件,MYI文件,ibd文件,ibdata文件,ibdata1文件,ib_logfile0和ib_logfile1文件等。
    • db.opt文件:记录当前数据库使用的字符集和检验规则等信息。
    • frm文件:存储数据库表的结构信息,主要是数据库表结构定义信息,每张表都有一个frm文件。
    • MYD文件:MyISAM存储引擎专用的文件格式,主要存放MyISAM存储引擎数据表中的数据,每张MyISAM存储引擎都对应一个.MYD文件。
    • MYI文件:MyISAM存储引擎专用的文件格式,主要存放MyISAM存储引擎数据表相关的引擎信息,每张MyISAM存储引擎表都对应一个.MYI文件。
    • ibd文件:存放InnoDB存储引擎的数据文件和索引文件,主要存放的是独享表空间的数据和索引,每张表对应一个.ibd文件。
    • ibdata文件:存放InnoDB存储引擎的数据文件和索引文件,主要存放的是共享表空间的数据和索引,所有表共用一个或多个.ibdata文件。
  • 配置文件:存放MySQL所有配置信息,Unix/Linux中是my.cnf文件,Windows中是,my.ini文件。
  • pid文件:存放MySQL进程运行时的进程号文件,主要存在于Unix/Linux中。
  • socket文件: socket文件和pid文件一样,都是MySQL在Unix/Linux环境中运行才会有的文件。在Unix/Linux环境中,客户端可以直接通过socket来连接MySQL。

(四)MySQL的启动、停止、连接

关于MySQL的安装此处不再赘述,仅记录以下基本连接操作:
启动

net start mysql80

停止

net stop mysql

连接

mysql [-h 127.0.0.1] [-P 3306] -u root -p

一、基础知识

(一)基本术语

基本概念

  • 实体:客观存在并可以相互区分的事务叫实体
  • 属性:实体所具有的某一特性。
  • 实体型:用实体名以及其属性名集合来抽象和刻画同类实体,称为实体型。
  • 实体集:同一类型实体的集合。
  • 超键(码):能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键。
  • 候选键(候选码):若关系中的某一属性组的值能唯一标识一个元组,而其子集不能,则成为该属性组为候选码。
  • 主键(主码):若一个关系有多个候选码,则选定其中一个为主码。
  • 主属性:候选码的诸属性成为主属性(而非主键的属性)。
  • 非主属性:不包含在任何候选码中的属性称为非主属性(与主键无关,与候选码有关)。
  • 外键:子数据表中出现的父数据表的主键,称为子数据表的外键。
  • 全码:当所有的属性共同构成一个候选码时,这时该候选码为全码。
  • 完整性约束:用于保证数据库中数据的准确性和一致性而对数据库表作出的约束。

关系模型
  常用的数据模型有,层次模型、网状模型、关系模型、面向对象数据模型、对象关系数据模型等,而MySQL采用的是关系模型。在关系模型建立在严格的数学概念基础上,无论是实体还是实体之间的联系都用关系表示,且,关系模型要求关系必须是规范化的,关系的每一个分量必须是不可分的数据项。
术语对比:
MySQL基础语法与JDBC_第6张图片

(二)数据类型

以下内容参考:https://blog.csdn.net/weixin_45851945/article/details/114287877

1.整数类型

MySQL基础语法与JDBC_第7张图片
  MySQL中一共定义了五种整数类型,默认是有符号整数,可用unsigned修饰表无符号整数。
- 迷你类型:tinyint,使用1个字节存储整数,最多存储256个整数。
- 短整型:smallint,使用2个字节存储整数。
- 中整型:mediumint,使用3个字节存储整数。
- 标准整型:int,使用4个字节存储整数。
- 大整型:bigint,使用8个字节存储整数。

显示宽度:整数在数据库中显示的位数(符号+数字),其并不会影响类型所能存储的最大整数,可用zerofill让不够宽度的数值补充道对应宽度。

整型类型(L)

2.浮点数和定点数类型
  在MySQL数据库中使用浮点数和定点数来存储小数。浮点数的类型有两种:单精度浮点数类型(FLOAT)和双精度浮点数类型(DOUBLE)。而定点数类型只有一种即DECIMAL类型。下图列举了 MySQL中浮点数和定点数类型所对应的字节大小及其取值范围:
MySQL基础语法与JDBC_第8张图片
- 单精度:float,使用4个字节存储,精度范围为6~7位有效数字。
- 双精度:double,使用8个字节存储,精度范围为14~15位有效数字。
- 定点型:decimal,能够保证精度的小数。
  对于单/双精度,统称为浮点数,在超出精度范围时会自动进行四舍五入(故在存储对精度较高的数据时一般不用浮点数),可在定义时指定小数和整数部分,默认情况下整数部分不超过最大值,小数部分保留2位,指定语法:

float/double(总长度,小数部分长度);

可使用科学记数法插入数据,如,AEB,表示A*10^B。
  对于定点型,它的存储模式并不固定,可指定整数部分长度和小数部分长度(默认是10位有效整数,0位小数),且,有效数位不超过65位,指定语法为:

decimal(有效数位,小数部分数位);

当整数超出时会报错,小数部分超出时四舍五入。

3.字符串类型
  在MySQL中常用CHAR 和 VARCHAR 表示字符串。两者不同的是:VARCHAR存储可变长度的字符串。
MySQL基础语法与JDBC_第9张图片
  对于指定固定长度的char(L),L的最大值是255.
  对于根据实际存储的数据变化存储空间的varchar(L),L是最大存储的数据长度,最大值为65535,且需要额外产生1~2个字节用于记录实际数据的长度,虽然效率没有定长字符串高,但能更好利用存储空间。

此外,还有一些特殊类型用于存储字符串:

  • 文本字符串:text/blob,专门用来存储较长的文本。
    • text:普通字符,会根据字符串自动选择合适的类型。
类型 名称 存储空间
tinytext 迷你文本 不超过2^8-1个字符
text 普通文本 不超过2^16-1个字符
mediumtext 中长型文本 不超过2^24-1个字符
longtext 长文本 不超过2^32-1个字符(4GB)
  • blob:二进制字符
    MySQL基础语法与JDBC_第10张图片

例:

# 标题一般不会超过50个字符,varchar
# 作者一般不会超过10个字符:varchar
# 内容通常都很长,使用text
create table t_15(
	author varchar(10),
    title varchar(50),
    content text
)charset utf8;
insert into t_15 values('佚名','给联合国的一封信','给联合国的一封信...');
  • enum:枚举类型,定义可能出现的可能使用1~2字节存储,最多可有65535个选项,底层实际是是用数值映射对应的元素数据,从1开始,语法:
enum(元素1,元素2,...);
  • 集合:set,在定义时将可能出现的元素进行穷举,而后数据只能出现定义时其中的元素,可以是多个,使用1~8个字节存储数据,最多有64个数据,底层是使用数值(二进制位),映射元素数据,数据存在时对应位为1,不存在时则为0.
# 爱好可以是多种,并非固定的,但是只能从规定的类型中选择
create table t_17(
	hobby set('足球','篮球','羽毛球','网球','乒乓球','排球','台球','冰球')
)charset utf8;

insert into t_17 values('足球');
insert into t_17 values('冰球,台球,篮球');

4.日期和时间类型
  MySQL提供的表示日期和时间的数据类型分别是 :YEAR、DATE、TIME、DATETIME 和 TIMESTAMP。下图列举了日期和时间数据类型所对应的字节数、取值范围、日期格式以及零值:
MySQL基础语法与JDBC_第11张图片

  • YEAR:年,用来存储年份,大小为1个字节,能表示的范围是:1901~2155(256年),特殊值是0000,由于year字段表示范围有限,故一般使用字符串来存储。
create table t_18(
	y1 year,
    y2 year(4)
)charset utf8;
insert into t_18 values(1901,2155);
  • timestamp:时间戳,格式为:YYYY-MM-DD HH:II::SS,或YYYYMMDDHHIISS ,使用4个字节存储,范围为:1971年1月1日0时0分0秒-2155年12月31日23是59分59秒。
    记录商品库存最后更新时间:
create table t_19(
	goods_name varchar(10),
    goods_inventory int unsigned,
    change_time timestamp
)charset utf8;

insert into t_19 values('Nokia3110',100,'1971-01-01 00:00:00');
insert into t_19 values('Nokia7100',100,'19710101000000');
  • date:日期,使用3个字节存储,格式为:YYYY-MM–DD,或YYYYMMDD,存储区间是1001-01-01~9999-12-31
    例:
create table t_20(
	name varchar(10),
    birth date
)charset utf8;

insert into t_20 values('Jim','2000-12-12');
insert into t_20 values('Tom','10011212');
  • time:日期,记录时间或时间段,使用3个字节存储,范围为: -838:59:59 - 838:59:59,插入格式有:时间格式([H]HH:II:SS([ ]表示可以没有)),时间段格式(D HH:II:SS(D表示天))
    记录用户登录的具体时间:
# 具体登录时间可以使用时间戳(包含年月日时分秒信息)
# 也可以时间datetime格式,或者date+time双字段格式
create table t_22(
	login_time1 int unsigned,
    login_time2 datetime,
    login_date date,
    login_time3 time
)charset utf8;
insert into t_22 values(12345678,'2000-12-12 12:12:12','2000-12-12','12:12:12');
insert into t_22 values(1234567,'2000-12-12 12:12:12','2000-12-12','3 12:12:12');

数据格式format的表示:

  • %M 月名字(January……December)
  • %W 星期名字(Sunday……Saturday)
  • %D 有英语前缀的月份的日期(1st, 2nd, 3rd, 等等。)
  • %Y 年, 数字, 4 位
  • %y 年, 数字, 2 位
  • %a 缩写的星期名字(Sun……Sat)
  • %d 月份中的天数, 数字(00……31)
  • %e 月份中的天数, 数字(0……31)
  • %m 月, 数字(01……12)
  • %c 月, 数字(1……12)
  • %b 缩写的月份名字(Jan……Dec)
  • %j 一年中的天数(001……366)
  • %H 小时(00……23)
  • %k 小时(0……23)
  • %h 小时(01……12)
  • %I 小时(01……12)
  • %l 小时(1……12)
  • %i 分钟, 数字(00……59)
  • %r 时间,12 小时(hh:mm:ss [AP]M)
  • %T 时间,24 小时(hh:mm:ss)
  • %S 秒(00……59)
  • %s 秒(00……59)
  • %p AM或PM
  • %w 一个星期中的天数(0=Sunday ……6=Saturday )
  • %U 星期(0……52), 这里星期天是星期的第一天
  • %u 星期(0……52), 这里星期一是星期的第一天
  • %% 一个文字“%”

(三)约束与字段属性

  建立在字段类型之后,对字段进行除类型之外的其他约束,定义好的属性可使用desc进行查看。

  • null/not null(非空约束):限定数据是否可为null值。
  • default(默认约束):给定字段默认值,当系统检测到字段无数据时会自动赋值,不设置时,字段默认值为null。
  • primary key(主键约束):设置主键,可使用:primary key(字段1,字段2,…)来设置联合主键。主键是一行的唯一标识,设置后要求非空且唯一。

删除主键:

alter table t_26 drop primary key;

新增主键:

alter table t_26 add primary key(account,name);
  • auto_increment:自增长,被修饰的字段在新增时,自动增长数据,自增长只能是整数类型,且,一张表只能有一个自增长,其由auto_increment_offset(初始值,默认为1),auto_increment_increment(步长,默认为1)控制,可使用:
show variables like "auto_increment%";

进行查看。
修改自增长的值,下一条数据将直接从该值开始:

alter table t_28 auto_increment = 50;

修改自增长控制:

set auto_increment_increment = 2;	# 当前用户当前连接有效(局部)
set @auto_increment_increment = 2;	# 所有用户一直有效(全局)
  • unique key(唯一约束):唯一键,用于维护列数据的唯一性,可建立复合唯一键:
unique key(字段1,字段2,...);

删除唯一键:

alter table t_30 drop index stu_name;

追加唯一键:

alter table t_30 add unique key stu_course (stu_name,course);
  • comment:添加注释。
  • foreign key(外键约束):用来让两张表的数据之间建立联系,保证数据的一致性和完整性。

MySQL基础语法与JDBC_第12张图片
称dept_id为外键,此时,将部门表称为父表,而员工表称为子表。

注意,目前上述的两张表,在数据库层面并未建立外键关联,所以是无法保证数据的一致性和完整性的。
添加外键约束:

①创建表时添加
CREATE TABLE 表名(
字段名 字段类型,
...
[CONSTRAINT] [外键名称] FOREIGN KEY(外键字段名) REFERENCES 主表(主表列名)
);
②对表进行修改
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表(主表列名);
-- 例子
alter table emp add constraint fk_emp_dept_id foreign key(dept_id) references dept(id);

删除外键约束:

alter table 表名 drop foreign key 外键名称'
-- 例子
alter table emp drop foreign key fk_emp_dept_id;

外键的删除与更新行为:
MySQL基础语法与JDBC_第13张图片
语法格式

alter table 表名 add constraint 外键名称 foreign (外键字段) references 主表名(主表字段名) on update cascade on delete cascade;

:建立表emp和dept表的外键约束,当父表dept的键id被更新、删除时,子表中的外键亦会被更新、删除。

alter table emp add constraint fk_emp_dept_id foreign key(dept_id) references dept(id) on update cascade delete cascade;

:建立表emp和dept表的外键约束,当父表dept的键id被更新、删除时,子表中的外键亦会被设置为null。

alter table emp add constraint fk_emp_dept_id foreign key(dept_id) references dept(id) on update set null delete set null;
  • check(自定义约束):用户可自定义对字段数据的约束。
create database Test;
use Test;
create table user(
    id int primary key auto_increment comment '主键',
    name varchar(10) not null unique comment '姓名',
    age int check ( age > 0 && age <= 120 ) comment '年龄',
    status char(1) default '1' comment '状态',
    gender char(1) comment '性别'
)comment '用户表';
#插入数据
insert into user(name,age,status,gender)values('Tom1',19,'1','男'),('Tom2',25,'0','男');
#id不用插入,因为其是自动插入的.

二、SQL语法

MySQL基础语法与JDBC_第14张图片

(一)DDL

  DDL(Data Definition Language),数据库定义语言,用来定义数据库对象(数据库、表、字段)

数据库操作

创建数据库

create database [if not exists] 数据库名;

删除数据库

DROP DATABASE [if EXISTS] 数据库名;

使用数据库

--如果表名或者字段名是特殊字符,则需要带``
use 数据库名;

查看所有数据库

SHOW DATABASES;

查看当前数据库

select database();

表操作

创建数据表
  数据库创建成功后可在该数据库中创建数据表(简称为表)存储数据。请注意:在操作数据表之前应使用“USE 数据库名;”指定操作是在哪个数据库中进行先关操作,否则会抛出“No database selected”错误。

CREATE TABLE IF NOT EXISTS `student`(
	'字段名' 列类型 [属性] [索引] [注释],
    '字段名' 列类型 [属性] [索引] [注释],
    ......
    '字段名' 列类型 [属性] [索引] [注释]
)[表的类型][字符集设置][注释]

查看当前数据库所有数据表

show tables;

查看表的创建语句

show create table 表名;

查看表属性

desc 表名;
#Field:字段名称
#Type:数据类型
# Null:是否为空(属性)
# Key:索引类型(属性)
# Default:默认值(属性)
# Extra:额外属性

修改表名

alter table 旧表名 rename to 新表名;

删除数据表

drop table 表名;

修改字段名

alter table 表名 change 旧字段名 新字段名 字段类型;

修改字段数据类型

alter table 表名 modify 字段名 数据类型

增加字段

alter table 表名 add 字段名 数据类型;

删除字段

alter table 表名 drop 字段名;

(二)DML

  DML(Data Manipulation Language),数据库操作语言,用来对数据库表中的数据进行增删改。

添加数据

基本语法

insert into 表名(字段1,字段2,...)values(1,2,...)(1,2,...);

既可插入一条数据,也可同时插入多条数据。
既可插入所有字段,亦可选择字段进行插入。

更新数据

基本语法

UPDATE 表名 SET 字段名1=1[,字段名2 =2,] [WHERE 条件表达式];

当不写where表达式时则会更新表中该字段的所有属性值。

删除数据

基本语法

delete from 表名 [where 条件];

当不写where表达式时则会删除数据库表中所有记录。

(三)DQL

  DQL(Data Query Language),数据库查询语言,用来查询数据库中表的记录。

基本语法

SELECT [ALL | DISTINCT]
{* | table.* | [table.field1[as alias1][,table.field2[as alias2]][,...]]}
FROM table_name [as table_alias]
  [left | right | inner join table_name2]  -- 联合查询
  [WHERE ...]  -- 指定结果需满足的条件
  [GROUP BY ...]  -- 指定结果按照哪几个字段来分组
  [HAVING]  -- 过滤分组的记录必须满足的次要条件
  [ORDER BY ...]  -- 指定查询记录按一个或多个条件排序
  [LIMIT {[offset,]row_count | row_countOFFSET offset}];
   -- 指定查询的记录从哪条至哪条
  • ALL:默认为ALL,显示所有查询结果。
  • DISTINCT:将查询结果去重。
  • AS:将查询结果字段、查询表起别名。

简化

select(可使用聚合函数)
    字段列表
from
    表名列表
where(对应条件查询)
    条件列表
group by(对应分组查询)
    分组字段列表
having
    分组后条件列表
order by(对应排序查询)
    排序字段列表
limit(分页查询)
    分页参数

DQL语句执行顺序
MySQL基础语法与JDBC_第15张图片

常用函数

-- 数学运算
SELECT ABS(-8); -- 绝对值
SELECT CEIL(5.1); -- 向上取整
SELECT CEILING(5.1); -- 向上取整
SELECT RAND(); -- 返回0~1之间的一个随机数
SELECT SIGN(-10); -- 返回一个数的符号;0返回0;正数返回1;负数返回-1
round(x,保留位数);--四舍五入; 当对正数进行四舍五入:按照正常的计算方式,四舍五入即可。当对负数进行四舍五入:先把符号丢到一边,对去掉负号后的正数进行四舍五入,完成以后,再把这个负号,补上即可。
floor(x):向下取整
truncate(x,D);--截断函数,截取不要的部分,然后删掉(断掉)它.在小数点的D位置处,截取数字直接删去数字,若在左边就是位置取整不使用任何法则.
mod(被除数,除数);--求余
pow(x,D);--求x的D次幂
-- 字符串函数
SELECT LENGTH('我喜欢你'); -- 字符串长度
SELECT CONCAT('我','喜欢','你'); -- 拼接字符串
SELECT INSERT('我喜欢',1,1,'超级') -- INSERT(str,pos,len,newstr) 从str的pos位置开始替换为长度为len的newstr
SELECT UPPER('zsr'); -- 转大写
SELECT LOWER('ZSR'); -- 转小写
SELECT INSTR('zsrs','s'); -- 返回第一次出现字串索引的位置
SELECT REPLACE('加油就能胜利','加油','坚持'); -- 替换出现的指定字符串
SELECT SUBSTR('坚持就是胜利',3,6); -- 返回指定的字符串(源字符串,截取位置,截取长度)
SELECT REVERSE('rsz'); -- 反转字符串
SELECT INSTR(str,要查询的子串); -- 返回子串第一次出现的索引,凡存在则返回0(索引从1开始)
SELECT TRIM(str);--去掉字符串的前后空格
lpad(str,len,填充字符)--用指定的字符,将字符左填充至指定长度
rpad(str,len,填充字符)--用指定的字符,将字符右填充至指定长度
replace(str,子串,另一个字符串)--将字符串str中的子串替换为另一个字符串

-- 时间日期函数
SELECT CURRENT_DATE(); -- 获取当前日期
SELECT CURDATE(); -- 获取当前日期
SELECT now(); -- 获取当前时间
SELECT LOCALTIME(); -- 本地时间
SELECT SYSDATE(); -- 系统时间
weekofyear();--获取当前所属周数
quarter();--获取当前所属季度
str_to_date();--将日期格式转为字符串,可用%Y、%m、%d进行格式化,但格式是固定的,例:
select str_to_date("2023,1,15",'%Y,%c,%d');
会转换为:2023-01-15
select date_format(日期,指定格式);--将日期格式转为字符串,可用%Y、%m、%d进行格式化,格式可自行指定
date_add(日期,interval num 时间);--向前、后偏移时间和日期,用正负号来确定,例:
select date_add(curdate(),interval 1 day);
select last_day(now());--获取某个月的最后一天
datediff(end_date,start_date);--计算两个时间相差的天数


--获取年、月、日、小时、分钟、秒
SELECT YEAR(NOW());
SELECT MONTH(NOW());
SELECT DAY(NOW());
SELECT HOUR(NOW());
SELECT MINUTE(NOW());
SELECT SECOND(NOW());

-- 系统信息
SELECT SYSTEM_USER();
SELECT USER();
SELECT VERSION();
show processlist;--查看用户连接信息

聚合函数

MySQL基础语法与JDBC_第16张图片

普通查询

select 字段1,字段2,... from table 表名;

可使用通配符*来查询表中所有数据。

条件查询

  用于检索数据中符合条件的值,检索的条件可由一个或多个表达式组成,结果为布尔值。

运算符 语法 描述
and/&& exp1 and exp2/exp1 && exp2 逻辑与
or/|| exp1 or exp2/exp1 || exp2 逻辑或
not/! not exp/!exp 逻辑非

MySQL基础语法与JDBC_第17张图片

模糊查询

运算符 语法 描述
is null E is null 若字段E为null,则结果为真
is not null E is not null 若字段E不为null,则结果为真
between … and … A between B and C. 若A在B和C之间,则结果为真
like A like B SQL匹配,若A匹配B,则结果为真
in A in (A1,A2,A3,…) 若A是A1、A2、A3中的任意一个,则结果为真

  对于like查询,其目的是判断两个字符串是否匹配,有以下三种情况:

  1. 普通字符串
select 字段名 from 表名 where 字段名 like 字符串;

当字段值和字符串完全相等时才为true。
2. %通配符
  %通配符用于匹配任意长度的任意字符串,如:匹配以"l"结尾的字符串:

select 字段名 from 表名 where 字段名 like "%l";
  1. _通配符
      下划线通配符只匹配单个任意字符,若要匹配多个字符,则需要使用多个下划线。如,匹配"ax"开头,长度为4的字符串:
select 字段名 from 表名 where 字段名 like "ax__";

分组查询

  分组查询可将表中数据分组后再进行查询,使用group by来选择分组的字段,having则用于分组后的过滤。

select 分组函数,分组后的字段
from 表
【where 筛选条件】
group by 分组的字段
【having 分组后的筛选】
【order by 排序列表】

原理分析
MySQL基础语法与JDBC_第18张图片

select name from test group by name;

中间相当于增加了一个虚拟表,表3:
MySQL基础语法与JDBC_第19张图片

使用group by函数前的规定:

  • group by子句中每个列必须是检索列或有效的表达式,而不能是聚集函数,如果在select中使用表达式,则必须在group by中指定相同的表达式,不能使用别名。
  • 如果分组列中含有NULL值,则NULL将作为一个分组返回,多个NULL将合并为一组。
  • group by语句必须出现在where语句之后,order by语句之前。

过滤分组:group by可以对数据进行分组,但是分组后所有的组都会被查询,但有时,我们并不需要所有的分组,此时可以对分组进行过滤。where并不能对分组进行过滤,因为where的过滤对象是行(一个单元格只含有一个数据),MySQL提供了having来对分组进行过滤,实际上,目前为止所有的where子句都可以用having来代替,唯一的差别就是,where对行过滤,而having对分组进行过滤。且二者不能同时使用。
  分组的过滤常常和聚合函数一起使用。

例:

-- 查询不同科目的平均分、最高分、最低分且平均分大于90
-- 核心:根据不同的课程进行分组
SELECT SubjectName,AVG(StudentResult),MAX(`StudentResult`),MIN(`StudentResult`)
FROM result r
INNER JOIN `subject` s
on r.SubjectNo=s.SubjectNo
GROUP BY r.SubjectNo
HAVING AVG(StudentResult)>90;

注意:HAVING语句可与GROUP BY配合使用,但不能和WHERE语句配合使用。且,分组查询经常和聚合函数一起使用。

连接查询

  连接查询中,一共有左连接(LEFT JOIN)、右连接(RIGHT JOIN)、内连接(INNER JOIN)、外连接(OUTER JOIN)四种连接方式,并使用ON来指定连接键,结合where的条件语句,可得到以下七种连接方式:
MySQL基础语法与JDBC_第20张图片

-- 查询学员所属的年级(学号,学生姓名,年级名称)
SELECT `StudentNo`,`StudentName`,`GradeName`
FROM student s
INNER JOIN grade g
ON s.GradeID=g.GradeID;

-- 查询科目所属的年级
SELECT `SubjectName`,`GradeName`
FROM `subject` s
INNER JOIN `grade` g
ON s.GradeID=g.GradeID;

-- 查询列参加程序设计考试的同学信息(学号,姓名,科目名,分数)
SELECT s.`StudentNo`,`StudentName`,`SubjectName`,`StudentResult`
FROM student s
INNER JOIN result r
on s.StudentNo=r.StudentNo
INNER JOIN `subject` sub
on r.SubjectNo=sub.SubjectNo
where SubjectName='课程设计';

自连接
  自连接可将同一张表当做两张表来使用。
用法一:在同一张表内进行比较
:查找收入超过各自经理的员工姓名

 Id | Name  | Salary | ManagerId 
----+-------+--------+-----------
 1  | Joe   | 70000  | 3         
 2  | Henry | 80000  | 4         
 3  | Sam   | 60000  | NULL      
 4  | Max   | 90000  | NULL 
————————————————
SELECT e1.Name AS employee_name
FROM Employee AS e1, Employee AS e2
WHERE e1.ManagerId=e2.Id
AND e1.Salary>e2.Salary

:查找比昨天温度高的所有日期的id

| Id(INT) | RecordDate(DATE) | Temperature(INT) |
+---------+------------------+------------------+
|       1 |       2015-01-01 |               10 |
|       2 |       2015-01-02 |               25 |
|       3 |       2015-01-03 |               20 |
|       4 |       2015-01-04 |               30 |
SELECT w1.Id
FROM weather w1
JOIN weather w2
ON DATEDIFF(w1.RecordDate,w2.RecordDate)=1
WHERE w1.Temperature>w2.Temperature

用法二:找出列的组合
:查找共用同一车站的所有公交路线

SELECT * FROM route R1, route R2
WHERE R1.stop=R2.stop;

用法三:找出部分内容重复的记录
:查找价格相同但商品名不同的商品信息

SELECT DISTINCT P1.name, P1.price
FROM Products P1, Products P2
WHERE P1.price = P2.price
AND P1.name != P2.name;

:删除Person表中所有重复的电子邮箱

DELETE p1 FROM Person p1, Person p2
WHERE p1.Email = p2.Email
AND p1.Id > p2.Id

子查询

  子查询是指一个查询语句嵌套在另一个查询语句内部的查询;该查询语句可以嵌套在一个 SELECT、SELECT…INTO、INSERT…INTO等语句中。在执行查询时,首先会执行子查询中的语句,再将返回的结果作为外层查询的过滤条件。在子査询中通常可以使用比较运算符和IN、EXISTS、ANY、ALL等关键字。
带比较运算符的子查询
例:比张三所在班级编号还大的班级的信息

select * from class where cid>(select classid from student where sname='张三');

带EXISTS的子查询
  EXISTS关键字后面的参数可以是任意一个子查询, 它不产生任何数据只返回TRUE或FALSE。当返回值为TRUE时外层查询才会 执行
例:假如王五同学在学生表中则从班级表查询所有班级信息

select * from class where exists (select * from student where sname='王五');

带ANY的子查询
  ANY关键字表示满足其中任意一个条件就返回一个结果作为外层查询条件。

select * from class where cid > any (select classid from student);

带ALL的子查询
  带ALL关键字的子査询返回的结果需同时满足所有内层査询条件。

select * from class where cid > all (select classid from student);

  事实上,子查询可分为相关子查询和不相关子查询。

  • 不相关子查询:所有的不相关子查询都可用连接查询代替。
  • 不相关子查询:子查询执行依赖于外部查询,多数情况下是子查询的where语句引用了外部查询的表。
    例:
    MySQL基础语法与JDBC_第21张图片
    查询大于每门课平均分的数据。
-- 主语句
select * from world where 成绩>分科平均分;
-- 子查询:分科平均分

MySQL基础语法与JDBC_第22张图片

#方法1.子查询用having子句通过条件筛选这个group by 数组后,就能使得每一行的数据对应一个avg(成绩)
select * ,
(select avg(成绩) from score as s2 group by 课程号 having s1.课程号=s2.课程号 ) as 分科平均成绩
from score as s1;

MySQL基础语法与JDBC_第23张图片

#方法2.这里的where子句可以起到和having一样的效果,where已经起到选组作用,group by 子句可以省略
select * ,
(select avg(成绩) from score as s2 where s1.课程号=s2.课程号 /*group by 课程号*/) as 分科平均成绩
from score as s1;

分页查询

  分页查询是在页面上将本来很多的数据分段显示,每页显示用户自定义的行数。可提高用户体验度,同时减少一次性加载,内存溢出风险。
在开发过程中的效果:
在这里插入图片描述
语法:

select 字段列表 from 表名 limit 起始索引,查询记录数;
  • 起始索引从0开始,起始索引=(查询页码-1)*每页显示记录数。
  • 如果查询的是第一页数据,起始索引可以省略,直接简写为LIMIT 10。
    例:
    查询第一页员工数据,每页显示10条记录
select * from emp limit 10;
--起始索引为0,可以省略

查询第二页员工数据,每页显示10条记录

select * from emp limt 10 10;

排序

  使用ORDER BY可根据指定字段对查询结果进行排序,字段优先级取决于字段的设置次序。

SELECT 字段名1,字段名2,FROM 表名
ORDER BY 字段名1 [ASCDESC],字段名2 [ASC | DESC];

(四)DCL

  DCL(Database Control Language),数据库控制语言,用来管理数据库,控制数据库用户访问权限。

-- 查看所有用户
select * from mysql.user;
-- 查看当前所用用户
select user();
-- 创建用户,并指定密码
CREATE USER 用户名 IDENTIFIED BY '123456'

-- 删除用户
DROP USER 用户名

-- 修改当前用户密码
SET PASSWORD = PASSWORD('200024')

-- 修改指定用户密码
SET PASSWORD FOR 用户名 = PASSWORD('200024')

-- 重命名
RENAME USER 用户名 to 新用户名

-- 用户授权(授予全部权限,除了给其他用户授权)
GRANT ALL PRIVILEGES on 数据库名.表名 TO 用户名

--用户授权(授予指定权限)
GRANT 权限 on 数据库名.表名 TO 用户名

-- 查询用户权限
SHOW GRANTS FOR 用户名

-- 查看root用户权限
SHOW GRANTS FOR root@localhost

-- 撤销所有权限
REVOKE ALL PRIVILEGES ON 数据库名.表名 FROM 用户名

可指定主机,使得用户只能在该主机上访问数据库,格式为:‘用户名’@‘主机名’。可使用’%'代替主机名,表示任意地址均可登录。
GRANT语句中可选权限:
MySQL基础语法与JDBC_第24张图片
MySQL基础语法与JDBC_第25张图片

  事实上,在MySQL中,会自带四个用户,其中root用户是经常使用的,它对整个MySQL服务器有完全的控制,但一般来说,最好不要使用root,因为其权限过大,容易出错。用户信息存放在mysql数据库下的user表,可以用select * from mysql.user;进行查看。
  在DataGrip、IDEA中连接数据库时就可指定用户,而在命令行中,可使用:

mysql -u 用户名 -p

进行登录。

三、三大范式、BCNF、反范式与依赖关系

  • 范式:设计数据库时需要遵从的一些规范,目前的关系数据库有六种范式,分别是,第一范式(1NF),第二范式(2NF),第三范式(3NF),巴斯-科德范式(BCNF),第四范式(4NF),第五范式(完美范式,5NF),要遵循后面的范式就必须得先遵循前面的范式。
    • 第一范式(1NF):每个列都不可再分。
    • 第二范式(2NF):在1NF的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
    • 第三范式(3NF):在2NF的基础上,非主键列值依赖于主键,而不依赖其他非主键。
    • BCNF:3NF的基础上,消除主属性(属于主键的属性即称为主属性)对码的部分与传递函数依赖。
    • 反范式:不满足范式的模型就是反范式模型。反范式模型与范式所要求的正好相反,在反范式设计模式中,允许适当的数据的冗余,利用这个冗余去缩短操作数据所用的时间,本质就是空间换取时间,把数据冗余在多个表中,当查询时可以减少或是避免表之间的关联。

(一)三大范式案例

:将下表变为规范的表(主键为(学号,课程名))
MySQL基础语法与JDBC_第26张图片
①1NF的改造:使得没个列都不可再分。
  从表中可知,"系"列又分为了系名和系主任,因此,"系"列是可再分的,需要进行改造:
MySQL基础语法与JDBC_第27张图片
②2NF的改造:1NF的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。

  • 函数依赖:若通过属性组A可以唯一确定属性B的值,则称B依赖于A,如:系主任可由(学号,姓名,系名)唯一确定,就称系主任函数依赖于(学号,姓名,系名),但事实上,(学号,姓名)是冗余的。函数依赖又可分为完全函数依赖、部分函数依赖、传递函数依赖。
  • 完全函数依赖:若A是一个属性组,而B属性值的确定需要A属性组中的所有属性的属性值都确定,则称B完全函数依赖于A,如上述例子中,系主任完全函数依赖于(系名),而与(学号,姓名)是否确定无关。
  • 部分函数依赖:若A是一个属性组,而B属性组的确定需要确定A属性组中的一部分属性即可,则称属性组B部分函数依赖于属性组A,如上述函数依赖中的例子就是一个部分函数依赖的例子。
  • 传递函数依赖:若A是一个属性组,由属性组A可确定一个属性组B的值(可能是完全函数依赖,亦可能是部分函数依赖),而B属性组的值又可以唯一确定C属性组的值(可能是完全函数依赖,亦可能是部分函数依赖),则称属性组C传递函数依赖于属性组A。

  对于上表的主键(学号,课程名),和该表中其他列的关系,有:
(学号,课程名)->姓名,姓名对(学号,课程名)是部分函数依赖。
(学号,课程名)->系名,系名对(学号,课程名)是部分函数依赖。
(学号,课程名)->系主任,系主任对(学号,课程名)是部分函数依赖。
(学号,课程名)->分数,分数对(学号,课程名)是完全函数依赖。
而2NF的目的就是在1NF的基础上消除部分函数依赖。由于姓名、系名、系主任均可由学号唯一完全确定,而分数由(学号,课程名)唯一完全确定,故可分解为两个表:
MySQL基础语法与JDBC_第28张图片
可以发现,很多数据出现次数减少,如,"张无忌"有1NF中的3次出现变成了一次出现,这大大减少了存储空间,提高查询效率。
  事实上,2NF可以理解为:在1NF的基础上消除了非主键对主键的部分函数依赖。
③3NF的改造:在2NF的基础上,非主键列值依赖于主键,而不依赖其他非主键。
  对于选课表,属性列:课程名称和分数均完全函数依赖于学号,且二者之间并无函数依赖关系,故满足3NF。
  对于学生表,系主任可由系名唯一完全确定,即,系主任对系名存在函数依赖(与是完全函数依赖还是部分函数依赖无关),且系名并非主键,其函数完全函数依赖于主键,故,这里存在传递函数依赖,可以进行化简:
MySQL基础语法与JDBC_第29张图片
  由此可见,符合3NF要求的数据库设计,基本上解决了数据冗余过大,插入异常,修改异常,删除异常的问题。当然,在实际中,往往为了性能上或者应对扩展的需要,经常 做到2NF或者1NF,但是作为数据库设计人员,至少应该知道,3NF的要求是怎样的。

(二)BCNF案例

MySQL基础语法与JDBC_第30张图片

已知函数依赖

  • 仓库名->管理员
  • 管理员->仓库名
  • (仓库名,物品名)->数量
  • 码:(管理员,物品名),(仓库名,物品名)
  • 主属性:管理员,物品名,仓库名
  • 非主属性:数量

  由于,仓库名->管理员 管理员->仓库名 ,的存在,故,存在主属性对码的部分函数依赖和传递函数依赖,因而,不属于BCNF。
  转化为BCNF的方法便是,将仓库名或管理员分离出表,而数量由(仓库名,物品名)唯一完全确定,故应将管理员分离出:

MySQL基础语法与JDBC_第31张图片

(三)范式化的优缺点

  • 优点
    • 范式化的更新操作通常比反范式化快。
    • 当数据较好地范式化时,就只有或者没有重复数据,所以只需要修改很少的数据。
    • 范式化的表通常很小,可以更小地放在内存中,所以执行速度会很快。
    • 很少有多余的数据意味着检索列表数据时更少需要DISTINCT或GROUP BY语句。
  • 缺点:通常需要关联,稍微复杂一点的查询语句在符合范式的表上都可能需要至少一次关联,或许更多,较多的关联可能使得一些索引策略无效。

(四)反范式化的优缺点

  • 优点:通过冗余数据来提高查询性能,可以减少表的关联和更好地进行索引优化。
  • 缺点:存在大量冗余数据,并且数据的维护成本更高。

四、引擎

  存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,所以,存储引擎也可被称为表类型,且,同一数据库的不同表可以指定不同的存储引擎。
  在Oracle、SQL Server数据库管理系统中只有InnoDB一种存储引擎,而MySQL数据库提供了多种存储引擎。用户可以预先设置或者在MySQL服务器中启用。也可以选择适用于服务器、数据库和表格的存储引擎,以便在选择如何存储你的信息,如何检索这些信息以及所需要的数据结合什么样的性能和功能的时候提供了最大的灵活性。

(一)引擎操作

--查看数据库支持的所有引擎
show engines;
--查看当前表所用的引擎
show table status like 表名;
--创建表时指定存储引擎
create table 表名{
    字段1 字段1类型 [comment 字段1注释],
    ...
    字段n 字段n类型 [comment 字段n注释]
}engine=InnoDB [comment 表注释];

(二)引擎分类

MySQL基础语法与JDBC_第32张图片

  • InnoDB:MySQL默认的存储引擎,是一个事务型的存储引擎,有行级锁和外键约束,支持全文检索(全文索引),它的设计目标就是为了处理大容量数据库系统。
    • 特点:DML操作遵循ACID模型,支持事务,提供行级锁,提高并发访问的性能,支持外键约束。
  • MyISAM:MySQL早期的默认存储引擎,基于ISAM引擎发展起来的,增加了许多有用的扩展。
    • 优势就在于占用空间小,查询处理速度快。缺点就是不支持事务的完整性和并发性,INSERT(插入)或UPDATE(更新)数据时需要锁定整个表,效率更低眼底下。
  • MEMORY:MEMORY引擎的表数据是存储在内存中的,由于受到硬件问题、或断电问题的影响,只能将这些表作为临时表或缓存使用。
    • 特点:内存存放。

(三)存储引擎的选择

MySQL基础语法与JDBC_第33张图片

五、视图

   视图是从一个或多个表中导出来的表,是一种虚拟存在的表。视图就像一个窗口,通过这个窗口可以看到系统专门提供的数据,这样用户可以不看整个数据库表中的数据,而只关心对自己有用的数据。视图可以使用户的操作更方便,而且可以保障数据库系统的安全性。
即:视图是虚拟表,本身不存储数据,而是按照指定的方式进行查询。
视图的好处

  • 减少重复的select语句。
  • 简化复杂的select操作。
  • 保护数据,使得用户只有表特定部分的访问权限而无整个表的访问权限。

视图的规则

  • 视图的数目并无限制。
  • 视图可嵌套构造。
  • 视图不能被索引,也不能有关联的触发器或默认值。

视图的基本操作
创建视图

create view 视图名 (1,2,3,...) as select1,2 from 表名

查看创建视图的语句

show create view 视图名;

删除视图

drop view 视图名;

视图的更新:视图是可更新的(利用insert、update、delete语句),更新一个视图是会影响到其基表的。但并非所有视图均可更新,当视图含义以下操作时,其是不可更新的:

  • 分组
  • 联结
  • 子查询
  • 聚类函数
  • distinct
  • 计算列

六、存储过程

  存储过程(PROCEDURE),是事先经过编译并存储在数据库中的一段SQL语句的集合。调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是很有好处的。
即,存储过程在SQL语言中相当于Java中定义的函数,是对操作的封装。

(一)基础语法

查看所有存储过程

show procedure status ;

显示存储过程源码

show create procedure 存储过程名;

删除存储过程

drop procedure 存储过程名;

创建存储过程
语法格式

  • declare:用于声明变量。
  • default:给变量赋默认值。
  • set:改变变量的值。
delimiter 自定义结束符
create procedure 存储过程名(参数列表)
begin 
	--1.局部变量
	--定义局部变量,仅在begin/end块中有效
	declare 局部变量名 数据类型;
	--给局部变量赋值
	set 局部变量名=;
	
	--2.用户变量
	--定义用户变量,当前会话中有效,不需要声明,直接赋值即可
	set @用户变量名=;

	--3.系统变量
	--系统变量,分为全局变量和会话变量
	--全局变量:全局变量在MYSQL启动的时候由服务器自动将他的初始化为默认值,这些默认值可以通过更改my.ini这个文件来更改
	--会话变量:会话变量在每次建立一个新的连接的时候,由MYSQL来初始化,MYSQL会将当前所有全局变量的值复制一份,来作为会话变量.也就是说,如果在建立会话以后,没有手动更改过会话变量与全局变量的值,那所有这些变量的值都是一样的.
	/*全局变量和会话变量的区别:对全局变量的修改会影响整个服务器,但是对会话变量的修改,
	只会影响到当前的会话(也就是当前的数据库连接).有些系统变量的值是可以利用语句来动态进行更改的,
	但是有些系统变量的值缺是只读的,对于那些可以更改的系统变量,我们可以利用set语句进行更改*/
	--查看全局变量
	show global variables;
	--查看某全局变量
	select @@global.auto_increment_increment;
	--设置全局变量
	set global sort_buffer_size=40000;
	--修改全局变量
	set @@global.sort_buffer_size=30000;
	
	--函数体:SQL语句集合
	
end 自定义结束符
    delimiter ;

参数的传递:存储过程一共提供三种参数供使用。

  • in:输入参数,参数值由调用程序指定。
  • out:输出参数,表示该参数的值经存储过程计算后,将out参数的计算结果返回给调用程序。
  • inout:代表既是输入参数,又是输出参数,表示该参数的值即可有调用程序制定,又可以将inout参数的计算结果返回给调用程序。

存储过程的调用

call 存储过程名(参数传递);

实例
  定义一个存储过程,可用来计算不同性别的人数。
(此处使用的是名为mybatis的数据库,存储过程名为fun,且需要定义一个用户变量,作用域为当前会话)

DELIMITER $$
CREATE
    PROCEDURE `mybatis`.`fun`(IN s_sex CHAR(1),OUT s_count INT)
	-- 存储过程体
	BEGIN
		-- 把SQL中查询的结果通过INTO赋给变量
		SELECT COUNT(*) INTO s_count FROM student WHERE SSex= s_sex;
		SELECT s_count;
		
	END$$
DELIMITER ;
call fun('男',@s_count);
select @s_count;

MySQL基础语法与JDBC_第34张图片
且,定义之后亦会存入数据库当中:
MySQL基础语法与JDBC_第35张图片

(二)IF ELSE

  if else,可用于流程控制,进行条件判断。
语法格式

	if 条件表达式1 then
		执行语句1;
	elseif 条件表达式2 then
		执行语句2;
	else
		执行语句3;
	end if;

实例

DELIMITER $$
create
    definer = root@localhost procedure getWeekday(IN day int, OUT res varchar(3))
BEGIN
		if `day` = 0 then
		    set res= '星期天';
		elseif `day` = 1 THEN
		    set res= '星期一';
		elseif `day` = 2 THEN
		    set res= '星期二';
		elseif `day` = 3 THEN
		    set res= '星期三';
		elseif `day` = 4 THEN
		    set res= '星期四';
		elseif `day` = 5 THEN
		    set res= '星期五';
		else
		    set res= '星期六';
		END IF;
	END$$
DELIMITER;

call getWeekday(week(now()),@week);
select @week as '今天';

MySQL基础语法与JDBC_第36张图片

(三)CASE

  case,亦可用于流程控制,共有两种用法。
第一种

DELIMITER $$
CREATE 
    PROCEDURE demo(IN num INT)
	BEGIN
		CASE num  -- 条件开始
		WHEN 1 THEN 
			SELECT '输入为1';
		WHEN 0 THEN 
			SELECT '输入为0';
		ELSE 
		SELECT '不是1也不是0';
		END CASE; -- 条件结束
	END$$
DELIMITER;

第二中

DELIMITER $$
CREATE 
    PROCEDURE demo(IN num INT)
	BEGIN
		CASE -- 条件开始
	
		WHEN num<0 THEN 
			SELECT '负数';
		WHEN num>0 THEN 
			SELECT '正数';
		ELSE 
		SELECT '不是正数也不是负数';
	
		END CASE; -- 条件结束
	END$$
DELIMITER;

(四)while

DELIMITER $$
CREATE 
    PROCEDURE demo(IN num INT,OUT SUM INT)
	BEGIN
	     SET SUM = 0;
	     WHILE num<10 DO -- 循环开始
	         SET num = num+1;
	         SET SUM = SUM+num;
	         END WHILE; -- 循环结束
	END$$
DELIMITER;

(五)repeat until

  REPEATE…UNTLL 语句的用法和 Java中的 do…while 语句类似,都是先执行循环操作,再判断条件,区别是REPEATE 表达式值为 false时才执行循环操作,直到表达式值为 true停止。

-- 创建过程
DELIMITER $$
CREATE 
    PROCEDURE demo(IN num INT,OUT SUM INT)
	BEGIN
	     SET SUM = 0;
	     REPEAT-- 循环开始
		SET num = num+1;
		SET SUM = SUM+num ;
		UNTIL num>=10
		END REPEAT; -- 循环结束
	END$$
DELIMITER;

(六)LOOP

  用来重复执行某些语句,执行过程中可用leave(相当于break)、iterate(相当于continue)跳出循环。

DELIMITER $$
CREATE 
    PROCEDURE demo(IN num INT,OUT SUM INT)
	BEGIN
	     SET SUM = 0;
	     demo_sum:LOOP-- 循环开始(定义循环的名称)
		 	SET num = num+1;
		 	
		 	IF num > 10 THEN
		    	LEAVE demo_sum; -- 结束此次循环
			ELSEIF num <= 9 THEN
		    	ITERATE demo_sum; -- 跳过此次循环
			END IF;
		
		SET SUM = SUM+num;
		END LOOP demo_sum; -- 循环结束
	END$$
DELIMITER;

(七)后端调用

<parameterMap type="savemap" id=“usermap"> 
	" jdbcType="VARCHAR" mode="IN"/>
	" jdbcType="CHAR" mode="IN"/>
	" jdbcType="VARCHAR" mode="OUT"/>


" parameterMap="savemap" statementType="CALLABLE"> 
{call saveuser(?, ?, ?)} 
</insert >

七、游标

  游标是一个存储在MySQL服务器上的数据库查询,它相当于是一个指针,可以定位每一条数据,向前定位、向后定位,或随意定位到某一条数据,并对指向的记录中的数据进行操作。
使用步骤
①声明游标
  使用查询语句会将结果集返回给游标、

declare 游标名称 cursor for 查询语句;

②打开游标
  打开游标后,查询结果集会送到游标工作区,为后面游标逐条读取数据作准备。

open 游标名称;

③使用游标
  使用游标来读取当前行,可以把数据保存到变量中,如果游标读取的数据有多列,可以在into后面赋值给多个变量,且var_name需要在之前就定义好类型,游标查询结果集中的字段数必须和into后面的变量数一样,否则会报错。

fetch 游标名 into 变量1,变量2,...

用一个游标来读取当前行,可以把数据保存到变量中,游标指针指到下一行。
④关闭游标
  游标会占用系统资源,所以需要及时关闭,如果没有及时关闭,游标会一直保存到存储过程结束,影响系统运行的效率,关闭游标可以释放游标占用的系统资源。

close 游标名;

例:实现累加薪资最高的几个员工的薪资值,直到薪资总和达到limit_total_salary参数的值,返回累加的人数给total_count。

DELIMITER //

CREATE PROCEDURE get_count_by_limit_total_salary(IN limit_total_salary DOUBLE,OUT total_count INT)

BEGIN
	DECLARE sum_salary DOUBLE DEFAULT 0;  #记录累加的总工资
	DECLARE cursor_salary DOUBLE DEFAULT 0; #记录某一个工资值
	DECLARE emp_count INT DEFAULT 0; #记录循环个数
	#定义游标
	DECLARE emp_cursor CURSOR FOR SELECT salary FROM employees ORDER BY salary DESC;
	#打开游标
	OPEN emp_cursor;
	
	REPEAT
		#使用游标(从游标中获取数据)
		FETCH emp_cursor INTO cursor_salary;
		
		SET sum_salary = sum_salary + cursor_salary;
		SET emp_count = emp_count + 1;
		
		UNTIL sum_salary >= limit_total_salary
	END REPEAT;
	
	SET total_count = emp_count;
	#关闭游标
	CLOSE emp_cursor;
	
END //

DELIMITER ;

八、事务

(一)基本概念

  事务,就是由单独单元的一个或多个sql语句组成,在这个单元中,每个sql语句都是相互依赖的。而整个单独单元是作为一个不可分割的整体存在,类似于物理当中的原子(一种不可分割的最小单位)。

四大特性(ACID)

  • 原子性(Atomicity):指事务是一个不可分割的最小工作单位,事务中的操作只有都发生和都不发生两种情况
  • 一致性(Consistency): 事务执行之前和执行之后数据都是合法的一致性状态,即使发生了异常,也不会因为异常引而破坏数据库的完整性约束,比如唯一性约束等。
  • 隔离性(Isolation):一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  • 持久性(Durability):一个事务一旦提交成功,它对数据库中数据的改变将是永久性的,接下来的其他操作或故障不应对其有任何影响。

事务的分类

  • 隐式事务:没有明显的开启和结束标记,它们都具有自动提交事务的功能;如,update语句修改数据时,是不是对表中数据进行改变了,它的本质其实就相当于一个事务。
  • 显式事务:该事务具有明显的开启和结束标记;也是本文重点要讲的东西。使用显式事务的前提是你得先把自动提交事务的功能给禁用。禁用自动提交功能就是设置autocommit变量值为0(0:禁用 1:开启)

查看当前autocommit变量值

SHOW VARIABLES LIKE 'autocommit';

开启、关闭自动提交

# 开启自动提交(默认)
SET autocommit = 1;
# 关闭自动提交
SET autocommit = 0;

在自动提交状态下,如果没有显示的开启事务,那每一条语句都是一个事务,系统会自动对每一条 sql 执行 commit 操作。使用 BEGIN 或 START TRANSACTION 开启一个事务之后,自动提交将保持禁用状态,直到使用 COMMIT(提交) 或 ROLLBACK(回滚) 结束事务之后,自动提交模式会恢复到之前的状态。

(二)基本操作

  正常使用事务的流程为:

#步骤一:开启事务
start transaction;
#步骤二:编写事务中的sql语句(insert、update、delete)
sql语句...
#步骤三:结束事务
commit; #提交事务

  在DataGrip上进行模拟:
建立test表:

CREATE TABLE `test` (
  `id` int(11) NOT NULL
) ENGINE=InnoDB;
INSERT INTO `test` VALUES ('1');
INSERT INTO `test` VALUES ('3');
INSERT INTO `test` VALUES ('5');

在第一个查询控制台执行

start transaction ;
insert into test value ('2'),('4'),('6');

在第二个查询控制台进行查询

select * from test;

MySQL基础语法与JDBC_第37张图片
可见,此时虽然第一个查询控制台执行了SQL语句,但表中数据并未更新,但再次执行:

commit;

MySQL基础语法与JDBC_第38张图片
此时在第二个查询控制台上即可查询到数据。
回滚:当在编写事务处理SQL语句时,若想要回到事务处理前数据库的状态,就可使用回滚。

rollback;

例:

start transaction ;
insert into test value ('10');
insert into test value ('11');
insert into test value ('12');
rollback ;
commit ;
select * from test;

MySQL基础语法与JDBC_第39张图片

保留点:上述回滚操作会回滚到当前事务前的状态,可设置保留点,回到指定的位置,例:

BEGIN;
insert into test value ('10') ;
SAVEPOINT s1;
insert into test value ('12') ;
ROLLBACK TO s1;   # 回滚到保留点s1,因此10成功写入,12被回滚

RELEASE SAVEPOINT s1; # 释放保留点

事实上,以上事务的并发执行若未采用必要的隔离机制,就会产生各种并发问题,如:

  • 脏读:对于两个事务T1,T2,T1读取了已经被T2更新但还没有被提交的字段之后,若T2回滚,T1读取的内容就是临时且无效的,而一开始读取的数据也称为脏数据,是不正确的。
  • 不可重复读:对于事务A多次读取同一个数据时,由于其他是事务也在访问这个数据,进行修改且提交,对于事务A,读取同一个数据时,有可能导致数据不一致,叫不可重复读。
  • 幻读:对于两个事务T1,T2,T1在A表中读取了一个字段,然后T2又在A表中插入了一些新的数据时,T1再读取该表时,就会发现多出几行了。

(三)隔离级别

  为了避免以上出现的各种并发问题,mysql数据库系统提供了四种事务的隔离级别,用来隔离并发运行各个事务,使得它们相互不受影响,这就是数据库事务的隔离性。

  • read uncommitted(读未提交数据):允许事务读取未被其他事务提交的变更。(脏读、不可重复读和幻读的问题都会出现)。
  • read committed(读已提交数据):只允许事务读取已经被其他事务提交的变更。(可以避免脏读,但不可重复读和幻读的问题仍然可能出现)
  • repeatable read(可重复读):确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,禁止其他事务对这个字段进行更新(update)。(可以避免脏读和不可重复读,但幻读仍然存在)
  • serializable(串行化):确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作,所有并发问题都可避免,但性能十分低下。

MySQL基础语法与JDBC_第40张图片

查看当前会话隔离级别

show variables like 'transaction_isolation';

查看系统隔离级别

select @@global.transaction_isolation;

设置当前会话的隔离级别

set session transaction isolation level read uncommitted;

设置数据库系统的全局的隔离级别

set global transaction isolation level read uncommitted;

默认隔离级别

  • MySQL:read committed
  • Oracle:repeatable read

九、JDBC

(一)概述

MySQL基础语法与JDBC_第41张图片
  JDBC(Java DataBase Connectivity:java数据库连接)是一种用于执行SQL语句的Java API,是java官方提供的一套规范(接口),可以为多种关系型数据库提供统一访问,它是由一组用Java语言编写的类和接口组成的。
  事实上,基本上每一种语言都有自己提供给数据库的接口,数据库开发商依据这些接口(规范)来进行完善,并提供自己数据库的服务,例:
MySQL基础语法与JDBC_第42张图片
好处

  • 各数据库厂商使用相同的接口,Java代码不需要针对不同数据库分别开发。
  • 可随时替换底层数据库,访问数据库的Java代码基本不变。

使用JDBC开发用到的包

说明
java.sql 存在于JDK中,提供所有与JDBC访问数据库相关的接口和类
javax.sql 数据库扩展包,提供数据库额外的功能,如,数据库连接池
数据库驱动 由各大数据库厂商提供,需要额外下载,是对JDBC接口实现的类,如,MySQL的mysql-connector-java

Maven导入jar包
MySQL提供的JDBC实现称为mysql-connector-java,不同的数据库版本需要使用不同的Connector。实际开发时根据数据库版本、JDK版本、选择不同的Connector。

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

使用步骤

  1. 注册驱动
  2. 获取连接
  3. 获取执行SQL语句的对象
  4. 执行SQL
  5. 处理返回的结果
  6. 释放资源

案例

import org.junit.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class MyTest {
    @Test
    public void test1() throws Exception {
        //1.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.获取连接对象
        String url="jdbc:mysql://127.0.0.1:3306/mybatis?useSSL=false&serverTimezone=GMT%2B8";
        String username="root";
        String password="123456";
        Connection connection= DriverManager.getConnection(url,username,password);
        //3.获取SQL的执行对象
        Statement statement=connection.createStatement();
        //4.执行SQL语句
        String sql="select * from student";
        ResultSet resultSet=statement.executeQuery(sql);
        //5.处理返回结果
        while(resultSet.next()){
            //根据列编号获取数据
            String SNo=resultSet.getString(1);
            String SName=resultSet.getString(2);
            String SSex=resultSet.getString(3);
            String SBirthday=resultSet.getString(4);
            String ClassNo=resultSet.getString(5);
            System.out.println("学生学号为:"+SNo+",姓名为:"+SName+",性别为:"+SSex+",生日为:"+SBirthday+",班级号为:"+ClassNo);
        }
        //6.释放资源
        resultSet.close();
        statement.close();
    }
}

(二)常用API

  JDBC中定义了各种操作数据库的接口和类型:

接口 作用
Driver 驱动接口,定义建立链接的方式
DriverManager 工具类,用于管理驱动,可以获取数据库的链接
Connection 表示Java与数据库建立的连接对象(接口)
Statement 发送SQL语句的工具(接口)
PreparedStatement 发送SQL语句的工具,是Statement的子接口
CallableStatement 是PreparedStatement的子接口,专门用于执行数据库中的存储过程。
ResultSet 结果集接口,用于获取查询语句的结果

①Driver

  Driver,数据库驱动,可用于加载驱动、注册驱动:

Class.forName(数据库驱动实现类);

MySQL厂商提供了该实现类,位置为:“com.mysql.cj.jdbc.Driver”,在使用反射机制后,会自动执行相关的静态代码来完成驱动的加载。
源码

// Driver 接口,所有数据库厂商必须实现的接口,表示这是一个驱动类。
public class Driver implements java.sql.Driver { 
  public Driver() throws SQLException {
  }

  static { 
    try {
      DriverManager.registerDriver(new Driver());   
      //注册数据库驱动
    } 
    catch (SQLException var1) {
      throw new RuntimeException("Can't register driver!"); 
      }
    }
  
}

注:可见,其底层使用的是DriverManager的静态方法。

②DriverManager

  DriverManager,驱动管理类,位于java.sql包中,主要用于管理和注册驱动、获取数据库连接。
常用方法

方法名 说明
static void registerDriver(java.sql.Driver driver) 静态方法,用于注册驱动,其参数即为"com.mysql.cj.jdbc.Driver"
static Connection getConnection(String url, String user, String password) 静态方法,用于获取数据库连接并返回连接对象

事实上,在之前的案例当中并未使用registerDriver注册数据库驱动,这是因为使用了:

Class.forName("com.mysql.cj.jdbc.Driver");

底层有静态代码块,自动调用了该函数。
对于getConnection方法:

  • url:指定连接的路径。语法:jdbc:mysql://ip地址(域名):端口号/数据库名称。
  • user:用户名。
  • password:密码。

MySQL中URL的写法MySQL基础语法与JDBC_第43张图片
(ip地址可用域名,本地即为localhost,而127.0.0.1即为本地ip地址)

常用参数

//mysql编码默认为GBK,此参数的作用就是允许插入unicode字符集中的数据,使用UTF-8编码方式进行编码,可用于解决乱码问题.
useUnicode = true&characterEncoding=UTF-8
//SSL是高版本Mysql提供的数据加密、安全保障的新协议,为了向下兼容所以设置为false关闭此协议.
useSSL=false
//为了确保日期类型数据能够正确存储,需要指定时区为上海时区(上海时区与北京一致),默认为美国时区。
serverTimezone=Asia/Shanghai

常用数据库的URL写法

  • Oracle:jdbc:oracle:thin:@localhost:1521:数据库名
  • SqlServer:jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=数据库名
  • MySql:jdbc:mysql://localhost:3306/mysql-db

注:MySQL 5之后的驱动包,可以省略注册驱动的步骤,因为其会自动加载jar包中的META-INF/services/java.sql.Driver文件中的驱动类。

③Connection

  Connection,数据库连接对象,可以通过对象DriverManager或者DataSource(数据库连接池)中的getConnection()方法拿到Connection对象,获取到数据库链接,有以下功能:

  • 获取执行者对象
    • 获取普通执行者对象:Statement createStatement();
    • 获取预编译执行者对象:PreparedStatement prepareStatement(String sql);
    • 获取存储过程执行者对象:CallableStatement prepareCall(String);
  • 管理事务
    • 开启事务:setAutoCommit(boolean autoCommit); 参数为false,则开启事务。
    • 提交事务:commit();
    • 回滚事务:rollback();
  • 释放资源
    • 立即将数据库连接对象释放:void close();

对于事务管理

        //注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //获取连接对象
        String url="jdbc:mysql://127.0.0.1:3306/student_course?useSSL=false&serverTimezone=GMT%2B8";
        String username="root";
        String password="123456";
        Connection connection=DriverManager.getConnection(url,username,password);
        //获取SQL执行对象
        Statement statement= connection.createStatement();
        try {
            //开启事务
            connection.setAutoCommit(false);
            //编写并执行SQL语句
            String sql1="update Student set SAge=SAge+1 where  Sno like '1001';";
            statement.executeUpdate(sql1);
            String sql2="update Student set SAge=SAge-1 where  Sno like '1002';";
            statement.executeUpdate(sql2);
            connection.commit();
        } catch (Exception e) {
            //回滚事务
            connection.rollback();
            e.printStackTrace();
        }
        //释放资源
        statement.close();
        connection.close();

④Statement、PreparedStatement、CallableStatement与SQL注入

1.Statement

  Statement,是SQL语句的执行对象,常用方法:

方法声明 作用
int executeUpdate(String sql) 可执行增、删、改的SQL语句,返回受执行影响到的行数。
ResultSet executeQuery(String sql) 执行SQL查询语句,并返回ResultSet对象。
boolean execute(String sql) 可执行任何SQL语句,返回一个布尔值,表示是否返回ResultSet对象,只有执行查询时才为true。
void close() 用于释放执行对象资源
2.PreparedStatement

  PreparedStatement,是Statement的子接口,是预编译的SQL语句的对象,可使用占位符,且,PreparedStatement是预编译的,其批处理效率要高于Statement。
  
关于预编译
对于Statement对象,若要执行两条SQL语句:

select SName from student where SNo like '101';
select SName from student where SNo like '102';

这会生成两个执行计划,而PreparedStatement可使用绑定变量重用执行计划,即,将:

select SName from student where SNo like ;

绑定到一个变量上,使得之后再使用相似语句时不用多次重复编译,而只需传入参数即可,当然,在对数据库只执行一次性存取的时侯,用 Statement 对象进行处理。PreparedStatement对象的开销比Statement大,对于一次性操作并不会带来额外的好处(不同的SQL语句需创建多个PreparedStatement对象)。
即:无论多少次地使用同一个SQL命令,PreparedStatement都只对它解析和编译一次,而当使用Statement对象时,每次执行一个SQL命令时,都会对它进行解析和编译。这就使得preparedstatement支持批处理,且此时性能优于Statement对象。
开启预编译功能
  事实上,我们可手动开启PreparedStatement对象的预编译功能,需要在URL中添加参数(因为检查、预编译是由数据库完成的):

useServerPrepStmts=true

使用方式
  通过Connection对象来获取:

PreparedStatement prepareStatement(String sql)

可见,PreparedStatement对象需要先传入SQL语句来进行预编译,在明确sql语句的格式后,就不会改变了,剩余的内容都会认为是参数。
  而参数的传入,使用的是占位符"?",可使用:

setXxx(参数1,参数2)
//参数1:?的位置编号,从1开始.
//参数2:?的实际传入参数.
//Xxx:?传入参数的数据类型

例:

        //3.获取SQL的执行对象
        String sql="select * from score where SNo = ? and ID = ? ";
        PreparedStatement prepareStatement=connection.prepareStatement(sql);
        prepareStatement.setString(1,"103");
        prepareStatement.setInt(2,4);
        //4.执行SQL语句
        ResultSet resultSet=prepareStatement.executeQuery();

常用方法

方法声明 作用
int executeUpdate() 可执行增、删、改的SQL语句,返回受执行影响到的行数。
ResultSet executeQuery() 执行SQL查询语句,并返回ResultSet对象。
boolean execute() 可执行任何SQL语句,返回一个布尔值,表示是否返回ResultSet对象,只有执行查询时才为true。
void close() 用于释放执行对象资源

注:因为PreparedStatement对象在创建时就指定了SQL语句,故而,执行时不需要再传递参数。

3.CallableStatement

  CallableStatement,存储过程执行者对象,是PreparedStatement的子接口,可用于调用存储在数据库当中的存储过程,同时也具有对PreparedStatement接口输入参数功能的支持。
使用方式
  CallableStatement,通过 Connection 对象的方法 prepareCall 创建,需要指定执行语句的String字符串,语法格式为:

CallableStatement callableStatement= connection.prepareCall("{call 存储过程名(占位符1,占位符2,...)}");

通过使用占位符来传递IN、OUT和INOUT参数,对于IN类型参数的设置同样使用PreparedStatement中的setXxx方法,而OUT参数的设置需要使用CallableStatement中的registerOutParameter(int parameterIndex, int sqlType)来进行设置(用于注册OUT参数的JDBC类型),再通过getXxx来获取输出值(将参数从JDBC类型转换为Java类型),例如,现有存储过程getCountBySex,即传入性别字符串来统计人数:

create
    definer = root@localhost procedure getCountBySex(IN s_sex char, OUT s_count int)
BEGIN
		-- 把SQL中查询的结果通过INTO赋给变量
		SELECT COUNT(*) INTO s_count FROM student WHERE SSex= s_sex;		
	END;

Java代码:

        //获取存储过程的执行对象
        CallableStatement callableStatement= connection.prepareCall("{call getCountBySex(?,?)}");
        //设置IN类型参数
        callableStatement.setString(1,"男");
        //设置OUT类型参数
        callableStatement.registerOutParameter(2, Types.TINYINT);
        //获取返回值,使用getXxx方法
        int count=callableStatement.getInt(2);
        System.out.println(count);
        //释放资源
        callableStatement.close();

对于INOUT类型的参数,则既需要使用setXxx进行传入,也需要使用registerOutParameter进行注册,再通过getXxx来获取输出值。

4.SQL注入

  SQL注入,是web应用程序对用户输入数据的合法性判断不严格,攻击者可以在web应用程序中事先定义好的查询语句结尾加上额外的SQL语句,使得在管理员不知情的情况下事先非法操作,实现欺骗数据库执行非授权的任意查询,从而进一步得到相应的数据信息
SQL注入的演示

show databases ;
create database MyDatabase;
use MyDatabase;
create table tb_user(
    id int,
    username varchar(20),
    password varchar(32)
);
insert into tb_user values (1,'zhangsan','123'),(2,'lisi','234');
select * from tb_user;

后端代码

package com.example.jdbcmysql;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;

@SpringBootApplication
public class JdbcMySqlApplication {
    public static void main(String[] args) throws Exception {
        //注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        //获取连接对象
        String url="jdbc:mysql://127.0.0.1:3306/MyDatabase?useSSL=false&serverTimezone=GMT%2B8";
        String username="root";
        String password="123456";
        Connection connection=DriverManager.getConnection(url,username,password);
        //获取SQL执行对象
        Statement statement= connection.createStatement();
        String name="zhangsan";
        String pwd="123";
        String sql="select * from tb_user where username='"+name+"'and password ='"+pwd+"'";
        ResultSet resultSet=statement.executeQuery(sql);
        //若结果集不为空,即为登录成功
        if(resultSet.next()){
            System.out.printf("登录成功");
        }else {
            System.out.printf("登录失败");
        }
        resultSet.close();
        statement.close();
        connection.close();
    }
}

MySQL基础语法与JDBC_第44张图片
但若输入的pwd为SQL语句时(这里就不写前端页面了):

        //获取SQL执行对象
        Statement statement= connection.createStatement();
        String name="cascascwa";
        String pwd="' or '1' = '1";
        String sql="select * from tb_user where username='"+name+"'and password ='"+pwd+"'";
        ResultSet resultSet=statement.executeQuery(sql);

MySQL基础语法与JDBC_第45张图片
这是因为,Statement对象在执行SQL语句和前端传递来的参数时,底层实际上是字符串的拼接,将以上代码拼接后即可得到:

select * from tb_user where username='cascascwa'and password ='' or '1' = '1'

而这是永真的。
使用PreparedStatement对象预防SQL注入
  SQL注入的核心是拼接字符串而改变了原先的SQL语句,而PreparedStatement是先将SQL语句框架编译,并用反斜杠的方式来避免此问题.

        //获取SQL执行对象
        Statement statement= connection.createStatement();
        String name="zhangsan";
        String pwd="' or '1' = '1";
        String sql="select * from tb_user where username = ? and password = ?";
        //获取PreparedStatement对象
        PreparedStatement preparedStatement=connection.prepareStatement(sql);
        //设置?的值
        //给第一个?设置值,并指定参数类型
        preparedStatement.setString(1,name);
        //给第二个?设置值,并指定参数类型
        preparedStatement.setString(2,pwd);

MySQL基础语法与JDBC_第46张图片
底层原理
  开启数据库的慢查询功能来生成日志文件:

# 配置MySQL执行日志
log-output=FILE
general-log=1
general_log_file="D:\MySQL\mysql.log"
slow-query-log=1
slow_query_log_file="D:\MySQL\mysql_slow.log"
long_query_time=2

在这里插入图片描述
可见,是通过添加反斜杠""的方式来防止SQL注入。

⑤ResultSet

  ResultSet,结果集对象,用于接收查询操作所返回的结果集,常用方法有:

方法声明 作用
boolean next() 若当前游标的指向有数据,则返回true,并下移一行,否则返回false
xx getXx(int index) 获取该行某个字段的数据,index为该字段的索引编号,从1开始,xx则表示该列的数据类型。
xx getXx(String name) 根据字段名获取该行某个字段的数据,xx则表示该列的数据类型。
void close() 释放结果集对象资源

(三)数据库连接池Druid

  JDBC编程中,每次创建和断开Connection对象都会消耗一定的时间和IO资源。这是因为在Java程序与数据库之间建立连接时,数据库端要验证用户名和密码,并且要为这个连接分配资源,Java程序则要把代表连接的java.sql.Connection对象等加载到内存中,所以建立数据库连接的开销很大。尤其是在大量的并发访问时,假如某网站一天的访问量是10万,那么,该网站的服务器就需要创建、断开连接10万次,频繁地创建、断开数据库连接势必会影响数据库的访问效率,甚至导致数据库崩溃。为了避免频繁地创建数据库连接,工程师们提出了数据库连接池技术。
  数据库连接池是一个容器,负责分配、管理数据库连接,可以提前申请多个数据库连接,当用户需要时从中取出即可,当不需要时重新归还给数据库连接池即可,从而实现了资源复用,也不需要避免不断地申请、释放。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池的请求连接数目大于最大连接数时,这些请求将被加入等待队列中。
好处

  • 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。
  • 资源重用。
  • 提升系统响应速度。
    MySQL基础语法与JDBC_第47张图片
    实现
      JDBC提供了数据库连接池DataSource的标准接口给数据库连接池开发商来实现,可通过他来获取数据库连接:
Connection getConnection(String username, String password)

常见的数据库连接池

  • DBCP:是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持。
  • C3P0:是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以,hibernate官方推荐使用。
  • Proxool:是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
  • Druid:是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,但是速度不确定是否有BoneCP快,其可由很好地监控数据库连接池和SQL的执行情况,是目前最好的数据库连接池之一。

Druid数据库连接池
  Druid连接池是由阿里巴巴开源的数据库连接池项目,功能强大,性能优秀,是Java语言最好的数据库连接池之一。
基本配置参数
MySQL基础语法与JDBC_第48张图片

1.导入依赖

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.15</version>
        </dependency>

2.定义配置文件

jdbc.driverClassName=com.mysql.cj.jdbc.Driver;
url=jdbc:mysql://127.0.0.1:3306/MyDatabase?useSSL=false&serverTimezone=GMT%2B8&useServerPrepStmts=true&characterEncoding=utf-8&useUnicode=true
username=root
password=nishiweiyi456
#初始化连接数量
initialSize=5
#最大连接数
maxActive=10
#最大等待时间,此处为3s
maxWait=3000

3.获取连接池对象

package com.example.jdbcmysql;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties;

public class Druid {
    public static void main(String[] args) throws Exception{
        //加载配置文件
        Properties properties=new Properties();
        properties.load(new FileInputStream("D:/IntelliJ IDEA/JDBCMySQL/src/main/resources/druid.properties"));
        //获取连接池对象
        DataSource dataSource= DruidDataSourceFactory.createDataSource(properties);
        //获取数据库连接
        Connection connection= dataSource.getConnection();
        System.out.println(connection);
    }
}

MySQL基础语法与JDBC_第49张图片

十、索引与源码

先留个坑,还是以考研为主,且要先复习完数据结构,对于所有和JDBC源码的解析有时间会补上。

十一、常用数据库案例(含数据)

最后,提供一些创建数据库表的案例,方便以后使用:
学生表

CREATE TABLE IF NOT EXISTS `student`(
                                        `id` INT(4)    NOT NULL AUTO_INCREMENT COMMENT '学号',
                                        `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
                                        `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
                                        `sex` VARCHAR(2) NOT NULL DEFAULT '女' COMMENT '性别',
                                        `birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
                                        `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
                                        `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
                                        PRIMARY KEY (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;
show columns from student;

学生表

create database student_course;
show databases;
use student_course;
create table Student(Sno char(9) primary key,SName char(20) not null,SSex char(2),SAge smallint,SDept char(20));
create table Course(Cno char(4) primary key,CName char(40) not null,CPno char(4) references Course(Cno),CCredit smallint);
create table SC(Sno char(9),Cno char(4),Grade smallint,primary key(Sno,Cno),foreign key(Sno) references Student(Sno),foreign key (Cno) references Course(Cno));


insert into Student(Sno,Sname,Ssex,Sage,Sdept)values('201215121','李勇','男','20','CS');
insert into Student(Sno,Sname,Ssex,Sage,Sdept)values('201215122','刘晨','女','19','CS');
insert into Student(Sno,Sname,Ssex,Sage,Sdept)values('201215123','王敏','女','18','MA');
insert into Student(Sno,Sname,Ssex,Sage,Sdept)values('201215125','张立','男','19','IS');
insert into Course(Cno,Cname,Cpno,Ccredit)values('1','数据库','5','4');
insert into Course(Cno,Cname,Cpno,Ccredit)values('2','数学','null','2');
insert into Course(Cno,Cname,Cpno,Ccredit)values('3','信息系统','1','4');
insert into Course(Cno,Cname,Cpno,Ccredit)values('4','操作系统','6','3');
insert into Course(Cno,Cname,Cpno,Ccredit)values('5','数据结构','7','4');
insert into Course(Cno,Cname,Cpno,Ccredit)values('6','数据处理','null','2');
insert into Course(Cno,Cname,Cpno,Ccredit)values('7','C语言','6','4');
insert into SC(Sno,Cno,Grade)values('201215121','1','92');
insert into SC(Sno,Cno,Grade)values('201215121','2','85');
insert into SC(Sno,Cno,Grade)values('201215121','3','88');
insert into SC(Sno,Cno,Grade)values('201215122','2','90');
insert into SC(Sno,Cno,Grade)values('201215122','3','80');

学生、教师、课程、分数表

create schema student_course;
CREATE TABLE `student` (
  `SNo` varchar(20) NOT NULL COMMENT '学号(主码)',
  `SName` varchar(20) NOT NULL COMMENT '学生姓名',
  `SSex` varchar(20) NOT NULL COMMENT '学生性别',
  `SBirthday` datetime DEFAULT NULL COMMENT '学生出生年月',
  `ClassNo` varchar(20) DEFAULT NULL COMMENT '学生所在班级',
  PRIMARY KEY (`SNo`),
  KEY `student_fk_class` (`ClassNo`),
  CONSTRAINT `student_fk_class` FOREIGN KEY (`ClassNo`) REFERENCES `class` (`CLNo`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

CREATE TABLE `course` (
  `CNo` varchar(20) NOT NULL COMMENT '课程号(主码)',
  `CName` varchar(20) NOT NULL COMMENT '课程名称',
  `TNo` varchar(20) NOT NULL COMMENT '教工编号',
  PRIMARY KEY (`CNo`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

CREATE TABLE `score` (
  `ID` int NOT NULL AUTO_INCREMENT COMMENT '主键自增',
  `SNo` varchar(20) NOT NULL COMMENT '学号',
  `CNo` varchar(20) NOT NULL COMMENT '课程号',
  `Degree` decimal(4,1) DEFAULT NULL COMMENT '成绩',
  PRIMARY KEY (`ID`),
  KEY `score_fk_student` (`SNo`),
  KEY `score_fk_course` (`CNo`),
  CONSTRAINT `score_fk_course` FOREIGN KEY (`CNo`) REFERENCES `course` (`CNo`),
  CONSTRAINT `score_fk_student` FOREIGN KEY (`SNo`) REFERENCES `student` (`SNo`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

CREATE TABLE `teacher` (
  `TNo` varchar(20) NOT NULL COMMENT '教工编号(主码)',
  `TName` varchar(20) NOT NULL COMMENT '教工姓名',
  `TSex` varchar(20) NOT NULL COMMENT '教工性别',
  `TBirthday` datetime DEFAULT NULL COMMENT '教工出生年月',
  `Prof` varchar(20) DEFAULT NULL COMMENT '职称',
  `Depart` varchar(20) NOT NULL COMMENT '教工所在部门',
  `ClassNo` varchar(20) DEFAULT NULL COMMENT '班级编号',
  PRIMARY KEY (`TNo`),
  KEY `teacher_fk_class` (`ClassNo`),
  CONSTRAINT `teacher_fk_class` FOREIGN KEY (`ClassNo`) REFERENCES `class` (`CLNo`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 

CREATE TABLE `class` (
  `CLNo` varchar(20) NOT NULL COMMENT '班级序号',
  `TNo` varchar(20) NOT NULL COMMENT '教工编号',
  PRIMARY KEY (`CLNo`),
  KEY `class_fk_teacher` (`TNo`),
  CONSTRAINT `class_fk_teacher` FOREIGN KEY (`TNo`) REFERENCES `teacher` (`TNo`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='班级表'COLLATE=utf8mb4_0900_ai_ci

insert into student values
                        ('108','曾华','男','1977-09-11','95033'),
                        ('105','匡明','男','1978-08-01','95031'),
                        ('107','王丽','女','1979-06-15','95033'),
                        ('101','李军','男','1976-07-21','95033'),
                        ('109','王芳','女','1977-03-30','95031'),
                        ('103','陆君','男','1977-12-16','95031');
insert into course values
                       ('3-105','计算机导论','825'),
                       ('3-245','操作系统','804'),
                       ('6-166','数字电路','856'),
                       ('9-888','高等数学','831');
insert into score(sno,cno,degree) values
                                      ('103','3-245','86'),
                                      ('105','3-245','75'),
                                      ('109','3-245','68'),
                                      ('103','3-105','92'),
                                      ('105','3-105','88'),
                                      ('109','3-105','76'),
                                      ('101','3-105','64'),
                                      ('107','3-105','91'),
                                      ('108','3-105','78'),
                                      ('101','6-166','85'),
                                      ('107','6-166','79'),
                                      ('108','6-166','81');
insert into teacher values
                        ('804','李诚','男','1958-12-02','副教授','计算机系','95033'),
                        ('856','张旭','男','1969-03-12','讲师','电子工程系'),
                        ('825','王萍','女','1972-05-05','助教','计算机系'),
                        ('831','刘冰','女','1977-08-14','助教','电子工程系','95031');
                        
insert into class values
					('95033','804'),
					('95031','831');

你可能感兴趣的:(后端开发,mysql,数据库,sql,后端,database)