MySQL学习

本文详细介绍了MySQL的安装、配置、数据库和数据表的管理,以及数据操作语言(DML)、数据查询语言(DQL)的使用。讨论了事务处理的ACID原则和四种事务隔离级别,讲解了索引的原理和使用,以及JDBC连接数据库的基本操作。此外,还探讨了数据库设计的规范性和防止SQL注入的方法。
摘要由CSDN通过智能技术生成

MySQL

一、简介

1.1、安装

  1. 解压

  2. 把文件夹放到电脑环境目录下

  3. 配置环境变量

  4. 新建mysql配置文件ini

    [mysql]
    # 设置mysql客户端默认字符集
    default-character-set=utf8 
    [mysqld]
    #设置3306端口
    port = 3306 
    # 设置mysql的安装目录
    basedir=D:\mysql\mysql-5.6.33-winx64\
    # 设置mysql数据库的数据的存放目录
    datadir=D:\mysql\mysql-5.6.33-winx64\data\
    # 允许最大连接数
    max_connections=200
    # 服务端使用的字符集默认为8比特编码的latin1字符集
    character-set-server=utf8
    # 创建新表时将使用的默认存储引擎
    default-storage-engine=INNODB
    
  5. 启动管理员下的CMD,运行Mysql安装

  6. 安装mysql服务:mysqld -install

  7. 初始化数据库:mysqld --initialize-insecure --user=mysql

  8. 启动mysql,修改密码:set password for root@localhost = password(‘123456’);

  9. 如果ini文件中有跳过密码检查,要删除或者注释掉

  10. 删除数据库:sc delete mysql

1.2、什么是数据库

数据库(DB,DataBase)

概念:数据仓库,软件,安装在操作系统(windows,linux,mac…)之上,可以存储大量数据

1.3、数据库分类

关系型数据库(SQL):有行和列

  • MySQL、Oracle、Sql Server、DB2、SQLite
  • 通过表和表之间,行和列之间的关系进行数据存储

非关系型数据库(No SQL):键值对

  • Redis,MongDB
  • 非关系型数据库,对象存储,通过对象自身属性来决定

DBMS(数据库管理系统)

  • 数据库管理软件,科学有效的管理数据,维护和获取数据

1.4、MySQL三层结构

DBMS(数据库管理系统) -> 数据库 -> 数据表

  1. 所谓安装数据库就是在主机装一个数据库管理系统(DBMS),管理程序可以管理多个数据库(DataBaseManageSystem)

  2. 一个数据库可以建多个表,用来保存信息

    表的一行称之为一条记录,在Java程序中往往用对象表示

1.5、连接数据库

命令行:所有语句以分号结尾

mysql -uroot -p123456 

update mysql.user set authenticantion_string=password('123456') where user='roor' and Host='localhost'		--修改密码

flush privileges;		--刷新权限

exit;		--退出连接

-- 单行注释
/*
多行注释
*/

DDL:数据库定义语言

DML:数据库操作语言

DQL:数据库查询语言

DCL:数据库控制语言

二、操作数据库

操作数据库 > 操作数据表 >操作数据

mysql不区分大小写,如果表名或者字段名是特殊字符需要带 ``

2.1、操作数据库

增删改查数据库
create database if not exists 数据库名;
drop database 数据库名;
use database 数据库名;
show databases;

2.2、数据库的数据类型

数值:

  1. tinyint 1个字节
  2. smallint 2个字节
  3. mediumint 3个字节
  4. int 标准数据类型 4个字节
  5. bigint 8个字节
  6. float 4个字节
  7. double 8个字节
  8. decimal 字符串形式浮点数

字符串

  1. char 固定大小字符串 0~255
  2. varchar 可变字符串 0~65535
  3. tinytext 微型文本 2^8-1
  4. text 文本串 2^16-1

时间日期

java.util.Data

  • date YYYY-MM-DD 日期格式
  • time HH:mm:ss 时间格式
  • datetime YYYY-MM-DD HH:mm:ss 最常用的时间格式
  • timestamp 时间戳 1970.1.1到现在的毫秒数

null

空值,不要使用null进行运算

2.3、数据库字段属性

Unsigned:无符号整数

  • 声明之后该列不能为负数

zerofill:0填充

  • 不足的位数用0填充

auto_increment:自增,自动在上一条记录的基础上加一

  • 通常用来设置唯一的主键,且必须是整数类型

not NULL:非空

default:默认值报错可能需要用’'引起来

2.4、操作数据表

除了最后一条其余语句句末加 , 号,表名尽量用``括起来

