zoukankan      html  css  js  c++  java
  • MySQL初识

    目录

    MySQL

    1、简介

    MySQL是一个关系型数据库管理系统

    前世:瑞典MySQL AB公司

    今生:属于Oracle旗下产品

    MySQL是最好的RDBMS(关系型数据库管理系统)应用软件之一

    开源的数据库软件~

    体积小,速度快,总体拥有成本低,所有人必须会~

    中小型网站、或者大型网站,集群!

    官网:https://www.mysql.com

    官网下载地址:https://dev.mysql.com/downloads/mysql

    1.1安装建议

    1、尽量不要使用exe,注册表

    2、尽可能使用压缩包安装~

    1.2安装步骤

    1、解压

    2、添加环境变量 属性--高级系统设置--环境变量--下面有个path--把bin目录粘进去

    3、新建mysql配置文件 my.ini

    [mysqld]
    #目录换成自己的
    basedir=D:mysqlmysql-5.7.32-winx64
    datadir=D:mysqlmysql-5.7.32-winx64data
    port=3306
    skip-grant-tables
    

    4、启动管理员模式下的cmd,运行所有的命令

    5、cd /d D:mysqlmysql-5.7.32-winx64in 进入bin目录 执行mysqld -install ===》安装mysql服务

    注意:【输入安装命令mysqld install
    出现问题The service already exists
    这是由于之前已经安装过mysql并且没有删除干净

    重新以管理员身份运行,输入sc query mysql,查看一下名为mysql的服务

    命令sc delete mysql,删除该mysql

    再重新安装即可】

    6、再输入mysqld --initialize-insecure --use=mysql 会自动创建data目录 ===》初始化数据库文件

    7、net start mysql ===》启动mysql,修改密码~

    8、进入mysql通过命令行 mysql -u root -p 进入mysql管理界面(密码可为空)

    9、进入mysql> 要修改密码

    update mysql.user set authentication_string=password('123456') where user='root' and Host='localhost';
    

    (最后输入 flush privileges 刷新权限 )

    1.3 安装SQLyog

    官网:https://sqlyog.en.softonic.com/

    新建一个数据库school

    新建一张表student

    1.4 连接数据库

    命令行链接

    mysql -uroot -p123456  --连接数据库
    

    -- 所有的语句都是用分号结尾;
    -- 查看所有的数据库
    show databases;
    show tables;
    use shcool;
    describle shcool;

    create database test;

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

    
    数据库xxx语言 =====》CRUD增删改查!
    
    - DDL 数据库定义语言
    - DML 数据库操作语言
    - DQL 数据库查询语言
    - DCL 数据库控制语言
    
    # 2、操作数据库
    
    操作数据库 ===》操作数据库中的表 ===》操作数据库中表的数据
    
    ## 2.1数据库
    
    ```mysql
    CREATE DATABASE IF NOT EXISTS test;
    
    DROP DATABASE IF EXISTS test;
    
    SHOW DATABASES;
    
    -- 特殊字符用` `括上
    

    2.2数据库的数据类型

    数值

    • tinyint 十分小的数据 1个字节
    • smallint 较小的数据 2个字节
    • mediumint 中等大小的数据 3个字节
    • int 标准的数据 4个字节
    • bigint 较大的数据 8个字节
    • float 浮点数 4个字节
    • double 浮点数 8个字节(精度问题!)
    • decimal 字符串形式的浮点数 金融计算的时候,一般是使用decimal

    字符串

    • char 字符串固定大小的 0-255
    • varchar 可变字符串 0-65535 常用的变量 String
    • datetime YYYY-MM-DD HH:mm:ss 最常用的时间格式
    • timestamp 时间戳 1970.1.1到现在的毫秒数!也较为常用!全球唯一
    • year 年份表示

    null

    • 没有值,未知
    • 【注意】:不要使用null进行运算,结果为null

    2.3 数据库的字段属性(重点)

    unsigned

    • 无符号的整数
    • 声明了该列不能声明为负数

    zerofill

    • 0填充
    • 不足的位数,使用0填充 int(3) 5 ====> 005

    自增

    • 通常理解为自增,自动在上一条记录的基础上+1(默认+1)
    • 通常用来设计唯一的主键~ index,必须是整数类型
    • 可以自定义设计主键自增的起始值和步长

    非空

    null or not null

    • 假设设置为not null,如果不给它赋值,就会报错!
    • null 如果不填写值,默认就是null!

    默认

    • 设置默认的值
    • sex 默认值为男,如果不指定该列的值,则会有默认的值!
    阿里巴巴规范手册  每一个表必须存在以下五个字段  项目用的,表示一个记录存在的意义~~
    id  主键
    `version`  乐观锁
    is_delete  伪删除
    gmt_create 创建时间
    gmt_update 修改时间
    

    2.4创建数据库表

    -- 注意点,使用英文() 表的名称 和 字段 尽量使用`` 括起来
    -- AUTO_INCREMENT自增
    -- 字符串使用 单引号括起来
    -- 所有的语句后面加,  最后一个不用加逗号
    -- PRIMARY KEY主键,一般一个表只有一个
    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;
    

    格式

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

    常用命令

    SHOW CREATE DATABASE school;-- 查看创建数据库的语句
    SHOW CREATE TABLE student;-- 查看创建表语句
    DESC student;-- 显示表的结构
    

    2.5数据表的类型

    -- 关于数据库的引擎
    /*
    INNODB 默认使用
    MYISAM 早些年使用
    */
    
    MYISAM INNODB
    事物支持 不支持 支持
    数据行锁定 不支持[表锁] 支持[行锁]
    外键的表 不支持 支持
    全文索引 支持 不支持
    表空间的大小 较小 较大(约为myisam两倍)

    常规使用操作

    • MYISAM 节约空间,速度较快
    • INNODB 安全性高,事物的处理,多表多用户操作

    在物理空间存在的位置

    所有的数据库文件都存在data目录下----》本质还是文件的存储!

    MySQL引擎在物理文件上的区别:

    • INNODB 在数据库表中只有一个*.frm文件【.ibd文件是备份相关】,以及上级目录下的ibdata1文件
    • MYISAM对应的文件
      • *.frm - 表结构的定义文件
      • *.MYD - 数据文件(data)
      • *.MYI - 索引文件(index)

    设置数据库表的字符集编码

    CHARSET=utf8
    
    • 不设置的话,回是MySQL默认的字符集编码~(不支持中文)

    在my.ini中配置默认的编码

    character-set-server=utf8
    

    2.6修改删除表

    修改

    -- 修改表名
    ALTER TABLE teacher RENAME AS teacher1;
    -- 增加表的字段
    ALTER TABLE teacher1 ADD age INT(4);
    -- 修改表的字段(两种情况:1、重命名 2、修改约束)
    ALTER TABLE teacher1 MODIFY age VARCHAR(11); -- 修改约束
    ALTER TABLE teacher1 CHANGE age age1 INT(1); -- 重命名
    
    

    删除

    -- 删除表的字段
    ALTER TABLE teacher1 DROP age1;
    
    -- 删除表
    DROP TABLE IF EXISTS teacher1; 
    

    注意点

    • `` 所有字段名,使用这个包裹
    • 注释 -- 或者 /**/
    • sql 关键字大小写不敏感,建议大家写大写~
    • 所有的符号必须用英文

    3、MySQL数据管理

    3.1 外键(了解)

    -- 学生表的grade_id字段 要去引用grade表的grade_id
    -- 定义外键key
    -- 给这个外键添加约束(执行引用)
    CREATE TABLE `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 '出生日期',
      `grade_id` INT(10) NOT NULL COMMENT '年级id',
      `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
      `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
      PRIMARY KEY (`id`),
      KEY `FK_grade_id` (`grade_id`),
      CONSTRAINT `FK_grade_id` FOREIGN KEY (`grade_id`) REFERENCES `grade`(`grade_id`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8
    
    CREATE TABLE `grade` (
      `grade_id` int(10) NOT NULL AUTO_INCREMENT COMMENT '年级id',
      `grade_name` varchar(50) NOT NULL COMMENT '年级名称',
      PRIMARY KEY (`grade_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8
    

    删除有外键的表,必须先删除引用的表(从表),比如删除grade表之前 必须先删除student表 直接删除grade表会报错

    • 创建表成功后添加外键约束
    -- 创建表的时候没有外键关系
    ALTER TABLE `student` ADD CONSTRAINT `FK_grade_id` FOREIGN KEY (`grade_id`) REFERENCES `grade` (`grade_id`);
    -- alter table 表 add constraint 约束名 foreign key (作为外键的列) references 那个表 (哪个字段)
    

    以上的操作都是物理外键,数据库级别的外键,我们不建议使用(避免数据库过多造成困扰,这里了解即可)

    最佳实践

    • 数据库就是单纯的表,只用来存数据,只有行(数据)和列(字段)
    • 我们想使用多张表的数据,想使用外键(程序去实现)

    3.2 DML语言

    数据库的意义:数据存储和数据管理

    DML语言:数据管理语言

    • inset
    • update
    • delete
    • select

    3.3 添加insert

    insert INTO 表名 (字段1,字段2,字段3) VALUES (值1,值2,值3);

    -- 插入语句(添加)
    --insert INTO 表名 (字段1,字段2,字段3) VALUES (值1,值2,值3);
    INSERT INTO `grade` (`grade_name`) VALUES ('大四');
    
    -- 由于主键自增我们可以省咯,如果不写表的字段,他就会一一匹配
    -- 一般写插入语句,我们一定要数据和字段一一对应
    
    -- 插入多个字段
    INSERT INTO `grade` (`grade_name`) 
    VALUES ('大一'),('大二');
    
    INSERT INTO `student` (`name`,`pwd`,`sex`) VALUES ('小红','aaaa','男');
    
    INSERT INTO `student` (`name`,`pwd`,`sex`) VALUES ('小小','bbbb','女'),('大大','cccc','男');
    

    注意点:

    • 字段和字段之间使用 英文逗号 隔开
    • 字段是可以省略的,但是后面的值必须要一一对应,不能少
    • 可以同时插入多条数据 values 后面的值需要使用(),()....

    3.4 修改update

    update 表 set 字段=新值 where条件

    -- 修改学院名字,带了简介
    UPDATE `student` SET `name`='长江7号' WHERE id = 1;
    
    -- 不指定条件 就都改了!!!
    UPDATE `student` SET `name`='长江7号';
    
    -- 修改多个数据
    UPDATE `student` SET `name` = 'summer',`email`='123@qq.com' WHERE id = 1;
    

    条件:where

    操作符会返回布尔值

    操作符 含义 示例 结果
    = 等于 5=6 false
    <> 或 != 不等于 5<>6 true
    >
    <
    <=
    >=
    BETWEEN ... AND ... [闭合区间]在某个范围内 [2,5]
    AND && 5>1 && 2>1 true
    OR || 5<1 || 2>1 true

    3.5 删除delete

    delete from 表名 [where 条件]

    -- 删除数据
    DELETE FROM `student` WHERE id =1;
    
    -- 删表跑路
    DELETE FROM `student`;
    

    TRUNCATE命令

    • 作用:完全清空一个数据库表,表的结构和索引约束都不会变;

    • TRUNCATE `student`;比较TRUNCATE和DELETE
      

    比较TRUNCATE和DELETE

    相同点:

    • 都能删除数据,都不会删除表结构

    不同:

    • TRUNCATE 重新设置 自增列 计数器会归零
    • TRUNCATE 不会影响事物

    了解即可

    DELETE删除数据的问题 重启数据库,现象

    • INNODB 自增列会从1开始 (存在内存当中,断电即失)
    • MyISAM 继续从上一个自增从新开始(存在文件中的,不会丢失)

    4、DQL查询数据(最重点)

    4.1 DQL

    Data Query Language 数据查询语言

    • 所有的查询操作都用它,select
    • 简单的查询,复杂的查询他都能做
    • 数据库中最核心的语言,最重要的语句
    • 使用频率最高语句

    4.2 DISTINCT & CONCAT

    CONCAT(a,b) -- 连接字符串
    
    DISTINCT -- 去重
    
    SELECT VERSION(); -- 查询系统的版本(函数)
    SELECT 100*2-1 AS 计算结果; -- 用来计算(表达式)
    SELECT @@auto_increment_increment; -- 查询自增的步长(变量)
    
    

    4.3 where条件子句

    运算符 语法 描述
    and && a and b a&&b
    or || a or b a||b
    not ! not a !a

    4.4 联表查询

    join 表 on 条件 ----》连接查询

    where ----》等值查询

    内联左联右联

    操作 描述
    inner join 如果表中至少有一个匹配,就返回
    left join 会从左表中返回所有的值,即使右表中没有匹配
    right join 会从右表中返回所有的值,即使左表中没有匹配

    自连接

    • 自己的表和自己的表连接,核心:一张表拆为两张一样表即可

    父类

    categoryid categoryName
    2 信息技术
    3 软件开发
    5 美术技术

    子类

    pid categoryid categoryName
    3 4 数据库
    2 8 办公信息
    3 6 web开发
    5 7 ps技术

    操作:查询父类对应的子类关系

    父类 子类
    信息技术 办公信息
    软件开发 数据库
    软件开发 web开发
    美术设计 ps技术

    CREATE TABLE IF NOT EXISTS `category` (
    	`categoryid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主题id',
    	`pid` INT(10) NOT NULL COMMENT '父id',
    	`categoryName` VARCHAR(50) NOT NULL COMMENT '主题名字',
    	PRIMARY KEY (`categoryid`)
    )ENGINE= INNODB AUTO_INCREMENT = 9 DEFAULT CHARSET=utf8;
    
    INSERT INTO `category` (`categoryid`,`pid`,`categoryName`)VALUES
    ('2','1','信息技术'),
    ('3','1','软件开发'),
    ('4','3','数据库'),
    ('5','1','美术设计'),
    ('6','3','web开发'),
    ('7','5','ps技术'),
    ('8','2','办公信息');
    
    -- 查询父子信息 把一张表看为两张表
    SELECT a.`categoryName` '父栏目',b.`categoryName` '子栏目'
    FROM
    `category` a ,`category` b
    WHERE
    a.`categoryid` = b.`pid`;
    

    4.5 分页和排序

    order by

    WHERE
    a.`categoryid` = b.`pid`;
    
    -- 排序:升序 asc  降序desc
    

    limit

    为什么要分页?

    • 缓解数据库压力 ,给人的体验更好,瀑布流(图片)
    limit 起始值,页面大小
    -- page size
    limit (page-1)*size , size;
    

    4.6 子查询

    where中加select

    4.7 分组

    group by

    having

    5、MySQL函数

    看官网~~

    5.1 常用函数

    
    SELECT ABS(-8);
    SELECT CEILING(9.4) -- 向上取整 10
    SELECT FLOOR(9.4) -- 向下取整 9
    SELECT RAND() -- 返回一个0-1之间的随机数
    SELECT SIGN(-10) -- 判断一个数的符号 正数 1 负数-1 0 0 
    SELECT CHAR_LENGTH('即使再小的帆也能远航') -- 字符串长度
    SELECT CONCAT('1','hhahah','0090') -- 拼接字符串
    SELECT INSERT('我爱编程',1,2,'超级喜欢') -- 
    SELECT LOWER('AAAA') -- aaaa
    SELECT UPPER('aaaQ') -- AAAQ
    SELECT INSTR('shuang','h') -- 2
    SELECT REPLACE('大大的世界','的','哈') -- 大大哈世界
    SELECT SUBSTR('shuang',4,2) -- an
    SELECT REVERSE('shuang') -- 反转gnauhs
    
    
    SELECT CURRENT_DATE() -- 2021-01-17
    SELECT NOW() -- 2021-01-17 17:06:34
    SELECT SYSDATE()
    SELECT LOCALTIME()
    SELECT YEAR(NOW())
    SELECT USER()
    

    5.2 聚合函数(常用)

    函数名称 描述
    COUNT() 计数
    SUM() 求和
    AVG() 平均值
    MAX() 最大值
    MIN() 最小值
    ...
    SELECT COUNT(*)  FROM `category`;
    SELECT COUNT(`categoryid`)  FROM `category`; -- 指定列
    SELECT COUNT(1)  FROM `category`;
    -- 这三个都能查 个数 区别是什么?
    /*
    COUNT(`字段`): 会忽略所有的null值
    
    COUNT(*):不会忽略null值; 本质计算行数
    
    COUNT(1):不会忽略null值; 本质计算行数
    */
    

    5.3 数据库级别的MD5加密(扩展)

    什么是MD5?

    MD5不可逆,具体的值 MD5是一样的

    MD5破解网站的原理,背后有一个字典MD5加密后的值

    -- 测试MD5 加密
    CREATE TABLE `testmd5`(
    	`id` INT(4) NOT NULL ,
    	`name` VARCHAR(20) NOT NULL,
    	`pwd` VARCHAR(50) NOT NULL,
    	PRIMARY KEY (`id`)
    )ENGINE = INNODB DEFAULT CHARSET = utf8;
    
    INSERT INTO `testmd5` VALUES
    (1,'shuang' ,'123456'),
    (2,'lisa' ,'123456'),
    (3,'rose' ,'123456');
    
    -- 加密
    UPDATE testmd5 SET pwd=MD5(pwd);
    
    INSERT INTO `testmd5` VALUES
    (4,'rabbit' ,MD5('123456'));
    
    -- 如何检验?将用户传递进来的密码,进行MD值加密,然后比对加密后的值
    

    select的通用语句

    select [all distinct] 字段 as 重新起字段名
    from 表 as 重新起表名
    [inner join on
     left join on
     right join on
    ]
    where(具体字段 或者 子查询)
    group by 
    having
    order by (通过那个字段排序)
    limit 
    

    6、事务

    6.1 什么是事务

    要么都成功,要么都失败

    1、SQL执行 A给B转账 A 1000 ----》200 B 200

    2、SQL执行 B收到A的钱 A 800 ---》 B 400

    将一组SQL放在一个批次中去执行

    事务原则

    ACID原则:原子性 一致性 隔离性 持久性 (脏读,幻读)

    原子性:

    比如说转钱,原子性表示,这两个步骤一起成功或者一起失败,不能只发生其中一个动作

    一致性:

    针对一个事务操作前与操作后的状态一致

    转钱前 一共1200,转钱后 还是一共1200

    持久性:

    转钱过程中 服务器宕机或者断电~~

    事务没有提交--》恢复到原状

    事务已经提交--》持久化到数据库

    事务一旦提交就不可逆了

    隔离性:

    针对多个用户同时操作,主要是排除其他事务对本次事务的影响

    事务的隔离级别

    参考博客链接:

    https://blog.csdn.net/dengjili/article/details/82468576

    脏读:

    指一个事务读取了另外一个事务未提交的数据;

    不可重复读:

    在一个事务内读取表中的某一行数据,多次读取结果不同(这个不一定是错误,只是某些场合不对)

    (虚读)幻读:

    是指在一个事务内读取到了别的事务插入的数据,导致前后数据不一致(一般是行影响,多了一行)

    6.2 执行事务

    事务执行步骤

    -- 事务
    -- mysql 是默认开启事务自动提交的
    SET autocoment = 0; -- 关闭
    SET autocoment = 1; -- 开启(默认)
    
    -- 手动处理事务
    SET autocoment = 0; -- 关闭自动提交
    
    -- 事务开启
    START TRANSACTION -- 标记一个事务的开始,从这个之后的sql,都在同一个sql中
    
    INSERT
    INSERT
    
    -- 提交:持久化
    COMMIT
    
    -- 回滚:回到原来的样子
    ROLLBACK
    
    -- 事务结束
    SET autocoment = 1; -- 开启自动提交
    
    -- 了解
    SAVEPOINT  保存点 -- 设置一个事务的保存点
    ROLLBACK TO SAVEPOINT 保存点 -- 回滚到保存点
    RELEASE SAVEPOINT 保存点 -- 撤销保存点
    

    模拟

    -- 转账
    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) NOT NULL,
       `money` DECIMAL(9,2) NOT NULL,
       PRIMARY KEY (`id`)
    )ENGINE = INNODB DEFAULT CHARSET = utf8;
    
    INSERT INTO `account` (`name`,`money`) VALUES ('A',2000.00),
    ('B',10000.00);
    
    -- 模拟转账
    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; -- 回复默认值s
    

    7、索引

    参考博客:

    https://blog.codinglabs.org/articles/theory-of-mysql-index.html

    MySQL官方对索引的定义为:索引(index)是帮助MySQL高效获取数据的数据结构。0.5s --》0.0000001s

    提取句子主干:就可以得到索引的本质:索引就是数据结构

    7.1 索引的分类

    • 主键索引【PRIMATY KEY】
      • 唯一的标识,主键不可重复,只能有一个列作为主键
    • 唯一索引【UNIQUE KEY】
    • 常规索引【KEY or INDEX】
    • 全文索引【FullText】
      • 在特定的数据库引擎下才有,MyISAM
      • 快速定位数据
    -- 索引的使用
    -- 1、在创建表的时候给字段增加索引
    -- 2、创建完毕后,增加索引
    SHOW INDEX FROM student;
    -- 增加索引 有个索引的名字 然后字段名
    ALTER TABLE student ADD FULLTEXT INDEX `address` (`address`);
    
    
    -- EXPLAIN 分析sql执行的状况
    EXPLAIN SELECT * FROM student; -- 常规索引 非全文索引
    EXPLAIN SELECT * FROM student WHERE MATCH(address) AGAINST('ff'); -- 全文索引 只有一行
    

    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 NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
         PRIMARY KEY (`id`)
    )ENGINE = INNODB DEFAULT CHARSET=utf8mb4 COMMENT='app用户表';
    
    
    -- 插入一百万条数据
    DELIMITER $$ -- 写函数之前必须写,当作标志
    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),'1234@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();
    
     INSERT INTO `app_user` (`name`,`email`,`phone`,`gender`,`password`,`age`) VALUES 
     (CONCAT('用户',i),'1234@qq.com',CONCAT('18',FLOOR(RAND()*((999999999-100000000)+100000000))),
       FLOOR(RAND()*2),UUID(), FLOOR(RAND()*100));
    
    
    SELECT * FROM app_user WHERE `name` = '用户9999';
    EXPLAIN SELECT * FROM app_user WHERE `name` = '用户9999';
    
    -- id_表明——字段名
    -- CREATE [fulltext] INDEX  索引名 on 表(`字段`)
    CREATE INDEX id_app_user_id ON app_user (`name`);
    
    SELECT * FROM app_user WHERE `name` = '用户9999';
    EXPLAIN SELECT * FROM app_user WHERE `name` = '用户9999';
    
    -- 对比有无索引 非常明显 查询的行数 都一样了!!!
    

    索引在小数据量的时候,用处不大,但是在大数据的时候,区别十分明显

    7.3 索引原则

    • 索引不是越多越好
    • 不要对经常变动数据加索引
    • 小数据量的表不需要加索引
    • 索引一般加在常用来查询的字段上!

    索引的数据结构

    hash 类型的索引

    innodb默认索引(数据结构):Btree

    参考学习博客:

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

    8、权限管理和备份

    8.1用户管理

    SQL yog可视化管理

    -- 用户表:mysql.user
    -- 本质:就是表的增删减查
    
    
    -- 创建用户 CREATE USER 用户名 IDENTIFIED  BY `密码`;
    CREATE USER shuang IDENTIFIED  BY `123456`;
    
    -- 修改密码 (修改当前用户密码)
    SET PASSWORD = PASSWORD(`111111`);
    
    -- 修改密码 (修改指定用户密码)
    SET PASSWORD FOR shuang = PASSWORD(`123123`);
    
    -- 重命名 RENAME USER 原名字 TO 新名字;
    RENAME USER shuang TO summer;
    
    -- 用户授权   ALL PRIVILEGES 全部的权限 on 库.表 to 给谁
    -- ALL PRIVILEGES 除了给别人授权,其他都能干
    GRANT ALL PRIVILEGES ON *.* TO summer;
    
    -- 查看权限
    SHOW GRANT FOR summer; -- 查看指定用户的权限
    SHOW GRANT FOR root@localhost;
    
    -- root用户的权限:GRANT ALL PRIVILEGES ON *.* to 'root@localhost' with grant option
    
    -- 撤销权限  REVOKE 那些权限 , 在哪个库 ,给谁撤销  [撤销用from]
    REVOKE  ALL PRIVILEGES ON *.* FROM summer;
    
    -- 删除用户
    DROP USER summer;
    

    8.2 MySQL备份

    为什么要备份?

    • 保证重要的数据不丢失
    • 数据转移

    MySQL数据库备份方式

    • 直接拷贝物理文件
    • 在SQLyog这种可视化工具中手动导出
    • 使用命令行导出 mysqldump【命令行使用】
    mysqldump
    # mysqldump -h主机 -u用户名 -p密码 数据库 表名字 > 物理磁盘位置/文件名
    mysqldump -hlocalhost -uroot -p123456 shcool student > D:/a.sql
    
    mysqldump -hlocalhost -uroot -p123456 shcool  > D:/c.sql
    
    • 使用命令行导入数据

    先登录上去 mysql -uroot -p123456

    use school; -- 指定数据库
    source D:/a.sql; -- 导入对应表
    

    没有登录(实际也是要先登录)

    mysql -uroot -p12345 库名 <  备份文件
    

    假设你要备份数据库,防止数据丢失。

    9、规范数据库设计

    9.1 为什么需要设计

    当数据库比较复杂的时候,我们就需要设计了。

    糟糕的数据库设计:

    • 数据冗余,浪费空间
    • 数据库插入和删除都会麻烦,异常
    • 程序的性能差

    良好的数据库设计:

    • 节省内存空间
    • 保证数据库的完整性
    • 方便我们开发系统

    软件开发中,关于数据库的设计:

    • 分析需求:分析业务和需要处理的数据库的需求
    • 概要设计:设计关系图E-R图

    设计数据库的步骤:(个人博客)

    • 搜集信息,分析需求
    • 标识实体(把需求落实到每个字段)
    • 标示实体 之间的关系

    9.2 三大范式

    为什么需要数据规范化?

    • 信息重复
    • 更新异常
    • 插入异常
      • 无法正常显示信息
    • 删除异常
      • 丢失有效的信息

    第一范式

    原子性:保证每一列都是不可分割的

    第二范式

    前提:满足第一范式

    每张表只描述一件事情

    第三范式

    前提:满足第一范式和第二范式

    第三范式需要确保数据表中的每一列数据都和主键知己相关,而不能间接相关

    规范数据库的设计

    规范性和性能的问题

    阿里手册:关联查询的表不得超过三张表

    • 考虑商业化的需求和目标,(成本,目标)数据库的性能更加重要
    • 在规范性能的问题的时候,需要适当的考虑一下 规范性
    • 故意给某些表增加一些荣誉的字段(从多表查询中变为单表查询)
    • 故意增加一些计算列(从大数据量降低为小数据量的查询【可以增加索引】)

    10、JDBC(重点)

    10.1 数据库驱动

    驱动:声卡,显卡、数据库

    我们的程序会通过 数据库 驱动,和数据库打交道~

    10.2 JDBC

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

    这些规范的实现由具体的厂商去做~

    对于开发人员来说,我们只需要掌握JDBC接口的操作即可。

    java.sql

    javac.sql

    还需要导入一个数据库驱动包

    10.3 第一个JDBC程序

    创建测试数据库

    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,'zhansan','123456','zs.@sina.com','1980-12-04'),
    (2,'lisi','123456','lisi.@sina.com','1981-12-04'),
    (3,'wangwu','123456','wangwu.@sina.com','1979-12-04')
    

    1.创建一个普通项目

    2.导入数据库驱动

    创建lib目录----导入jar包----add as library 然后才可以展开

    3.编写测试代码

    package com.summer.study01;
    
    import java.sql.*;
    
    //我的第一个jdbc程序
    public class JdbcFirstDemo {
    
        public static void main(String[] args) throws ClassNotFoundException, SQLException {
            //1 加载驱动
            Class.forName("com.mysql.jdbc.Driver");// 固定写法,加载驱动
            //2用户信息和url
            //?useUnicode=true&characterEncoding=utf8&useSSL=true 三个参数背下来
            // 支持中文编码       设定字符集为utf8   SSL使用安全的连接
            String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
            String userName  = "root";
            String passWord = "";
            //3 连接成功 数据库对象 Connection 代表数据库
            Connection connection = DriverManager.getConnection(url, userName, passWord);
            //4 执行SQL的对象 Statement 执行sql的对象
            Statement statement = connection.createStatement();
            //5 执行SQL的对象 去执行 SQL (可能存在结果,查看返回结果)
            String sql ="SELECT * FROM users";
            ResultSet resultSet = statement.executeQuery(sql);//ResultSet 返回的结果集[链表] 结果集中封装了我们全部查询出来的结果
    
            while(resultSet.next()){
                System.out.println("id"+resultSet.getObject("id"));
                System.out.println("name"+resultSet.getObject("name"));
                System.out.println("pwd"+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();
        }
    
    }
    
    
    id1
    namezhansan
    pwd123456
    emailzs.@sina.com
    birthday1980-12-04
    ===================我是美丽的分割线===================
    id2
    namelisi
    pwd123456
    emaillisi.@sina.com
    birthday1981-12-04
    ===================我是美丽的分割线===================
    id3
    namewangwu
    pwd123456
    emailwangwu.@sina.com
    birthday1979-12-04
    ===================我是美丽的分割线===================
    
    Process finished with exit code 0s
    

    步骤总结;

    • 加载驱动
    • 连接数据库 DriverManager
    • 获取执行SQL的对象 Statement
    • 获得返回的结果集(只查询的才有结果集哦) ResultSet
    • 释放连接

    DriverManager

    //DriverManager.registerDriver(new com.mysql.jdbc.Driver());
            Class.forName("com.mysql.jdbc.Driver");// 固定写法,加载驱动
    
    
    //Connection 代表数据库 数据库能干的它都能干
    // 数据库设置自动提交
    //事务提交
    //事务回滚
            connection.rollback();
            connection.commit();
            connection.setAutoCommit(true);
    
            Connection connection = DriverManager.getConnection(url, userName, passWord);
          
    

    URL

    String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
           
    //mysql 默认port--3306
    //协议://主机地址:端口号/数据库名?参数1&参数2&参数3
    //oralce 默认port--1521
    //jdbc:oralce:thin@localhost:1521:sid
    

    Statement & PrepareStatement

    执行SQL的对象

    Statement statement = connection.createStatement();
    
    String sql ="SELECT * FROM users";//编写SQL
    
    statement.executeQuery();//查询操作 返回结果集 ResultSet
    statement.execute();// 执行任何的sql 有个判断的过程 效率低一点
    statement.executeUpdate();// 更新 插入 删除 都用这个 返回一个受影响的行数
    statement.executeBatch();//可以放多个sql来执行
    

    ResultSet

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

    获得指定的数据类型

    resultSet.getObject();//在不知道类型的时候使用
    resultSet.getString();//如果知道列的类型 就是用指定的类型...
    resultSet.getInt();
    resultSet.getDouble();
    resultSet.getFloat();
    resultSet.getDate();
    

    遍历,指针

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

    释放资源【必须做!】

    像connection连接是十分占内存的,就是耗资源,用完要关掉

    //6 释放连接 先开放的最后释放哦
    resultSet.close();
    statement.close();
    connection.close();
    

    10.4 Statement对象(重要)

    jdbc中的statement对象用于向数据库发送SQL语句,想要完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可

    Statement对象的executeUpdate方法,用于向数据库发送增、删、改的SQL语句,executeUpdate执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。

    Statement的executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象【链表】。

    CRUD操作-create

    使用executeUpdate(String sql)方法完成数据添加操作,示例操作:

    Statement st = conn.createStatement();
    String sql = "insert into user(...) values (...)";
    int num = st.executeUpdate(sql);
    if(num>0){
        System.out.println("输入成功~~");
    }
    

    CRUD操作-delete

    使用executeUpdate(String sql)方法完成数据删除操作,示例操作:

    Statement st = conn.createStatement();
    String sql = "delete from user where id =1 ";
    int num = st.executeUpdate(sql);
    if(num>0){
        System.out.println("删除成功~~");
    }
    

    CRUD操作-update

    使用executeUpdate(String sql)方法完成数据修改操作,示例操作:

    Statement st = conn.createStatement();
    String sql = "update from user set name = '' where id =1 ";
    int num = st.executeUpdate(sql);
    if(num>0){
        System.out.println("修改成功~~");
    }
    

    CRUD操作-select

    使用executeQuery(String sql)方法完成数据查询操作,示例操作:

    Statement st = conn.createStatement();
    String sql = "select * from user where id =1 ";
    ResultSet rs = st.executeQuery(sql);
    where(rs.next()){
        //根据获得的列的数据类型,分别调用rs的相应方法映射到java对象中
    }
    

    代码实现

    提取工具类

    package com.summer.study02.utils;
    
    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");
    
                //1、驱动只需要加载一次
                Class.forName(driver);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        //获取链接
        public static Connection getConnection() throws SQLException {
            return DriverManager.getConnection(url, username, password);
        }
    
        //释放连接资源
        public static void release(Connection connection, Statement statement, ResultSet resultSet){
                if (resultSet!=null){
                    try {
                        resultSet.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            if (statement!=null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection!=null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    
    

    配置

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false
    username=root
    password=123456
    

    2、编写增删改的方法executeUpdate

    insert

    package com.summer.study02;
    
    import com.summer.study02.utils.JdbcUtils;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class TestInsert {
        public static void main(String[] args) {
    
            Connection connection= null;
            Statement statement = null;
            ResultSet resultSet = null;
    
            try {
                 connection = JdbcUtils.getConnection();
                 statement = connection.createStatement();
                 String sql = "INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`)" +
                         "VALUES (4,'summer','123456','123@qq.com','2020-01-01');";
                int i = statement.executeUpdate(sql);
                if (i>0){
                    System.out.println("插入成功~~");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                JdbcUtils.release(connection,statement,null);
            }
        }
    }
    
    

    delete

    package com.summer.study02;
    
    import com.summer.study02.utils.JdbcUtils;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class TestDelete {
        public static void main(String[] args) {
    
            Connection connection= null;
            Statement statement = null;
            ResultSet resultSet = null;
    
            try {
                connection = JdbcUtils.getConnection();
                statement = connection.createStatement();
                String sql = "delete from users where id = 1";
                int i = statement.executeUpdate(sql);
                if (i>0){
                    System.out.println("删除成功~~");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                JdbcUtils.release(connection,statement,null);
            }
        }
    }
    
    

    update

    package com.summer.study02;
    
    import com.summer.study02.utils.JdbcUtils;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class TestUpdate {
        public static void main(String[] args) {
    
            Connection connection= null;
            Statement statement = null;
            ResultSet resultSet = null;
    
            try {
                connection = JdbcUtils.getConnection();
                statement = connection.createStatement();
                String sql = "update users set name = 'spring' where id = 2";
                int i = statement.executeUpdate(sql);
                if (i>0){
                    System.out.println("修改成功~~");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }finally {
                JdbcUtils.release(connection,statement,null);
            }
        }
    }
    
    

    select

    package com.summer.study02;
    
    import com.summer.study02.utils.JdbcUtils;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class TestSelect {
        public static void main(String[] args) {
            Connection connection = null;
            Statement statement = null;
            ResultSet resultSet = null;
            try {
                 connection = JdbcUtils.getConnection();
                 statement = connection.createStatement();
                String sql = "select * from users";
                 resultSet = statement.executeQuery(sql);
                while (resultSet.next()){
                    System.out.println(resultSet.getInt("id"));
                    System.out.println(resultSet.getString("name"));
                    System.out.println(resultSet.getString("password"));
                    System.out.println(resultSet.getString("email"));
                    System.out.println(resultSet.getString("birthday"));
                    System.out.println("-----------------------------------");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.release(connection,statement,resultSet);
            }
        }
    }
    
    

    结果:

    2
    spring
    123456
    lisi.@sina.com
    1981-12-04
    -----------------------------------
    3
    wangwu
    123456
    wangwu.@sina.com
    1979-12-04
    -----------------------------------
    4
    summer
    123456
    123@qq.com
    2020-01-01
    -----------------------------------
    
    Process finished with exit code 0
    

    SQL注入的问题?

    SQL存在漏洞,会被攻击导致数据泄露sql会被拼接

    package com.summer.study02;
    
    
    import com.summer.study02.utils.JdbcUtils;
    
    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("summer","123456"); 正常注入
            login("'or '1=1","'or '1=1");//技巧 
    
        }
    
        //登录业务
        public static void login(String username, String password){
            Connection connection = null;
            Statement statement = null;
            ResultSet resultSet = null;
            try {
                connection = JdbcUtils.getConnection();
                statement = connection.createStatement();
                //select * from users where name='summer' and password ='123456'
    
                //select * from users where name=''or ' 1=1' and password ='' or '1=1'
                String sql = "select * from users where name='"+username+"' and password ='"+password+"'";
                resultSet = statement.executeQuery(sql);
                while (resultSet.next()){
                    System.out.println(resultSet.getInt("id"));
                    System.out.println(resultSet.getString("name"));
                    System.out.println(resultSet.getString("password"));
                    System.out.println(resultSet.getString("email"));
                    System.out.println(resultSet.getString("birthday"));
                    System.out.println("-----------------------------------");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.release(connection,statement,resultSet);
            }
        }
    }
    
    

    结果:

    2
    spring
    123456
    lisi.@sina.com
    1981-12-04
    -----------------------------------
    3
    wangwu
    123456
    wangwu.@sina.com
    1979-12-04
    -----------------------------------
    4
    summer
    123456
    123@qq.com
    2020-01-01
    -----------------------------------
    
    Process finished with exit code 0
    

    10.5 PreparedStatement对象

    PreparedStatement可以防止SQL注入,并且效率更高~

    1.新增

    package com.summer.study03;
    
    import com.summer.study02.utils.JdbcUtils;
    
    import java.sql.*;
    
    public class TestInsert {
        public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                connection = JdbcUtils.getConnection();
                //区别
                //使用? 问号占位符 代替参数
                String sql = "INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`) VALUES (?,?,?,?,?)";
                preparedStatement =connection.prepareStatement(sql);//预编译SQL,先写SQL然后不执行
                //手动给参数赋值
                preparedStatement.setInt(1,5);
                preparedStatement.setString(2,"home");
                preparedStatement.setString(3,"123123");
                preparedStatement.setString(4,"123123@qq.com");
                //注意点 util.Date() java的  new java.util.Date().getTime()获得时间戳
                //      sql.Date()  mysql的  转换为数据库的date
                preparedStatement.setDate(5,new Date(new java.util.Date().getTime()));
                int i = preparedStatement.executeUpdate();
                if (i>0){
                    System.out.println("插入成功~");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.release(connection,preparedStatement,null);
            }
        }
    }
    
    

    2.删除

    package com.summer.study03;
    
    import com.summer.study02.utils.JdbcUtils;
    
    import java.sql.*;
    
    public class TestDelete {
        public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                connection = JdbcUtils.getConnection();
                //区别
                //使用? 问号占位符 代替参数
                String sql = "delete from users where id = ?";
                preparedStatement =connection.prepareStatement(sql);//预编译SQL,先写SQL然后不执行
                //手动给参数赋值
               preparedStatement.setInt(1,2);
                int i = preparedStatement.executeUpdate();
                if (i>0){
                    System.out.println("删除成功~");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.release(connection,preparedStatement,null);
            }
        }
    }
    
    

    3.更新

    package com.summer.study03;
    
    import com.summer.study02.utils.JdbcUtils;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    public class TestUpdate {
        public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                connection = JdbcUtils.getConnection();
                //区别
                //使用? 问号占位符 代替参数
                String sql = "update users set name = ? where id = ?";
                preparedStatement =connection.prepareStatement(sql);//预编译SQL,先写SQL然后不执行
                //手动给参数赋值
                preparedStatement.setString(1,"夏天");
                preparedStatement.setInt(2,3);
                int i = preparedStatement.executeUpdate();
                if (i>0){
                    System.out.println("更新成功~");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.release(connection,preparedStatement,null);
            }
        }
    }
    
    

    4.查询

    package com.summer.study03;
    
    import com.summer.study02.utils.JdbcUtils;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    
    public class TestSelect {
        public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                connection = JdbcUtils.getConnection();
                String sql = "select * from users where id = ?"; //编写sql
                preparedStatement =connection.prepareStatement(sql);//预编译
                preparedStatement.setInt(1,3);//传递参数
                resultSet = preparedStatement.executeQuery();//执行
                if (resultSet.next()){
                    System.out.println(resultSet.getString("name"));
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.release(connection,preparedStatement,resultSet);
            }
        }
    }
    
    

    结果:

    夏天
    
    Process finished with exit code 0
    

    5.防止SQL注入

    package com.summer.study03;
    
    
    import com.summer.study02.utils.JdbcUtils;
    
    import java.sql.*;
    
    public class SQL注入 {
        public static void main(String[] args) {
    //        login("summer","123456"); //正常注入
            login("'' or 1=1","'' or 1=1");//技巧
    
        }
    
        //登录业务
        public static void login(String username, String password){
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                connection = JdbcUtils.getConnection();
                //PreparedStatement 防止SQL注入的本质,把传递进来的参数当做字符
                //假设其中存在转义字符 就直接忽略 ''会被直接被转义
                String sql = "select * from users where name=? and password =?";//mybatis
                preparedStatement = connection.prepareStatement(sql);
                preparedStatement.setString(1,username);
                preparedStatement.setString(2,password);
                resultSet = preparedStatement.executeQuery();
                while (resultSet.next()){
                    System.out.println(resultSet.getInt("id"));
                    System.out.println(resultSet.getString("name"));
                    System.out.println(resultSet.getString("password"));
                    System.out.println(resultSet.getString("email"));
                    System.out.println(resultSet.getString("birthday"));
                    System.out.println("-----------------------------------");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.release(connection,preparedStatement,resultSet);
            }
        }
    }
    
    

    结果:

    
    Process finished with exit code 0
    

    10.6 使用IDEA连接数据库

    解决IDEA中右边不显示数据库Database

    • file-settings-plugins install database navigator

    用IDEA连接数据库步骤

    • IDEA右边database----选择加号dataSource----Mysql

    然后会出现本机 用户名 密码 连接的url

    【注】:一定要把jar包导进去才能成功

    • 连接成功后,可以选择数据库
    • 点击设置---schemas 来勾选数据库 ---apply
    • 双击数据库和表可以看到表和数据
    • 修改后要点击提交
    • sql控制台也在界面中哦-- 可以在这里编写sql代码
    • 然后控制台右边可以切换数据库
    /* 创建一个账户表*/
    create table if not exists `account`(
      id int primary key auto_increment,
      name varchar(40),
      money float);
    
    -- 插入数据
    insert into account(name,money) values ('A','1000'),('B','1000'),('C','1000')
    

    用IDEA回遇到的一些坑

    • 在连接的时候driver是空的 会导致点击测试连接变成失败----原因是没有导入lib目录

    10.7 事务

    要么都成功,要么都失败

    ACID原则

    • 原子性:要么全部完成,要么都不完成
    • 一致性:最终一致性,总数不变
    • 隔离性:多个进程互不干扰
    • 持久性:一旦提交不可逆,持久化到数据库

    隔离性的问题:

    • 脏读:一个事务读取了另一个没有提交的事务
    • 不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变
    • 幻读/虚读:在一个事务内,读取到了别人插入的数据,导致前后读出来的结果不一致

    代码实现

    1、开启事务connection.setAutoCommit(false);

    2、一组业务执行完毕,提交事务connection.commit();

    3、可以在catch语句中显示的定义回滚语句connection.rollback(),但默认失败就会回滚

    示例1:

    package com.summer.study04;
    
    import com.summer.study02.utils.JdbcUtils;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    public class TestTransaction1 {
        public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
    
            try {
                connection= JdbcUtils.getConnection();
                //关闭数据库的自动提交功能 自动会开启事务
                connection.setAutoCommit(false);
                String sql1 = "update account set money = money -100 where name = 'A'";
                preparedStatement= connection.prepareStatement(sql1);
                preparedStatement.executeUpdate();
                String sql2 = "update account set money = money +100 where name = 'B'";
                preparedStatement= connection.prepareStatement(sql2);
                preparedStatement.executeUpdate();
                //业务完毕 提交事务
                connection.commit();
                System.out.println("操作成功");
            } catch (SQLException e) {
                //如果失败默认回滚
    //            try {
    //                connection.rollback();//如果失败则回滚事务
    //            } catch (SQLException exception) {
    //                exception.printStackTrace();
    //            }
                e.printStackTrace();
            } finally {
                JdbcUtils.release(connection,preparedStatement,null);
            }
        }
    }
    
    
    

    示例2:

    package com.summer.study04;
    
    import com.summer.study02.utils.JdbcUtils;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    
    public class TestTransaction2 {
        public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
    
            try {
                connection= JdbcUtils.getConnection();
                //关闭数据库的自动提交功能 自动会开启事务
                connection.setAutoCommit(false);
                String sql1 = "update account set money = money -100 where name = 'A'";
                preparedStatement= connection.prepareStatement(sql1);
                preparedStatement.executeUpdate();
    
                int x = 1/0;//报错
    
                String sql2 = "update account set money = money +100 where name = 'B'";
                preparedStatement= connection.prepareStatement(sql2);
                preparedStatement.executeUpdate();
                //业务完毕 提交事务
                connection.commit();
                System.out.println("操作成功");
            } catch (SQLException e) {
                //如果失败默认回滚
    //            try {
    //                connection.rollback();//如果失败则回滚事务
    //            } catch (SQLException exception) {
    //                exception.printStackTrace();
    //            }
                e.printStackTrace();
            } finally {
                JdbcUtils.release(connection,preparedStatement,null);
            }
        }
    }
    
    
    

    10.8 数据库连接池

    数据库连接---》执行完毕---》释放 【连接--释放 十分浪费系统资源】

    池化技术

    准备一些预先的资源,过来就连接预先准备好的

    最小连接数:10

    最大链接数:100 业务最高承载上限

    等待超时;100ms

    编写连接池

    实现一个接口 DataSource

    开源数据源实现

    DBCP

    C3P0

    Druid:阿里巴巴

    使用了这些数据库连接池后,我们在项目开发中就不需要编写连接数据库的代码了

    DBCP

    导两个jar包

    commons-dbcp-1.4.jar commons-pool-1.6.jar

    配置:

    # 连接设置 这里面的名字字,是DBCP数据源中定义好的
    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false
    username=root
    password=123456
    
    #初始化连接池
    initialSize=10
    
    #最大连接数
    maxAction=50
    
    #最大空闲连接
    maxIdle=20
    
    #最小空闲连接
    minIdle=5
    
    #超时等待时间以毫秒为单位60000毫秒/1000 等于60s
    maxWait=60000
    
    #JDBC驱动建立连接时附带的连接属性 属性的格式必须为这样:[属性名=property]
    #注意:“user” 与 “password” 两个属性会被明确地传递,因此这里不需要包含他们
    connectionProperties=useUnicode=true;characterEncoding=UTF8
    
    #指定由连接池所创建的连接的自动提交(auto-commit)状态
    defaultAutoCommit=true
    
    #driver default 指定由连接池所创建的连接的只读(read-only)状态
    #如果没有设置改值 则setReadOnly方法将不会被调用 (某些驱动并不支持只读模式,如:Informix)
    defaultReadOnly=
    
    
    

    utils

    package com.summer.study05.utils;
    
    import org.apache.commons.dbcp.BasicDataSource;
    import org.apache.commons.dbcp.BasicDataSourceFactory;
    
    import javax.sql.DataSource;
    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_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.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 connection, Statement statement, ResultSet resultSet){
                if (resultSet!=null){
                    try {
                        resultSet.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            if (statement!=null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection!=null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    
    

    test

    package com.summer.study05;
    
    import com.summer.study02.utils.JdbcUtils;
    import com.summer.study05.utils.JdbcUtils_DBCP;
    
    import java.sql.*;
    
    public class TestDbcp {
        public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                connection = JdbcUtils_DBCP.getConnection();
                //区别
                //使用? 问号占位符 代替参数
                String sql = "INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`) VALUES (?,?,?,?,?)";
                preparedStatement =connection.prepareStatement(sql);//预编译SQL,先写SQL然后不执行
                //手动给参数赋值
                preparedStatement.setInt(1,6);
                preparedStatement.setString(2,"home");
                preparedStatement.setString(3,"123123");
                preparedStatement.setString(4,"123123@qq.com");
                //注意点 util.Date() java的  new java.util.Date().getTime()获得时间戳
                //      sql.Date()  mysql的  转换为数据库的date
                preparedStatement.setDate(5,new Date(new java.util.Date().getTime()));
                int i = preparedStatement.executeUpdate();
                if (i>0){
                    System.out.println("插入成功~");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.release(connection,preparedStatement,null);
            }
        }
    }
    
    

    C3P0

    导两个jar包

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

    配置:

    <c3p0-config>
    <default-config><!--
        c3p0的缺省(默认)配置
        如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource();”
        这样写就表示C3P0的缺省配置
    -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&amp;characterEncoding=UTF8</property>
        <property name="user">root</property>
        <property name="password">123456</property>
    
        <property name="acquireIncrement">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”);”
          这样写就表示C3P0 name的配置
    -->
    <named-config name="MySQL">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&amp;characterEncoding=UTF8</property>
        <property name="user">root</property>
        <property name="password">123456</property>
    
        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
    </c3p0-config>
    

    utils

    package com.summer.study05.utils;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    
    public class JdbcUtils_C3P0 {
    
       private static ComboPooledDataSource dataSource = null;
    
        static {
            try {
                //  代码版配置 一般不用
    //            dataSource =new ComboPooledDataSource();
    //            dataSource.setDriverClass();
    //            dataSource.setUser();
    //            dataSource.setPassword();
    //            dataSource.setJdbcUrl();
    //            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 connection, Statement statement, ResultSet resultSet){
                if (resultSet!=null){
                    try {
                        resultSet.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            if (statement!=null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection!=null){
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
        }
    }
    
    

    test

    package com.summer.study05;
    
    import com.summer.study02.utils.JdbcUtils;
    import com.summer.study05.utils.JdbcUtils_C3P0;
    import com.summer.study05.utils.JdbcUtils_DBCP;
    
    import java.sql.*;
    
    public class TestC3P0 {
        public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try {
                connection = JdbcUtils_C3P0.getConnection();//原来是自己实现的,现在用别人实现的
                //区别
                //使用? 问号占位符 代替参数
                String sql = "INSERT INTO users(`id`,`name`,`password`,`email`,`birthday`) VALUES (?,?,?,?,?)";
                preparedStatement =connection.prepareStatement(sql);//预编译SQL,先写SQL然后不执行
                //手动给参数赋值
                preparedStatement.setInt(1,8);
                preparedStatement.setString(2,"home");
                preparedStatement.setString(3,"123123");
                preparedStatement.setString(4,"123123@qq.com");
                //注意点 util.Date() java的  new java.util.Date().getTime()获得时间戳
                //      sql.Date()  mysql的  转换为数据库的date
                preparedStatement.setDate(5,new Date(new java.util.Date().getTime()));
                int i = preparedStatement.executeUpdate();
                if (i>0){
                    System.out.println("插入成功~");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JdbcUtils_C3P0.release(connection,preparedStatement,null);
            }
        }
    }
    
    

    结论

    无论使用什么数据源,本质还是一样的,DataSource接口不会变,方法就不会变

    Druid

    springboot讲~

  • 相关阅读:
    动态调整iframe的高度
    Binary Tree Zigzag Level Order Traversal
    Leetcode Anagrams
    二叉树层次遍历串成单链表
    leetcode 4sum
    leetcode 二叉树系列
    编程之美2.3
    Decode Ways
    leetcode graycode
    leetcode editdistance
  • 原文地址:https://www.cnblogs.com/ls-summer/p/14260297.html
Copyright © 2011-2022 走看看