2.4.1.新建表
CREATE TABLE `teacher` (
  `id` int(4) NOT NULL AUTO_INCREMENT COMMENT '教师编号',
  `name` varchar(20) DEFAULT '匿名' COMMENT '教师姓名',
  `sex` char(2) DEFAULT '女' COMMENT '性别',
  `birthday` datetime DEFAULT NULL COMMENT '出生日期',
  `email` varchar(200) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

show CREATE table teacher;查看建表语句

DESC teacher查看表结构

2.4.2.修改表
ALTER TABLE teacher RENAME AS myteacher;	--修改表名
ALTER TABLE teacher ADD `age` INT(11);		--增加字段
ALTER TABLE teacher MODIFY age VARCHAR(11);		--修改字段约束,不能重命名
ALTER TABLE teacher CHANGE age `year` int(3);		--重命名
ALTER TABLE teacher DROP year;		--删除字段
2.4.3.删除表
DROP TABLE teacher;

2.5、数据表类型

2.5.1.MySQL引擎
MYISAMINNODB
事务支持(一个成功其他失败,统统不提交)不支持支持
数据行锁定不支持支持
外键约束不支持支持
全文索引支持不支持
表空间大小较小较大,余额为MYISAM两倍

ps:数据行锁定:在进行表查询时,类似java会对表中内容上锁,MYISAM对整张表上锁,INNODB对单行上锁,因此INNODB效率更高。

区别

  • MYISAM:节约空间,速度快

  • INNODB:安全性高,事务处理,多表多用户操作

  • 存储方式:

    数据库所有内容都存在data文件夹下

    1. INNODB
      • 对应数据库名文件夹下的 *.frm 文件以及上级目录下的 ibdata1文件
    2. MYISAM
      • *.frm 表结构定义文件
      • *.MYD 数据文件(data)
      • *.MYI 索引文件(index)
2.5.2.编码

MySQL默认编码Latin1,不支持中文

修改方式:

  1. 在建表时设置字符集编码:CHARSET=utf-8
  2. 在my.ini中配置默认编码:character-set-server=utf-8(不建议,本地设置了到其他地方可能出现编码问题)

三、MySQL数据管理

3.1、外键(了解)

方式一:建表的时候添加约束

-- 学生表的 gradeid 字段 去引用年级表的 gradeid 字段
-- 定义外键key
-- 给外键添加约束(执行引用) references引用
CREATE TABLE `student` (
  `id` int(10) unsigned zerofill NOT NULL AUTO_INCREMENT COMMENT '学员ID\r\n',
  `name` varchar(100) DEFAULT NULL COMMENT '学员姓名',
  `age` int(3) DEFAULT NULL COMMENT '学员年龄',
  `gradeid` varchar(255) NOT NULL COMMENT '学员年级',
  PRIMARY KEY (`id`),
	KEY `FK_gradeid` (`gradeid`),
	CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

方式二:修改表添加外键约束

ALTER TABLE `student`
ADD CONSTRAINT `FK_gradeid` FOREIGN KEY(`gradeid`) REFERENCES `grade`(`gradeid`);

以上都是物理外键,数据库级别的,不建议使用。

3.2、DML语言**(背)**

数据操作语言

insert
-- 插入字段需要字段名和数值对应,插入多个字段用 , 号隔开
INSERT INTO `student` (`name`,`age`) VALUES('Darwin','22'),('Alex','21'),('Sya','23')
update

需要加条件,否则改动整列数据

-- 修改名字
update `student` set `name`='Alexander' where `name`='Alex';
-- 不指定条件情况下修改的是整列数据
update `student` set `name`='Darwin';
-- 修改多列
update `student` set `name`='Alexander',`age`='121' where `name`='Alex';

条件:where语句

操作符含义
=等于
<>或!=不等于
between… and …在某个范围内
AND多个条件
OR多个条件任意一个成立即可
delete

delete from 表名 where 条件

-- 删除指定数据,不加条件就是删除整表数据
DELETE FROM `student` WHERE `id`=2;

truncate table 表名

  • 相同点:都能删除数据
  • 不同:
    • truncate 重新设置自增列,计数器会清零
    • truncate 不会影响事物

在不同的引擎中使用delete:

  • InnoDB自增列会从1开始,因为InnoDB引擎下断电即失,从现存记录里最大的自增
  • MyISAM从现有记录的自增量开始

四、DQL语言(数据库查询语言)

select 语法

|:表示或者,{}:必填项,[]:可选项

SELECT [去重]*|.*| [.列名1 [ AS 别名1],.列名2 [AS 别名2],[... ...] ]FROM 表名 [AS 别名]
	[左连接|右连接|内连接 表名2]	-- 连接查询
	[WHERE ...]		-- 指定结果需要满足的条件
	[GROUP BY ...]		-- 指定结果按照那几个字段来分组
	[HAVING]		-- 过滤分组记录必须满足的次要条件
	[ORDER BY ...]		-- 通过哪个字段排序【升序/降序】
	[LIMIT {起始位置,每页显示数据}]		-- 指定分页查询记录起始位置以及每页展示数据

Select语法

实例代码:

DROP DATABASE IF EXISTS `school`;
-- 创建一个school数据库
CREATE DATABASE IF NOT EXISTS `school`;
-- 使用school数据库
USE `school`;
-- 创建学生表
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`(
    `student_no` INT(4) NOT NULL COMMENT '学号',
    `login_pwd` VARCHAR(20) DEFAULT NULL,
    `student_name` VARCHAR(20) DEFAULT NULL COMMENT '学生姓名',
    `sex` TINYINT(1) DEFAULT NULL COMMENT '性别,0或1',
    `grade_id` INT(11) DEFAULT NULL COMMENT '年级编号',
    `phone` VARCHAR(50) NOT NULL COMMENT '联系电话',
    `address` VARCHAR(255) NOT NULL COMMENT '地址',
    `born_date` DATETIME DEFAULT NULL COMMENT '出生时间',
    `email` VARCHAR (50) NOT NULL COMMENT '邮箱账号',
    `identity_card` VARCHAR(18) DEFAULT NULL COMMENT '身份证号',
    PRIMARY KEY (`student_no`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;

-- 创建年级表
DROP TABLE IF EXISTS `grade`;
CREATE TABLE `grade`(
  `grade_id` INT(11) NOT NULL AUTO_INCREMENT COMMENT '年级编号',
  `grade_name` VARCHAR(50) NOT NULL COMMENT '年级名称',
   PRIMARY KEY (`grade_id`)
) ENGINE=INNODB DEFAULT CHARSET = utf8;

-- 创建科目表
DROP TABLE IF EXISTS `subject`;
CREATE TABLE `subject`(
  `subject_no`INT(11) NOT NULL AUTO_INCREMENT COMMENT '课程编号',
  `subject_name` VARCHAR(50) DEFAULT NULL COMMENT '课程名称',
  `class_hour` INT(4) DEFAULT NULL COMMENT '学时',
  `grade_id` INT(4) DEFAULT NULL COMMENT '年级编号',
   PRIMARY KEY (`subject_no`)
)ENGINE = INNODB  DEFAULT CHARSET = utf8;

-- 创建成绩表
DROP TABLE IF EXISTS `result`;
CREATE TABLE `result`(
  `student_no` INT(4) NOT NULL COMMENT '学号',
  `subject_no` INT(4) NOT NULL COMMENT '课程编号',
  `exam_date` DATETIME NOT NULL COMMENT '考试日期',
  `student_result` INT (4) NOT NULL COMMENT '考试成绩'
  )ENGINE = INNODB DEFAULT CHARSET = utf8;
  
-- 插入学生数据 其余自行添加 这里只添加了2行
INSERT INTO `student` (`student_no`,`login_pwd`,`student_name`,`sex`,`grade_id`,`phone`,`address`,`born_date`,`email`,`identity_card`)
VALUES
(1000,'123456','张伟',0,2,'13800001234','北京朝阳','1980-1-1','text123@qq.com','123456198001011234'),
(1001,'123456','赵强',1,3,'13800002222','广东深圳','1990-1-1','text111@qq.com','123456199001011233');

-- 插入年级数据
INSERT INTO `grade` (`grade_id`,`grade_name`) VALUES(1,'大一'),(2,'大二'),(3,'大三'),(4,'大四'),(5,'预科班');

-- 插入科目数据
INSERT INTO `subject`(`subject_no`,`subject_name`,`class_hour`,`grade_id`)VALUES
(1,'高等数学-1',110,1),
(2,'高等数学-2',110,2),
(3,'高等数学-3',100,3),
(4,'高等数学-4',130,4),
(5,'C语言-1',110,1),
(6,'C语言-2',110,2),
(7,'C语言-3',100,3),
(8,'C语言-4',130,4),
(9,'Java程序设计-1',110,1),
(10,'Java程序设计-2',110,2),
(11,'Java程序设计-3',100,3),
(12,'Java程序设计-4',130,4),
(13,'数据库结构-1',110,1),
(14,'数据库结构-2',110,2),
(15,'数据库结构-3',100,3),
(16,'数据库结构-4',130,4),
(17,'C#基础',130,1);

-- 插入成绩数据  这里仅插入了一组,其余自行添加
INSERT INTO `result`(`student_no`,`subject_no`,`exam_date`,`student_result`)
VALUES
(1000,1,'2013-11-11 16:00:00',85),
(1000,2,'2013-11-12 16:00:00',70),
(1000,3,'2013-11-11 09:00:00',68),
(1000,4,'2013-11-13 16:00:00',98),
(1000,5,'2013-11-14 16:00:00',58);

4.1、指定查询字段

 -- 查询全部的学生   SELECT 字段 FROM 表名;
  SELECT * FROM student;
  
  -- 查询指定字段
  SELECT student_name, student_no FROM student;
  
  -- 别名,给结果起一个名字 AS  可以给字段起别名,也可以给表起别名
  SELECT student_name AS '学号', student_no AS '姓名' FROM student;
  
  -- 函数 concat(a,b)
  SELECT CONCAT('姓名:', student_name)  AS '新姓名' FROM student;

语法:SELECT 字段,. . . FROM 表

4.2、去重复、操作数据库的列

distinct [dɪˈstɪŋkt]
不同的;明显的;清晰的;清楚的;明白的;有区别的;不同种类的;确定无疑的;确切的

-- 查询哪些同学参加了考试
SELECT * FROM result;		-- 查询全部成绩

SELECT DISTINCT `student_no` FROM result	-- 去重

语法:SELECT DISTINCT 字段 FROM 表名;

SELECT VERSION();	-- 查询系统版本(函数)
SELECT 100*3-41 AS '计算结果';	-- 计算(表达式)
SELECT @@auto_increment_increment;	-- 查询自增步长(变量)

-- 学员考试所有成绩加一分
SELECT `student_no`,`student_result` AS '提分前',`student_result`+1 AS '提分后' FROM `result`;

数据库中的表达式:文本、列、Null、函数、计算表达式、系统变量

语法:select 表达式 from 表

4.3、where条件子句

作用:检索数据中符合条件的值

条件:由一个或者多个表达式组成

1.逻辑运算符
运算符语法描述
and &&A and B A&&B逻辑与,全真为真
or ||A or B A||B逻辑或,一真为真
NOT !NOT A !A逻辑非,真为假假为真
-- 查询成绩在95~100
SELECT * FROM `result` WHERE `student_result`>=95 && `student_result`<=100;

-- 模糊查询(区间)
SELECT * FROM `result` WHERE `student_result` BETWEEN 95 AND 100;

-- 除了学号等于1000意外的所有学生
SELECT * FROM `student` WHERE student_no != 1000
SELECT * FROM student WHERE NOT student_no=1000 
2.比较运算符(模糊查询)
运算符语法描述
IS NULLA is null空即是真
IS NOT NULLA is not null非空为真
BETWEENA between B and C若A在[B,C]间为真
LIKEA like B如果AB中有共同元素,结果为真
INA in (A1,A2,A3…)如果A在A1~An某个值中,结果为真
-- 模糊查询	
-- 查询刘姓同学
-- 使用like结合 %(表示0到任意字符), _(一个字符)
SELECT * FROM `student` WHERE student_name LIKE '刘%';
-- 查询刘姓同学名字后面只有一个字的
SELECT * FROM `student` WHERE student_name LIKE '刘_';
-- 查询刘姓同学名字后面有两个字的
SELECT * FROM `student` WHERE student_name LIKE '刘__';
-- 查询名字中带有德字的同学
SELECT * FROM `student` WHERE student_name LIKE '%德%';
-- 查询学号1001~1003的学院
SELECT* FROM `student` WHERE student_no=1000 or student_no=1002 or student_no=1003;
SELECT * FROM `student` WHERE student_no in (1001,1002,1003);

-- 查询地址为空的学生 
-- 注意!!!
-- 空字符串 和 null 不一样
SELECT * FROM `student` WHERE address='';
SELECT * FROM `student` WHERE address IS NULL;

4.4、联表查询

联表查询中,join on(连接查询)和 join where(等待查询)没有太大区别

查询思路

要查什么数据就select什么,从哪张表查就from什么,没有的数据就join表,on什么条件,具体哪种join看查询需求

在这里插入图片描述

案例代码:

-- 联表查询
SELECT s.student_no,student_name,subject_no,student_result
FROM student AS s
INNER JOIN result AS r
ON s.student_no=r.student_no;

-- RIGHT JOIN
SELECT s.student_no,student_name,subject_no,student_result
FROM student AS s
RIGHT JOIN result AS r
ON s.student_no = r.student_no;

-- LEFT JOIN
SELECT s.student_no,student_name,subject_no,student_result
FROM student AS s
LEFT JOIN result AS r
ON s.student_no = r.student_no;

-- 查询缺考的同学
-- 联表查询
SELECT s.student_no,student_name,subject_no,student_result
FROM student AS s
INNER JOIN result AS r
ON s.student_no=r.student_no;

-- RIGHT JOIN
SELECT s.student_no,student_name,subject_no,student_result
FROM student AS s
RIGHT JOIN result AS r
ON s.student_no = r.student_no;

-- LEFT JOIN
SELECT s.student_no,student_name,subject_no,student_result
FROM student AS s
LEFT JOIN result AS r
ON s.student_no = r.student_no
WHERE student_result IS NULL;

-- 查询参加考试的学生信息(学号,姓名,科目,成绩)
SELECT s.student_no,student_name,subject_name,student_result
FROM student AS s
RIGHT JOIN result AS r
ON s.student_no = r.student_no
INNER JOIN `subject` AS sub
ON r.subject_no = sub.subject_no;

-- 查询学生学号,姓名,C语言成绩
SELECT	s.student_no,s.student_name,sub.subject_name,r.student_result
FROM student AS s
INNER JOIN result AS r
ON s.student_no = r.student_no
INNER JOIN `subject` AS sub
ON sub.subject_no = r.subject_no
WHERE sub.subject_name = 'C语言-1';
操作描述
Inner join两表有共同匹配项,就返回行
Left join即使右表没有匹配数据,也会从左表返回所有值
Right join即使左表没有匹配数据,也会从右表返回所有值
自连接查询

表和表自己连接,核心:一张表分为两张表

本张表中每个字段都有自己的id,和一个父id,通过子数据的父id去绑定对应的父数据自己的id即可实现

案例代码:

-- 创建表
-- unsigned 无符号
-- auto_increment=9 自增的起始值
DROP TABLE IF EXISTS `category` ;
CREATE TABLE `category` (
  `category_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主题id',
  `pid` INT(10) NOT NULL COMMENT '父id',
  `category_name` VARCHAR(50) NOT NULL COMMENT '主题名字',
  PRIMARY KEY (`category_id`)
) ENGINE=INNODB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

-- 插入值
INSERT INTO `category`(`category_id`,`pid`,`category_name`)
VALUES('2','1','信息技术'),
('3','1','软件开发'),
('4','3','数据库'),
('5','1','美术设计'),
('6','3','web开发'),
('7','5','ps技术'),
('8','2','办公信息');

SELECT * FROM `category`;

父表:

categoryIdcategoryName
2信息技术
3软件开发
5美术设计

子表

pid(父Id)categoryIdcategoryName
34数据库
28办公信息
36web开发
57美术设计

测试代码:

-- 自连接
-- 经测试,只能用inner join,因为不管左或右都有多余数据
SELECT f.category_id,f.category_name,c.category_id,c.pid,c.category_name
FROM category AS f
INNER JOIN category AS c
ON c.pid = f.category_id;

4.5、分页和排序

分页:limit

语法:limit (n-1)*pagesize,pagesize

n代表当前页

(n-1)*pagesize:显示数据起始位置

pagsize:每页显示数据数量

排序:order by

升序asc,降序desc

-- 查询 高等数学-1 倒数5名同学的成绩
SELECT s.student_no,student_name,subject_name,student_result
FROM student AS s
INNER JOIN result AS r
ON s.student_no = r.student_no
INNER JOIN `subject` AS sub
ON sub.subject_no = r.subject_no
WHERE subject_name = '高等数学-1'
ORDER BY student_result ASC
LIMIT 0,5

-- 查询 C语言-1 排名前十,并且分数大于80的学生信息
SELECT s.student_no AS '学生学号',student_name AS '学生姓名',subject_name AS '科目名',student_result AS '成绩'
FROM student AS s
INNER JOIN result AS r
ON s.student_no = r.student_no
INNER JOIN `subject` AS sub
ON sub.subject_no = r.subject_no
WHERE subject_name = 'C语言-1' AND r.student_result > 80
ORDER BY student_result desc
limit 0,10

4.6、子查询

where值是固定的,如果希望值是计算出来的需要通过子查询

本质:在where 语句中嵌套子查询

-- 查询C语言-1 的所有考试结果结果(学号,科目编号,成绩)降序
-- 方式一:使用连接查询
SELECT r.student_no,r.subject_no,student_result
FROM result AS r
INNER JOIN `subject` AS sub
ON r.subject_no = sub.subject_no
WHERE subject_name = 'C语言-1'
ORDER BY student_result DESC

-- 方式二:使用子查询
-- 在where条件中新建查询 科目名为 C语言的科目编号,并赋给成绩表的科目编号
SELECT student_no,subject_no,student_result
FROM result AS r
WHERE r.subject_no = (
		SELECT subject_no FROM `subject`
		WHERE subject_name = 'C语言-1'									
)
ORDER BY student_result DESC
-- 查询高等数学-2 分数不小于80分的学号和姓名
SELECT DISTINCT s.student_no,student_name
FROM student AS s
INNER JOIN result AS r
ON r.student_no = s.student_no
WHERE r.student_result >= 80 AND r.subject_no = (
		SELECT subject_no FROM `subject` 
		WHERE subject_name = '高等数学-1'
)

4.7、分组(Group By) 和 过滤(Having)

where判断只适用于分组前,分组后使用过滤添加查询条件

-- 查询不同课程的平均分,最高分,最低分
-- 核心 :根据不同的课程分组

SELECT subject_name,AVG(student_result) AS 平均分,MAX(student_result),MIN(student_result)
FROM result AS r
INNER JOIN `subject` AS sub
ON r.subject_no = sub.subject_no
GROUP BY r.subject_no
HAVING 平均分 > 80

五、MySQL函数

5.1、常用函数

官方文档:https://dev.mysql.com/doc/refman/5.7/en/built-in-function-reference.html

mysql中有大量函数可以使用

-- ===常用函数===
-- 数学运算
SELECT ABS(-8)	-- 绝对值
SELECT CEILING(9.4)		-- 向上取整
SELECT FLOOR(9.4)		-- 向下取整
SELECT RAND()		-- 返回一个0 ~1之间的随机数
SELECT SIGN(-100)		-- 返回参数符号

-- 字符串函数
SELECT CHAR_LENGTH('阿米豆腐')		-- 返回字符串函数
SELECT CONCAT('南五','阿米','豆腐')	AS 字符串		-- 拼接字符串
SELECT INSERT ('我爱天安门',3,4,'斯台普斯') -- 从某个位置插入(替换)某个长度
SELECT LOWER('Darwin')	-- 转小写
SELECT UPPER('Darwin') -- 转大写
SELECT INSTR('Darwin','w')	-- 返回某个字符第一次出现的位置
SELECT REPLACE('我爱天安门','天安门','斯台普斯')		-- 替换指定字符串
SELECT SUBSTR('我爱天安门',3,5)		-- 截取指定字符串,起止位置
SELECT REVERSE('赵兄托我办点事')		-- 反转字符串

-- 查询周姓同学,换位邹
SELECT REPLACE(student_name,'周','邹') FROM student
WHERE student_name LIKE '周%'

-- 时间和日期函数
SELECT CURRENT_DATE		-- 获取当前日期
SELECT NOW()		-- 获取当前时间:时分秒
SELECT LOCALTIME() -- 获取本地时间:时分秒
SELECT SYSDATE()		-- 获取系统时间

SELECT YEAR(NOW())
SELECT MONTH(NOW())
SELECT DAY(NOW())

-- 系统
SELECT SYSTEM_USER()
SELECT USER()
SELECT VERSION()

5.2、聚合函数

聚合函数对一组值执行计算并返回单一的值。除了 COUNT(*) 外,聚合函数都会忽略 Null 值。 聚合函数经常与 SELECT 语句的 GROUP BY 子句一起使用。

函数名描述
COUNT()计数
SUM()求和
AVG()平均值
MAX()最大值
MIN最小值
-- == 聚合函数 ==
-- Count(字段) 只包括列名的一列,统计结果时会忽略null值(不是空字符串)
SELECT COUNT(student_name) FROM student;	
-- Count(*) 包括了所有的列,相当于行数,在统计结果时,不会忽略null值
SELECT COUNT(*) FROM student;
-- Count(1) 包括了忽略的所有列,用1表示代码行,在统计结果时,不会忽略null值
SELECT COUNT(1) FROM student;

SELECT SUM(student_result) AS 'Total' FROM result;
SELECT AVG(student_result) AS 'Average' FROM result;
SELECT MAX(student_result) AS 'Max' FROM result;
SELECT MIN(student_result) AS 'Min' FROM result;

-- 查询不同课程的平均分,最高分,最低分
-- 核心 :根据不同的课程分组
SELECT subject_name,AVG(student_result) AS 平均分,MAX(student_result),MIN(student_result)
FROM result AS r
INNER JOIN `subject` AS sub
ON r.subject_no = sub.subject_no
GROUP BY r.subject_no

HAVING 平均分 > 80

5.3、数据库级别的 MD5加密

MD5主要增强算法复杂度和不可逆性

-- ==== 测试 MD5 ====
CREATE TABLE testmd5(
	id INT(4) NOT NULL auto_increment,
	`name` VARCHAR(20),
	`pwd` varchar(14),
	PRIMARY KEY(id)
	)ENGINE = INNODB DEFAULT CHARSET=utf8;

-- 插入数据
INSERT INTO `testmd5` VALUES (1, '王定伟', '123456'),(2, '李明亮', '123456'),(3, '马画藤', '123456');
-- 加密
UPDATE testmd5 SET pwd = MD5(pwd) WHERE id = 1

-- 插入时加密
INSERT INTO testmd5(`name`,pwd) VALUES ('马涛',MD5('111111'));

-- 如何校验:将用户传递进来的密码进行md5加密,比较加密后的值
SELECT * FROM testmd5 WHERE pwd = MD5('123456') AND `name` = '李明亮';

六、事务(Transition)

事务是数据库最小的工作单元,即将SQL放到一个批次中执行,要么都成功,要么都失败

6.1、事物原则:ACID原则

例:A有800,B有200,A给B转200

A:800-200 = 600

B:200+200=400

原子性(Atomicity)
  • 一起成功或者一起失败
  • 案例中体现为:A转出200和B收到200必须同时实现
一致性(Consistency)
  • 针对事务操作前后状态一致(最终一致性)

  • 案例中体现为:不管怎么转账,两人资金总数1000,不会凭空产生也不会凭空消失

隔离性(Isolation)
  • 针对多个用户同时操作,主要是排除其他事务影响

  • 案例中体现为:如果A给B转钱的同时,C也给B转钱,两个事物相互独立

持久性(Durability)
  • 事务的结束状态不会随着外界导致数据丢失,事务没有提交

  • 案例中体现为:

    • 操作前A:800,B:200

    • 操作后A:600,B:400

    • 如果操作前(事务没有提交),服务器宕机或者断电,重启后A应为800,B为200

    • 如果操作后(事务没有提交),服务器宕机或者断电,重启后A应为600,B为400

支持事务的数据库必须具有以上四个特性

6.2、隔离导致的问题

类似线程中的同步问题

脏读:
  • 指一个事务读取到另一个事务未提交的数据
  • 后继事务先执行了,导致前驱事务将后继事务的数据覆盖(脏写)
  • 案例中体现为:
    • A给B转200,C给B转100
    • A的事务还没操作完,C的事务读取到B只有200,于是B变成了300
    • A的事务操作完B只有400块钱,少了100
不可重复读
  • 在一个事务读取表中某一行数据,多次读取不同结果(不一定是错误的,只是场合不对)即同一事务前后两次读取数据不一样
  • 例:
    • A首先读取了一条数据,执行逻辑时,B将数据修改了,A再次读取发现数据不匹配
幻读
  • 指在事务内读到了别的事务插入的数据,导致前后读取不一致(一般是多一条数据)
  • 幻读和不可重复度类似,但是幻读强调集合的增减,而不是单条数据更新
  • 例:
    • A根据条件索引获得了M条数据,事务B改变了这M条数据或添加了N条数据,导致A再次搜索发现有N+M条数据,产生了幻觉
第一类更新丢失

事务A和事务B都对数据进行更新,但是由于某种原因事务A回滚,以至于已经提交的事务B更新后的数据被覆盖。

第一类更新丢失

第二类更新丢失

跟第一类更新丢失类似,两个事务同时对数据进行更新,但事务A的更新把已提交的事务B的更新数据给覆盖了。

第二类更新丢失

6.4、事务隔离级别

为了解决上述问题,主流关系型数据库都会提供四种事务隔离级别,由低到高分别是:读未提交 --> 读已提交 --> 可重复读 --> 串行化。隔离级别越高,性能损失越高,mysql默认可重复读

读未提交

最低隔离级别,所有事物都能看到其他事物未提交的事务执行结果,只能防止第一类更新丢失,不能解决脏读,可重复 读,幻读

读已提交

一个事务的更新操作只有在该事物提交后,另一事务才能读取到同一笔数据更新后的结果。可防止脏读和第一类更新丢失

可重复读

一个事务多次读取同一数据,在这个事务未结束前,其他事物不能访问该数据。防止脏读、不可重复读、第一/二类更新丢失,还是会出现幻读

串行化

要求事务序列化执行,每个事务挨个执行,不能并发。可解决所有问题,但是可能导致大量超时和锁竞争,一般不推荐

在这里插入图片描述

扩展:回滚机制

在mysql中,恢复机制是通过回滚日志(undo log)实现的,所有的事务进行的修改都会先记录到这个回滚日志中,然后在堆数据库中的对应进行写入。

mysql的事务是由redo和undo的

  1. redo操作的所有信息都是记录到重做日志(redo_log)中,即当一个事务做commit操作时,先把这个事务的操作写到redo_log中,然后将这些操作flush到磁盘上,出现故障时,只需要读取redo_log,然后重新flush到磁盘就行了。

  2. undo就比较麻烦,mysql在处理事务时,会在数据共享表空间里申请一个片段做segment段,用于保存undo信息,在处理rollback时,不是完全的物理undo,而是逻辑undo,即之前的操作进行反操作(对于每个insert,回滚时会执行delete;对于每个delete,回滚时会执行insert;对于每个update,回滚时会执行一个相反的update,把数据改回去。)但是这些共享表空间是不进行回收的。这些表空间的回收需要由mysql的master thread进程进行回收。

6.5、执行事务

mysql默认开启事务自动提交,如果想手动处理事务,要先关闭

-- 手动处理事务
SET autocommit = 0		-- 关闭事务自动提交

-- 事务开启
START TRANSACTION		-- 标记事务开启,从此之后sql都在同一个事务内

-- 提交:持久化(成功)
COMMIT

-- 回滚:回到原来的样子(失败)
ROLLBACK

-- 事务结束
SET autocommit = 1

SAVEPOINT	保存点名		-- 设置事务的保存点

ROLLBACK TO 保存点名		-- 回滚到保存点

RELEASE 保存点名		-- 删除保存点

测试手动处理事务

  • 例子中的两个update语句作为一组事务
  • Navicat需要在COMMIT提交事务之后才能看到数据变化
-- 案例
CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci
use shop

CREATE TABLE account(
	`id` int(3) NOT NULL auto_increment,
	`name` VARCHAR(20),
	`money` DECIMAL(9,2),
	PRIMARY KEY(`id`)
)ENGINE = INNODB CHARSET = utf8

INSERT INTO account(`name`,`money`) 
VALUES('A',2000),('B',10000)

-- 模拟事务:转账
SET autocommit = 0;		-- 关闭自动提交事务
START TRANSACTION			-- 开启事务
UPDATE account SET money = money - 500 WHERE `name` = 'A';			-- A减500
UPDATE account SET money = money + 500 WHERE `name` = 'B';			-- B加500

COMMIT;		-- 提交事务
ROLLBACK;		-- 回滚

SET autocommit = 1;		-- 恢复默认值

七、索引

定义:索引(index)是帮助MySQL高效获取数据的数据结构

索引是数据结构

7.1、索引分类

  • 主键索引(PRIMARY KEY)
    • 唯一表示,主键不可重复,只能由一个列作为主键
  • 唯一索引(UNIQUE KEY)
    • 避免重复列出现,唯一索引可以重复,多个列都可以表示为唯一索引
  • 常规索引(KEY / INDEX)
    • 默认的,可以用index关键字或者key关键字来设置
  • 全文索引(FullText)
    • 特定的数据库引擎才有(MYISAM)
    • 快速定位数据
  • 索引的使用
    1. 在创建表的时候增加索引
    2. 创建表之后增加索引

基础语法

-- 索引的使用

-- 显示所有索引信息
SHOW INDEX FROM student

-- 增加一个全文索引		索引名(列名)
ALTER TABLE school.student ADD FULLTEXT INDEX student_name(student_name);

-- 分析sql语句
EXPLAIN SELECT * FROM student;		-- 非全文索引

-- MATCH 匹配列名,against匹配字段名 
-- 全文索引在数据少时没有用
SELECT * FROM student WHERE MATCH(student_name) against ('马');
EXPLAIN SELECT * FROM student WHERE MATCH(student_name) against ('马');

7.2、测试索引

添加索引相当于给列中每条数据增加了唯一定位,就不需要遍历

CREATE TABLE `app_user` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) DEFAULT'' COMMENT'用户昵称',
`email` VARCHAR(50) NOT NULL COMMENT'用户邮箱',
`phone` VARCHAR(20) DEFAULT'' COMMENT'手机号',
`gender` TINYINT(4) UNSIGNED DEFAULT '0'COMMENT '性别(0:男;1:女)',
`password` VARCHAR(100) NOT NULL COMMENT '密码',
`age` TINYINT(4) DEFAULT'0'  COMMENT '年龄',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT = 'app用户表';

-- 自定义函数插入一百万条数据
DELIMITER $$	-- 写函数之前必写
/*
  第一个语句 delimiter 将 mysql 解释器命令行的结束符由”;” 改成了”$$”,
  让存储过程内的命令遇到”;” 不执行
*/
CREATE FUNCTION mock_data()
RETURNS INT
BEGIN
	DECLARE num INT DEFAULT 1000000;
	DECLARE i INT DEFAULT 0;
	
	WHILE i<num DO	
	INSERT INTO `app_user`(`name`,`email`,`phone`,`gender`,`password`,`age`)
		VALUES(CONCAT('用户',i),'19224305@qq.com',
		CONCAT('18',FLOOR(RAND()*(999999999-100000000)+100000000)),
		FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100));
		SET i=i+1;
	END WHILE;
	RETURN i;
END;$$

SELECT mock_data();

drop FUNCTION mock_data;

-- 创建索引 id_表名_字段名
SELECT * FROM app_user WHERE id = 99999;

-- 创建索引在内存中新建一棵树
-- CREATE INDEX 索引名 on 表(字段名)
CREATE INDEX id_app_user_name ON app_user(`name`);

SELECT * FROM app_user WHERE id = 99999;

添加索引前查询时间在1s左右,添加后0.01s左右

索引在小数据量用处不大,大数据效果明显

7.3、索引原则

  • 索引并非越多越好
  • 不适用于 经常变动的数据加索引
  • 小数据量不需要加索引
  • 一般用于经常查询的字段
索引的数据结构

Hash类型

Btree类型(InnoDB默认数据结构)

参考:http://blog.codinglabs.org/articles/theory-of-mysql-index.html

八、权限管理和备份

8.1、用户管理

SQL命令操作

  • mysql数据库下的user表用来储存用户信息
  • 操作用户的本质就是操作user表
-- 创建用户CREATE USER 用户名 IDENTIFIED BY '密码'
CREATE USER darwin IDENTIFIED BY '111111'

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

-- 修改指定用户密码
SET PASSWORD FOR darwin = PASSWORD('111111');

-- 重命名
RENAME USER darwin TO zhong;

-- 用户授权 *.*:所有权限
GRANT ALL PRIVILEGES ON *.* TO zhong

-- 查询权限(查询root需要在root后加@localhost)
SHOW GRANTS FOR zhong

-- 撤销权限 REVOKE 权限 ON 何种权限 FROM 用户
REVOKE ALL PRIVILEGES ON *.* FROM zhong

-- 删除用户
DROP USER zhong

8.2、MySQL备份

方式:

  1. 直接拷贝物理文件

  2. 使用可视化工具导出数据库

  3. 使用命令行

    # mysqldump -h 主机 -u用户名 -p密码 数据库 表名 > 物理磁盘位置:文件名
    C:\Users\Administrator>mysqldump -hlocalhost -uroot -p123456 school student >D:/a.sql
    
    # 如果导出多张表只需在加入别的表名,cmd命令行没有逗号,用空格隔开
    C:\Users\Administrator>mysqldump -hlocalhost -uroot -p123456 school student subject result >D:/a.sql
    
    # 导出数据库
    C:\Users\Administrator>mysqldump -hlocalhost -uroot -p123456 school >D:/a.sql
    
    # 导入
    # 登录情况下,切换数据库后使用source命令
    source D:/a.sql
    C:\Users\Administrator>mysqldump -hlocalhost -uroot -p123456 school source < D:/a.sql
    

九、规范数据库设计

良好数据库设计:

  • 节约内存空间
  • 保证数据库完整性
  • 方便开发

糟糕的数据库设计:

  • 数据冗余,浪费空间
  • 插入删除很麻烦
  • 程序性能差

9.1、设计步骤

以个人博客为例

分析需求:分析业务逻辑和处理数据库的需求

概要设计:E-R图

设计数据库:

收集信息分析需求

  • 用户表(用户登录注销,用户的个人信息,写博客,创建分类)

在这里插入图片描述

  • 分类表(文章分类,谁创建的)

在这里插入图片描述

  • 评论表

在这里插入图片描述

  • 友链表(友情链接信息)

在这里插入图片描述

  • 自定义表(系统信息,某个关键的字,或者一些主字段) key:value

  • 关注表(粉丝数)
    关注表

  • 说说表(发表心情, id…content…create_time)

  • 标记实体(把需求落实到每个字段)

  • 标记实体之间的关系

    • 写博客:user --> blog
    • 创建分类:user --> category
    • 关注:user --> user
    • 友链:links
    • 评论:user --> user --> blog

9.2、三大范式

为什么需要数据规范化?

  • 信息重复
  • 异常
  • 插入异常
    • 无法正常显示信息
  • 删除异常
    • 丢失有效信息
第一范式(1NF)

原子性:保证每一列不可再分

第一范式案例.jpg

上表中,家庭信息和学校信息不满足原子性,应分隔开

第二范式(2NF)

前提:满足第一范式

每张表只描述一件事情

在这里插入图片描述

上表中,产品和订单应该是两件区分开的事件,应该分位产品表和订单表

第三范式(3NF)

前提满足第一和第二范式

确保每张表中每一列数据和主键直接相关

第三范式案例.jpg

班主任和学号毫无瓜葛,应该分割为学生表和班主任表,班主任为主键

规范性和性能问题

关联查询的表不超过三张(阿里)

  • 考虑商业化需求和目标(成本,用户体验更重要),因此着重性能
  • 适当考虑规范性
  • 故意给表增加冗余字段(由多表查询变为单表查询)
  • 故意增加计算列

十、JDBC(重点)

10.1、驱动

因为应用程序不能直接操作数据库,所以需要驱动(驱动程序)让他们进行相应的通信。

驱动:声卡、显卡等

在这里插入图片描述

10.2、JDBC

SUN公司为了简化开发人员对数据库的统一操作,提供了一个Java操作数据库的规范,俗称JDBC。

没有什么是加一层解决不了的

因此这些规范就交给厂商做,开发人员只需要学会使用JDBC接口操作即可

JDBC图解.jpg

java.sql

javax.sql

还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar

10.3、第一个JDBC程序

  1. 创建测试数据库

    CREATE DATABASE jdbcstudy CHARACTER SET utf8 COLLATE utf8_general_ci;
    
    USE jdbcstudy;
    
    CREATE TABLE users(
      `id` INT PRIMARY KEY,
      `name` VARCHAR(40),
      `password` VARCHAR(40),
      `email` VARCHAR(60),
      `birthday` DATE
    );
    
    INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`)
    VALUES(1,'张三','123456','zs@sina.com','1980-12-04'),
    (2,'李四','123456','lisi@sina.com','1981-12-04'),
    (3,'王五','123456','wangwu@sina.com','1982-12-04');
    
  2. 导入数据库驱动

    新建lib目录,将jar包复制到lib目录下,邮件目录选择 add as library 添加为库

    在这里插入图片描述

  3. 编写测试代码

package com.darwin.lesson01;


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

// 我的第一个jdbc程序
public class Jdbc01 {
    public static void main(String[] args) throws Exception {
        // 1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");     //固定写法:加载驱动

        // 2.用户数据和url
        // 用问号连接参数
        // useUnicode:支持中文编码;characterEncoding:设定中文字符集为utf8;useSSL:使用安全连接
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String username = "root";
        String password = "123456";
        // 3.连接成功,数据库对象 connection
        Connection connection = DriverManager.getConnection(url, username, password);
        // 4.执行SQL的对象 statement
        Statement statement = connection.createStatement();


        // 5.执行SQL的对象去执行SQL,可能存在的结果,查看返回结果
        String sql = "SELECT * FROM users";
        ResultSet resultSet = statement.executeQuery(sql);      // 返回的结果集,封装了全部查询结果

        while(resultSet.next()){
            System.out.println("id=" + resultSet.getObject("id"));
            System.out.println("name=" + resultSet.getObject("name"));
            System.out.println("password=" + resultSet.getObject("password"));
            System.out.println("email=" + resultSet.getObject("email"));
            System.out.println("birthday=" + resultSet.getObject("birthday"));
            System.out.println();
        }

        // 6.释放连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

步骤总结:

  1. 加载驱动
  2. DriverManager获取连接数据库的对象connection
  3. connection获取执行sql的对象 statement
  4. statement活的返回的结果集resultSet
  5. 释放连接

注意点:

  • 使用安全连接时,如果mysql版本高于驱动就设置为false
代码解释
A:DriverManager

为什么获取到Driver类的对象不使用?

因为注册驱动的本质是去加载driver类的静态代码块,只要声明类了,初始化driver类就将代码块执行了

        // 1.加载驱动
        //DriverManager.registerDriver(new Driver());   本来应这样写
        Class.forName("com.mysql.jdbc.Driver");     //固定写法:加载驱动
B:URL
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";

jdbc:mysql:类似链接中的https

localhost:3306:地址和端口号

jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true":连接的数据库名?参数1&参数2&参数3

C:Connection对象

connection代表数据库

//数据库自动提交,事务回滚,事务提交
connection.setAutoCommit(true);
connection.rollback();
connection.commit();
D:Statement对象

执行sql的对象

String sql = "";	//编写sql
statement.executeQuery();    //查询操作,返回字符集resultset
statement.execute();
statement.executeUpdate();      //更新、插入、删除,返回一个受影响的函数
E:ResultSet

查询的结果集,封装了所有查询结果

resultSet.getObject();      //在不知道列具体类型时使用
// 知道数据类型就是用固定类型
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();

resultset是一个链表,可以通过以下代码移动指针位置

resultSet.beforeFirst();// 移动到最前面
resultSet.afterLast();//移动到最后面
resultSet.next();//移动到下一个数据
resultSet.previous();//移动到前一行
resultSet.absolute(row);//移动到指定行

10.4、statement对象

JDBC中statement对象用于向数据库发送SQL语句,完成对应的增删改查

使用statement.executeUpdate完成增删改查之后会返回一个记录(几行数据发生改变的)整数

CRUD

为了节约资源,创建数据库配置文件db.properties通过工具类JdbcUtil.java读取配置文件,避免每次都需要配置数据库

db.propertise:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
工具类:
package com.darwin.lesson02.utils;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;

    static{
        try{
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);

            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            // 驱动只用加载一次
            Class.forName(driver);
        } catch (IOException | ClassNotFoundException e){
            e.printStackTrace();
        }
    }

    // 获取连接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,username,password);
    }
    //释放资源
    public static void release(Connection con, Statement st, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(st != null){
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(con != null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }
}
增加操作:
package com.darwin.lesson02.utils;

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

public class TInsert {
    public static void main(String[] args) {
        Connection con = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            con = JdbcUtils.getConnection();
            st = con.createStatement();
            String insert = "INSERT INTO users(id,`name`,`password`,email,birthday)" +
                    "VALUES(4,'Darwin','111111','ex@qq.com','1999-05-28')";
            int i = st.executeUpdate(insert);
            if (i>0){
                System.out.println("执行成功"+i);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            JdbcUtils.release(con,st,rs);
        }
    }
}
删除操作:
public class TDelete {
    public static void main(String[] args) {
        Connection con = null;
        Statement st = null;
        ResultSet rs = null;

        try{
            con = JdbcUtils.getConnection();
            st = con.createStatement();
            String delete = "DELETE FROM users WHERE `name` = 'Darwin';";
            int i = st.executeUpdate(delete);
            if (i>0){
                System.out.println("删除执行成功");
            }
        }catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally{
            JdbcUtils.release(con,st,rs);
        }
    }

修改操作:
public class TDelete {
    public static void main(String[] args) {
        Connection con = null;
        Statement st = null;
        ResultSet rs = null;

        try{
            con = JdbcUtils.getConnection();
            st = con.createStatement();
            String delete = "DELETE FROM users WHERE `name` = 'Darwin';";
            int i = st.executeUpdate(delete);
            if (i>0){
                System.out.println("删除执行成功");
            }
        }catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally{
            JdbcUtils.release(con,st,rs);
        }
    }
查询操作:
    public static void main(String[] args) {
        Connection con = null;
        Statement st = null;
        ResultSet rs = null;

        try{
            con = JdbcUtils.getConnection();
            st = con.createStatement();
            String select = "SELECT `name`,`birthday` FROM users WHERE `name` = 'Darwin';";
            rs = st.executeQuery(select);

            while(rs.next()){
                System.out.println("姓名:"+rs.getString("name"));
                System.out.println("生日:"+rs.getString("birthday"));
            }

        }catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally{
            JdbcUtils.release(con,st,rs);
        }
    }
SQL注入问题

web应用程序对用户输入数据的和反省没有判断或过滤不严

SQL会被拼接

package com.darwin.lesson02.utils;

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

public class SQL注入 {
    public static void main(String[] args) {
        login("'or '1=1","'or '1=1");
    }

    public static void login(String username,String password) {
        Connection con = null;
        Statement st = null;
        ResultSet rs = null;

        try{
            con = JdbcUtils.getConnection();
            st = con.createStatement();
            String select = "SELECT * FROM `users` WHERE `name` = '" + username + "'AND `password` = '"+password+"';";
            rs = st.executeQuery(select);

            while(rs.next()){
                System.out.println("姓名:"+rs.getString("name"));
                System.out.println("密码:"+rs.getString("password"));
            }

        }catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally{
            JdbcUtils.release(con,st,rs);
        }
    }
}

当查询条件为' 'or '1=1'时,所有用户数据都被查出来了

10.5、PreparedStatement对象

PreparedmentStatement对象防止SQL注入

本质:把传递进来的参数当做字符,假设其中有 ’ 号会被直接转义,不存在拼接

区别:

  1. PrepareStatement需要预编译sql语句,然后在获取对象时传入

  2. PrepareStatement在获取对象时使用connection.preparestatement而Statement使用connection.createStatement

  3. String insert = "INSERT INTO users(id,`name`,`password`,email,birthday)" +
            "VALUES(?,?,?,?,?)";
    

    传值时,PrepareStatement可以用占位符,然后用preparestatement.set方法赋值,更为便捷高效

查询:

public static void main(String[] args) {
    Connection con = null;
    PreparedStatement st = null;
    ResultSet rs = null;

    try{
        con = JdbcUtils.getConnection();
        String select = "SELECT `name`,`birthday` FROM users WHERE `id` = ?;";

        st = con.prepareStatement(select);
        st.setInt(1,1);
        rs = st.executeQuery();

        while(rs.next()){
            System.out.println("姓名:"+rs.getString("name"));
            System.out.println("生日:"+rs.getString("birthday"));
        }

    }catch (SQLException throwables) {
        throwables.printStackTrace();
    }finally{
        JdbcUtils.release(con,st,rs);
    }
}

新增:

package com.darwin.lesson03;

import com.darwin.lesson02.utils.JdbcUtils;

import java.sql.*;

public class TInsert {

    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            con = JdbcUtils.getConnection();

            // 使用?占位符代替参数
            //预编译SQL后执行
            String insert = "INSERT INTO users(id,`name`,`password`,email,birthday)" +
                    "VALUES(?,?,?,?,?)";
            st = con.prepareStatement(insert);

            //preparedstatement里的set方法,给预编译sql中的占位符赋值
            //setXXX(下标,所赋的值)
            st.setInt(1,5);
            st.setString(2,"Alex");
            st.setString(3,"222222");
            st.setString(4,"1024@qq.com");
            st.setDate(5,new Date(new java.util.Date().getTime()));
            int i = st.executeUpdate();
            if (i>0){
                System.out.println("执行成功");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            JdbcUtils.release(con,st,rs);
        }
    }

}

删除:

package com.darwin.lesson03;

import com.darwin.lesson02.utils.JdbcUtils;

import java.sql.*;

public class TDelete {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try{
            con = JdbcUtils.getConnection();
            String delete = "DELETE FROM users WHERE `name` = ?;";
            st = con.prepareStatement(delete);
            st.setString(1,"Alex");
            int i = st.executeUpdate();
            if (i>0){
                System.out.println("删除执行成功");
            }
        }catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally{
            JdbcUtils.release(con,st,rs);
        }
    }
}

防注入:

package com.darwin.lesson03;

import com.darwin.lesson02.utils.JdbcUtils;

import java.sql.*;

public class Wrong {
    public static void main(String[] args) {
        login("'or '1=1","'or 123456");
    }

    public static void login(String username,String password) {
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try{
            con = JdbcUtils.getConnection();
            String select = "SELECT * FROM `users` WHERE `name` = ? AND `password` = ?;";

            st = con.prepareStatement(select);
            st.setString(1,username);
            st.setString(1,password);
            rs = st.executeQuery();

            while(rs.next()){
                System.out.println("姓名:"+rs.getString("name"));
                System.out.println("密码:"+rs.getString("password"));
            }

        }catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally{
            JdbcUtils.release(con,st,rs);
        }
    }
}

10.6、IDEA连接数据库

连接后在架构(schemas)选择数据库

idea连接数据库.jpg

10.7、JDBC处理事务

使用connection.setAutoCommit(false);会自动关闭自动提交,并开启事务

package com.darwin.lesson04;

import com.darwin.lesson02.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class TTransition {
    public static void main(String[] args){
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            con = JdbcUtils.getConnection();
            // 关闭数据库自动提交后会自动开启事务
            con.setAutoCommit(false);

            String sql1 = "update account set money = money - 100 where name = 'A'";
            st = con.prepareStatement(sql1);
            st.executeUpdate();
            // 让事务失败
            // int x = 1/0;
            String sql2 = "update account set money = money + 100 where name = 'B'";
            st = con.prepareStatement(sql2);
            st.executeUpdate();

            // 业务完成提交事务
            con.commit();
            con.setAutoCommit(true);
            boolean autoCommit = con.getAutoCommit();
            System.out.println(autoCommit);
            System.out.println("Over");


        } catch (SQLException e) {
            e.printStackTrace();
            // 失败则回滚
            try {
                con.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }finally {
            JdbcUtils.release(con,st,rs);
        }


    }
}

10.9、数据库连接池

数据库连接 --> 执行完毕 --> 释放

整个过程是十分浪费资源的

池化技术:预先准备资源,过来就连接,链接完再放回连接池

设置最小连接数:10、最大连接数:15、等待超时:100ms,当达到最大连接数后续就需要等待,超时则取消

编写连接池,只用实现接口DataSource

开源的数据源连接池实现类:

  1. DBCP
  2. C3P0
  3. Druid
DBCP

DBCP和JDBC的本质区别在于:

  1. 两者工作模式不同
    1. JDBC需要频繁的连接以及释放
    2. DBCP准备了池
  2. 连接方式不同
    1. JDBC需要读取出配置信息(driver、url、username、password)后手动加载驱动并配置连接
    2. DBCP只需要读取配置文件后,创建工厂对象,用工厂去创建连接

需要用到的jar包:

commons-dbcp-1.4、commons-pool-1.6

2.0以上的版本需要commons-logging

dbcpconfig.properties:配置文件

#所有连接名都是dbcp定义的,不能修改
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false
username=root
password=123456

#初始化连接
initialSize=10

#最大连接数量
maxActive=50

#最大空闲连接
maxIdle=20

#最小空闲连接
minIdle=5

#超时等待时间以毫秒为单位 6000毫秒/1000等于60秒
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:【属性名=property;】
#注意:user 与 password 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF8

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_COMMITTED

JdbcUtils_DBCP:工具类

package com.darwin.lesson05.utils;

import com.darwin.lesson02.utils.JdbcUtils;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils_DBCP {

    private static DataSource dataSource = null;

    static{
        try{
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbctconfig.properties");
            Properties properties = new Properties();
            properties.load(in);

            //工厂模式
            dataSource = BasicDataSourceFactory.createDataSource(properties);


        } catch (Exception e){
            e.printStackTrace();
        }
    }

    // 从数据源中获取连接
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    //释放资源
    public static void release(Connection con, Statement st, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(st != null){
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(con != null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }
}
C3P0

可以定义多套配置文件,XML文件不用读取

需要用到的jar包

c3p0-0.9.5.5.jar,mchange-commons-java-0.2.19.jar

配置文件c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!--
    c3p0的缺省(默认)配置
    如果在代码中ComboPooledDataSource ds=new ComboPooledDataSource();这样写就表示使用的是c3p0的缺省(默认)
    -->
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=UTC</property>
        <property name="user">root</property>
        <property name="password">123456</property>
        <property name="acquiredIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>


    <!--
    c3p0的命名配置
    如果在代码中ComboPooledDataSource ds=new ComboPooledDataSource("MySQL");这样写就表示使用的是name是MySQL
    -->
    <name-config name="MySQL">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=UTC</property>
        <property name="user">root</property>
        <property name="password">123456</property>
        <property name="acquiredIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </name-config>
</c3p0-config>

工具类

import com.mchange.v2.c3p0.ComboPooledDataSource;

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

public class JDBCC3P0Utils {
    private static DataSource dataSource = null;
    //private static ComboPooledDataSource dataSource = null;


    static {
        try {
            //代码的方式配置
//            dataSource = new ComboPooledDataSource();
//            dataSource.setDriverClass();
//            dataSource.setJdbcUrl();
//            dataSource.setUser();
//            dataSource.setPassword();
//            dataSource.setMaxPoolSize();
//            dataSource.setMinPoolSize();
            //配置文件写法,不写名字使用默认配置文件
            dataSource = new ComboPooledDataSource("MySQL");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取连接
     */
    public static Connection getConnection() throws SQLException {
        //从数据源中获取连接
        return dataSource.getConnection();
    }

    /**
     * 释放资源
     */
    public static void release(Connection con, Statement st, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
结论:

无论是用什么数据源,本质始终实现DataSource接口

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值