zoukankan      html  css  js  c++  java
  • MySql

    连接数据库

    命令行连接

    mysql -uroot -pmyPass@123456
    

    创建数据库

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

    常用命令

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

    引擎

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

    常规使用操作:

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

    在物理空间存在的位置

    所有的数据库文件都在data目录下,一个文件夹对应一个数据库

    本质还是文件的存储!

    MySql引擎在物理文件上的区别

    • INNODB 在数据库表中只有一个 *.frm文件,以及上级目录下的idtada1文件
    • MYISAM 对应的文件
      • *.frm 表结构的定义文件
      • *.MYD 数据文件(data)
      • *.MYI 索引文件(index)

    设置数据表的字符集编码

    CHARSET=UTF8
    

    不设置的话会是mysql默认的字符集编码 (不支持中文)

    修改和删除表

    -- 修改表名ALTER TABLE 旧表名 RENAME AS 新表名;
    ALTER TABLE student RENAME AS teacher;
    
    -- 增加字段 ALTER TABLE 表名 ADD 字段名 列属性[];
    ALTER TABLE teacher ADD age INT(11);
    
    -- 修改表的字段(重命名/修改约束)
    -- ALTER TABLE 表名 MODIFY 字段名 列属性[];
    ALTER TABLE teacher MODIFY age VARCHAR(11);  -- 修改字段约束,不修改名字
    -- ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列属性[];
    ALTER TABLE teacher CHANGE age age1 INT(11);  -- 修改字段名字,不修改约束
    
    -- 删除表的字段
    -- ALTER TABLE 表名 DROP 字段名;
    ALTER TABLE teacher DROP age1;
    
    -- 删除表(如果表存在再删除)
    DROP TABLE IF EXISTS teacher;
    

    所有的创建和删除操作尽量加上判断,以免报错

    Mysql数据管理

    • 外键(了解即可)

      方式一,在创建表的时候,增加约束(麻烦,比较复杂)

      方式二,建表后再添加外键约束

      -- 建表的时候没有外键关系
      alter table `student` 
      add constraint `FK_gradeid` foreign key(`gradeid`) 
      references `grade`(`gradeid`);
      -- alter table 表 
      -- add constraint 约束名 foreign key(作为外键的列) 
      -- references 哪个表(哪个字段)
      

      以上的操作都是物理级别的外键,数据库级别的外键,不建议使用,避免数据库过多造成困扰

      DML语言(全部记住背下来)

      数据操作语言
      • insert
      • update
      • delete
    • 添加insert

      insert into 表名(字段名1,字段名2,......]) values('值1','值2'),('值1','值2'),(......)

      -- 插入语句(添加)
      -- insert into 表名(字段名1,字段名2) values('值1'),('值2'),(...)
      insert	into `grade`(`gradename`) values('大四');
      
      -- 由于主键自增我们可以省略(如果不写表的字段,他就会一一匹配)
      -- 一半血插入语句,我们一定要数据和字段一一对应
      -- 插入多个字段
      insert into `student`(`name`,`sex`)
      values('张三','男'),('李四','男');
      
    • 修改update

      update 修改谁(条件) set 原来的值=新值

      -- 修改学生的名字  带条件
      update `student` set `name`='大帅' where id=1;
      -- 不指定条件情况下  会改动所有
      update `student` set `name`='大帅';
      -- 修改多个属性  属性之间逗号隔开
      update `student` set `name`='大江',`sex`='女' where id=1;
      -- 语法
      -- update 表名 set 字段名1=值1,字段名2=值2,...... where [条件]
      
      -- 通过多个条件定位数据  无上限
      update `student` set `name`='邱大江',`sex`='男' where `name`='大帅' and `sex`='男';
      
    操作符 含义 范围 结果
    = 等于
    <>或!= 不等于
    >
    <
    >=
    <=
    between 2 an 5 在某个范围内,闭合区间 [2,5]
    and && 5>1 and 1>2 false
    or || 5>1 and 1>2 true
    • 删除

      • delete命令

      delete from 表名[where 条件]

      -- 删除数据(避免这样写,会全部删除)
      delete from `student`;
      -- 删除指定数据
      delete from `student` where `id`=1;
      
      • truncate命令

        完全清空一个数据库表,表的结构和索引约束不会变

        -- 清空student表
        truncate `student`;
        
      • delete和truncate的区别

        相同点:都可以删数据,都不会删除表结构

        不同点:

        • truncate重新设置自增列,计数器会归零
        • truncate不会影响事务
        -- 测试delete和truncate的区别
        create table `test`(
        `id` int(4) not null auto_increment,
        `name` varchar(20) not null,
        primary key(`id`)
        )engine=innodb default charset=utf8
        insert into `test`(`name`) values('qiu'),('da');
        delete from `test`;   -- 不会影响自增
        truncate table `test`;  -- 自增会归零
        

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

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

    DQL查询数据(最重点)

    DQL:数据查询语言

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

    指定查询字段

    -- 查询全部学生  select 字段 from 表
    select * from student;
    -- 查询指定字段
    select `studentno`,`studentname` from `student`;
    
    -- 使用别名,给结果起一个名字 as   可以给字段起别名,也可以给表起别名
    select `studentno` as 学号,`studentname` as 学生姓名 from `student` as 学生;
    
    -- 函数 Concat(a,b)
    select CONCAT('姓名:',`studentname`) as 新名字 from `student`;
    

    去重:distinct

    取除重复的数据,只显示一条

    -- 查询一下哪些同学参加了考试
    select * from `result`; -- 查询全部的考试成绩
    
    -- 查询有哪些同学参加了考试
    select `studentno` from `result`;
    -- 发现重复数据   去重
    select distinct `studentno` from `result`;
    

    数据库的列

    select VERSION();-- 查看系统版本(函数)
    select 100*3-1 as 计算结果;  -- 用来计算(计算表达式)
    select @@auto_increment_increment; -- 查询自增的步长(变量)
    
    -- 学员考试成绩+1分
    select `studentno`,`studentresult`+1 as '提分后' from `result`;
    
    

    where 条件子句

    搜索条件由一个或者多个表达式组成,结果为布尔值

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

    逻辑运算符

    运算符 语法 描述
    and && a and b a && b 逻辑与
    or || a or b a || b 逻辑或
    not ! not a !a 逻辑非

    尽量使用英文字母

    -- ========================= where =======================
    
    select studentno,studentresult from result;
    -- 查询考试成绩再95-100之间的
    
    select studentno,studentresult from result
    where studentresult >= 95 and studentresult <= 100;
    
    
    -- 模糊查询
    select studentno,studentresult from result
    where studentresult between 70 and 100;
    
    -- 除了1000号学生之外的同学的成绩
    select studentno,studentresult from result
    where studentno !=1000;
    

    模糊查询 :比较运算符

    运算符 语法 描述
    is null
    is not null
    between a between b and c a在b和c之间为真
    like a like b SQL匹配,如果a能匹配到b则结果为真
    in a in (a1,a2,a3,......) a在a1或a2或a3......其中的某一个值中,结果为真
    -- ========================= 模糊查询 =======================
    
    -- 查询姓张的同学
    -- =========like结合 %(代表0到任意个字符)  _(代表一个字符)================
     select `studentno`,`studentname` from `student`
     where studentname like '张%';
     
     -- 名字后面只有一个字的
     select `studentno`,`studentname` from `student`
     where studentname like '张_';
    
     -- 名字后面只有两个字的
     select `studentno`,`studentname` from `student`
     where studentname like '张__';
    
    -- 查询名字中间有强字的同学
     select `studentno`,`studentname` from `student`
     where studentname like '%强%';
    
    -- ========= in (具体的一个或者多个值)==========
    
    -- 查询1000,1001号学员
     select `studentno`,`studentname` from `student`
     where studentno in (1000,1001);
    
    -- 查询在北京的学生
    select studentno ,studentname from student
    where address in ('北京朝阳','广东深圳');
    
    -- ======== null , not null==========
    -- 查询地址为空的学生  null或者''
    select studentno,studentname from student
    where address='' or address is null;
    
    -- 查询有出生日期的同学
    select studentno,studentname from student
    where borndate is not null;
    

    联表查询

    img

    -- =============== 联表查询 join ===============
    -- 查询参加了考试的同学(学号 姓名 科目编号 分数)
    select * from `student`;
    select * from `result`;
    /*
    1.分析需求,分析查询的字段来自哪些表(连接查询)
    2.确定使用哪种连接查询?
    3.确定交叉点(这两个表中哪个数据是相同的)
    判断条件:学生表 studentno = 成绩表 studentno
    */
    -- ================ inner join =============
    select s.studentno,studentname,subjectno,studentresult
    from student as s
    inner join result  as r
    where s.studentno=r.studentno;
    -- =========== right join ===============
    select s.studentno,studentname,subjectno,studentresult
    from student as s
    right join result as r
    on s.studentno=r.studentno;
    
    -- =========== left join ===============
    select s.studentno,studentname,subjectno,studentresult
    from student as s
    left join result as r
    on s.studentno=r.studentno;
    
    操作 描述
    inner join 如果表中至少有一个匹配就返回行
    left join 即使右表没有匹配也会从左表中返回所有的值
    right join 即使左表没有匹配也会从右表中返回所有的值
    -- =============== 联表查询 join ===============
    -- 查询参加了考试的同学(学号 姓名 科目编号 分数)
    select * from `student`;
    select * from `result`;
    /*
    1.分析需求,分析查询的字段来自哪些表(连接查询)
    2.确定使用哪种连接查询?
    3.确定交叉点(这两个表中哪个数据是相同的)
    判断条件:学生表 studentno = 成绩表 studentno
    */
    -- join(连接的表) on(判断的条件)   连接查询
    -- where     等值查询
    -- ================ inner join =============
    select s.studentno,studentname,subjectno,studentresult
    from student as s
    inner join result  as r
    on s.studentno=r.studentno;
    -- =========== right join ===============
    select s.studentno,studentname,subjectno,studentresult
    from student as s
    right join result as r
    on s.studentno=r.studentno;
    
    -- =========== left join ===============
    select s.studentno,studentname,subjectno,studentresult
    from student as s
    left join result as r
    on s.studentno=r.studentno;
    
    -- 查询缺考的同学
    select s.studentno,studentname,subjectno,studentresult
    from student as s
    left join result as r
    on s.studentno=r.studentno
    where studentresult is null;
    
    
    -- 思考题(查询了参加考试的同学的信息:学号,学生姓名,科目名称,分数)
    /*
    1.分析需求,分析查询的字段来自哪些表   student、result、subject(连接查询)
    2.确定使用哪种连接查询?     
    3.确定交叉点(这两个表中哪个数据是相同的)
    判断条件:学生表 studentno = 成绩表 studentno
    */
    select s.studentno as 学号,studentname as 姓名,subjectname as 学科,studentresult as 成绩
    from student s
    right join result r
    on r.studentno=s.studentno
    inner join subject sub
    on sub.subjectno=r.subjectno;
    
    

    自连接

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

    父类:

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

    子类:

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

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

    父类 子类
    信息技术 办公信息
    软件开发 数据库
    软件开发 web开发
    美术设计 PS技术
    -- 查询父子信息:  把一张表看成两张一摸一样的表
    select a.`categoryname` as 父栏目,b.`categoryname` as 子栏目
    from `category` as  a,`category` as b
    where a.`categoryid`=b.`pid`;
    

    分页和排序

    排序:

    -- ================= 分页 limit 和排序 order by ===================
    -- 排序: 升序asc  降序desc
    -- order by 需要排序的字段 怎么排
    -- 查询的结果根据成绩 降序 排序
    select s.studentno as 学号,studentname as 姓名,subjectname as 学科,studentresult as 成绩
    from student s
    right join result r
    on r.studentno=s.studentno
    inner join subject sub
    on sub.subjectno=r.subjectno
    order by studentresult desc;
    

    分页

    -- 分页,每页显示三条数据
    -- limit 起始值,页面的大小  limit 0,3  1~3
    -- 第一页 limit 0,3
    -- 第二页 limit 3,3
    -- 第三页 limit 6,3
    -- 第n页 limit (n-1)*3,3     (n-1)*pageSzie,pageSzie
    -- pageSzie:页面大小
    -- n:当前页
    -- 总页数:数据总数/页面大小
    select s.studentno as 学号,studentname as 姓名,subjectname as 学科,studentresult as 成绩
    from student s
    right join result r
    on r.studentno=s.studentno
    inner join subject sub
    on sub.subjectno=r.subjectno
    order by studentresult desc
    limit 0,3;
    

    练习:

    -- 练习
    -- 查询高等数学-1 课程成绩排名前十的学生,并且分数要大于80的学生信息
    -- 学号,姓名,课程名称,分数
    select s.studnetno,studentname,subjectname,studentresult
    from student s
    right join result r
    on s.studntno=r.`studentno`
    inner join subject sub
    on sub.`subjectno`=r.`subjectno`
    where studentresult>80 and subjectname='高等数学-1'
    order by studentresult desc
    limit 0,10;
    

    子查询

    where(这个值是计算出来的)

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

    -- ============= where 子查询===============
    -- 查询高等数学-1 的所有考试结果(学号,科目编号,成绩)
    -- 子查询 (由里及外)
    select studentno,r.subjectno,studentresult
    from result r
    where subjectno=(
    	select subjectno from subject
    	where subjectname='高等数学-1'
    )
    order by studentresult desc;
    
    
    -- 查询 高等数学-1 分数不小于80分的学生的学号和姓名
    select distinct s.studentno,studentname
    from student s
    inner join result r
    on r.`studentno`=s.`studentno`
    inner join subject sub
    on sub.`subjectno`=r.`subjectno`
    where studentresult>=80 and subjectname='高等数学-1'
    
    -- 在上面的基础上增加一个科目,高等数学-1  查询高等数学-1的编号
    select distinct s.studentno,studentname
    from student s
    inner join result r
    on r.`studentno`=s.`studentno`
    where studentresult>=80 and subjectno=(
    select subjectno from `subject`
    where `subjectname`='高等数学-1'
    )
    
    -- 再改造  多重嵌套  (由里及外)
    select studentno,studentname from student where studentno in(
    	select studentno from result where studentresult >80 and subjectno=(
    		select subjectno from `subject` where subjectname='高等数学-1'
    	)
    )
    

    分组和过滤

    -- 查询不同课程的平均分 最高分 最低分 平均分大于80分
    -- 核心:根据不同的课程分组
    select subjectname,AVG(studentresult),MAX(studentresult),MIN(studentresult)
    from result r
    inner join `subject` sub
    on r.`subjectno`=sub.`subjectno`
    group by r.`subjectno`  -- 通过什么字段来分组
    having AVG(studentresult)>80 -- 分组后过滤
    

    select小结

    完整语法

    -- 顺序不能变!!!
    -- select 去重 要查询的字段 from 表
    -- xxxx join 要连接的表 on 等值判断
    -- where(具体的值/子查询语句)
    -- group by 字段
    -- having 过滤条件
    -- order by 字段 排序规则
    -- limit 开始位置,每页大小
    

    MySql函数

    常用函数

    --  =========== 常用函数 ============
    -- 数学运算
    select ABS(-1)  -- 绝对值
    select CEILING(1.23)  -- 向上取整
    select FLOOR(1.23)   -- 向下取整
    select RAND()  -- 返回一个0-1之间的随机数
    select SIGN(-123)  -- 判断一个数的符号  1:正数   -1:负数  
    
    -- 字符串函数
    select CHAR_LENGTH('qiudajiang') -- 返回字符串长度
    select CONCAT('wo','ai','ni')  -- 拼接字符串
    
    -- 插入,替换 从第1某个位置开始替换2长度的字符串
    select INSERT('woaibiancheng',1,2,'chaojireai')  
    
    -- 大小写转换
    select LOWER('Qiu')
    select UPPER('dahusia')
    
    -- 查询字符在指定字符串第一次出现的位置(索引)
    select INSTR('qiu','i')
    
    -- 替换指定字串的字符
    select REPLACE('qiudajiang','jiang','shuai')
    
    -- 截取指定字符串中的一段字符  从第4个开始,截取6个
    select SUBSTRING('qiudashai',4,6)
    
    -- 反转字符串
    select REVERSE('qiudajiang')
    
    -- 查询姓周的同学,替换姓为李 
    select REPLACE(studentname,'张','李') from student
    where studentname like '张%'
    
    -- 时间和日期函数(记住)
    select CURRENT_DATE() -- 获取当前日期
    select NOW() -- 获取当前准确时间
    select LOCALTIME()  -- 获取本地时间
    select SYSDATE() -- 系统时间
    
    -- 年月日时分秒
    select YEAR(NOW())
    select MONTH(NOW())
    select DAY(NOW())
    select HOUR(NOW())
    select MINUTE(NOW())
    select SECOND(NOW())
    
    -- 系统
    select USER()
    select VERSION()
    

    聚合函数(常用)

    函数名称 描述
    count() 计数
    sum() 求和
    avg() 平均值
    max() 最大值
    min() 最小值
    ...... ......
    -- ============ 聚合函数 ==================
    --  都能够统计表中的数据
    select COUNT(studentname) from student -- count(字段)   会忽略所有的null值
    select COUNT(*) from student  -- count(*)  不会忽略null值  本质 计算行数
    select COUNT(1) from result  -- count(1)   不会忽略null值  本质 计算行数
     
     select SUM(studentresult) as 总和 from result
     select AVG(studentresult) as 平均分 from result
     select MAX(studentresult) as 最高分 from result
     select MIN(studentresult) as 最低分 from result
    

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

    MD5不可逆

    -- =================== 测试MD5 加密 =================
    create table `md5test`(
    `id` int(4) not null,
    `name` varchar(100) not null,
    `pwd` varchar(100) not null,
    primary key(`id`)
    )engine=innodb default charset=utf8
    
    -- 明文密码
    insert into `md5test` values(1,'qiu','123456'),(2,'zhang','123456'),(3,'li','123456')
    
    -- 加密 
    update `md5test` set pwd=MD5(pwd) where id =1
    update `md5test` set pwd=MD5(pwd)  -- 加密全部的密码
    
    -- 插入时就加密
    insert into `md5test` values(4,'qiudajiang',MD5('234235'))
    
    -- 如何校验  将用户传递进来的密码进行MD5加密,然后比对加密后的值
    select * from md5test where `name`='qiudajiang' and pwd=MD5('123456')
    

    事务

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

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

    原子性:

    要么都成功,要么都失败

    一致性

    事务前后的数据完整性要保证一致

    持久性

    事务一旦提交则不可逆,被持久化到数据库中

    隔离性

    执行事务

    事务的隔离性是多个用户并访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,事务之间要相互隔离

    -- ====================== 事务 ===============
    -- MySql 是默认开启事务自动提交的
    -- 手动处理事务
    set autocommit = 0  -- 关闭自动提交
    -- 事务开启
    start transaction -- 标记一个事务的开始   从这个之后的sql都在同一个事务内
    
    insert xxx
    insert xxx
    
    -- 提交:持久化(成功)
    commit
    -- 回滚:回到原来的样子(失败)
    rollback
    
    -- 事务结束
    set aotocommit = 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(30) not null,
    `money` decimal(9,2) not null,
    primary key(`id`)
    )engine=innodb default charset=utf8
    
    insert into `account`(`name`,`money`) 
    values ('求大奖',200.00),('阿迪斯',2000.00)
    
    -- 模拟转账:事务
    set autocommit = 0 -- 关闭自动提交
    start transaction -- 开启一个事务
    
    update account set money = money-100 where `name` = '求大奖'; -- 求大奖减100
    update account set money = money+100 where `name` = '阿迪斯'; -- 阿迪斯加100
    
    commit;  -- 提交事务,成功就会被持久化,无法回滚
    rollback; -- 回滚
    set autocommit = 1; -- 恢复默认值
    

    索引

    索引的分类

    • 主键索引 primary key
      • 唯一的标识 主键不可重复 只能有一个列作为主键
    • 唯一索引 unique key
      • 避免重复的列出现,唯一索引可以重复,多个列都可以标识为唯一索引
    • 常规索引 key/index
      • 默认的
    • 全文索引 fulltext
      • 在特定的数据库引擎下才有
      • 快速定位数据

    基础语法

    -- 索引的使用
    -- 1、创建表的时候增加索引
    -- 2、创建完毕后增加索引
    
    -- 显示索引信息
    show index from student
    
    -- 增加全文索引
    -- alter table 表名 add 索引类型 索引名(列名)
    alter table school.`student` add fulltext index `studentname`(`studentname`)
    
    -- 删除索引
    -- ALTER TABLE 表名 DROP INDEX 索引名;
    ALTER TABLE schoole.`student` DROP INDEX `studentname`;
    
    -- explain  分析SQL语句执行的状况
    explain select * from student ;  -- 非全文索引
    
    explain select * from student where MATCH(studentname) against('张伟')
    

    索引原则

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

    用户管理

    • 用户管理

    SQL命令操作

    用户表mysql.user

    本质:对这张表增删改查

    -- 创建用户 create user 用户名 identified by '密码'
    create user qiudashuai identified by '123456';
    
    -- 修改密码(修改当前用户密码)
    set password = PASSWORD('123456');
    
    -- 修改指定用户密码 
    set password for qiudashuai = PASSWORD('123456');
    
    -- 重命名  rename user 旧名字 to 新名字
    rename user qiudashuai to qiudajiang;
    
    -- 用户授权 all privileges 全部的权限,库,表  
    -- all privileges  除了给别人授权,其他的都能干
    grant all privileges on *.* to qiudajiang;
    
    -- 查看指定用户的权限
    show grants for qiudajiang;
    
    -- 查看管理员的权限
    show grants for root@localhost;
    
    -- 撤销权限
    -- revoke 哪些权限 在哪个库撤销 给谁撤销
    revoke all privileges on *.* from qiudajiang
    
    -- 删除用户
    drop user qiudajiang;
    

    MySql备份

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

    mysql数据库备份方式

    • 直接拷贝物理文件

    • 在可视化工具上直接导出

    • 使用命令行导出 mysqldump

      -- 导出
      mysqldump -uroot -pmyPass@123456 school student >D:/a.sql
      -- 导入
      -- 先登录进去
      mysql -uroot -pmyPass@123456
      -- 导入过程
      use school;
      source d:/a.sql
      

    数据库的规约和三大范式(数据库设计)

    糟糕的数据库设计:

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

    良好的数据库设计:

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

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

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

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

    • 收集信息,分析需求
      • 用户表
      • 分类表(文章分类,谁创建的)
      • 文章表(文章的信息)
      • 评论表
      • 友情链接表
      • 自定义表(系统信息,某个关键的字,或者一些主字段) key:value
    • 标识实体之间的关系(把需求落地到每个字段)
      • user->blog 写博客
      • user->category 创建分类
      • user->user 关注
      • links 友情链接
      • user-user-blog 评论

    三大范式

    • 第一范式

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

    • 第二范式

      前提:满足第一范式

      每张表只描述一件事情

    • 第三范式

      满足第一范式和第二范式

      确保数据表中的每一列数据都和主键直接相关,而不能间接相关

    规范性和性能的问题:

    关联查询的表不能超过三张表

    1. 考虑商业化的需求和目标(成本,用户体验!)数据库的性能更加重要
    2. 在规范性能的问题的时候,需要适当考虑一下规范性
    3. 故意给某些表增加一些冗余的字段(从多表查询变为单表查询)
    4. 故意增加一些计算列(从大数据量降低为小数据量的查询)

    JDBC(重点)

    数据库驱动

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

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

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

    java.sql

    javax.sql

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

    创建测试数据库

    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. 导入数据库驱动

      1599619001075

    3. 编写测试代码

      package com.qiu.demo;
      import java.sql.*;
      //我的第一个JDBC程序
      public class jdbcFirst {
          public static void main(String[] args) throws ClassNotFoundException, SQLException {
              //1.加载驱动
              Class.forName("com.mysql.cj.jdbc.Driver");  //固定写法  加载驱动
              //2.用户信息和url
              //useUnicode=true&&characterEncoding=utf8&&useSSL=true 支持中文编码  设置字符集为utf8  使用安全连接
              //serverTimezone=GMT%2B8   时区设置 如果时区不是本地时区会报错
              String url="jdbc:mysql://localhost:3306/jdbcstudy" +
                          "?useUnicode=true&&characterEncoding=utf8&&useSSL=true&&serverTimezone=GMT%2B8";
              String username="root";
              String pwd="myPass@123456";
              //3.连接成功  返回数据库对象   Connection 代表数据库
              Connection connection = DriverManager.getConnection(url, username, pwd);
              //4.执行SQL的对象  Statement 执行SQL的对象
              Statement statement = connection.createStatement();
              //5.执行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("pwd="+resultSet.getObject("PASSWORD"));
                  System.out.println("email="+resultSet.getObject("email"));
                  System.out.println("birth="+resultSet.getObject("birthday"));
                  System.out.println("=================");
              }
              //6.释放连接
              resultSet.close();
              statement.close();
              connection.close();
          }
      }
      

      步骤总结:

      • 加载驱动
      • 连接数据库DriverManager
      • 获取执行SQL的对象 Statement对象
      • 获得返回的结果集
      • 释放连接

    代码实现

    1. 提取工具类

      public class jdbcUtils {
          private static String driver = null;
          private static String url = null;
          private static String name = null;
          private static String pwd = null;
          static {
              try{
                  InputStream inputStream = jdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
                  Properties properties = new Properties();
                  properties.load(inputStream);
      
                  driver = properties.getProperty("driver");
                  url = properties.getProperty("url");
                  name = properties.getProperty("name");
                  pwd = properties.getProperty("pwd");
      
                  //加载驱动  驱动只需要加载一次
                  Class.forName(driver);
              } catch (IOException | ClassNotFoundException e){
                  e.printStackTrace();
              }
          }
          // 获取连接
          public static Connection getConnection() throws SQLException {
              return DriverManager.getConnection(url,name,pwd);
          }
      
          //释放连接资源
          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();
                  }
              }
          }
      }
      
      
    2. 编写增删改的方法一样 executeUpdate(),这里只写一个插入。

      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(); //获得SQL的执行对象
                  String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`)" +
                          "values (4,'大帅','123456','12865@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,resultSet);
              }
          }
      }
      
      

      3.查询

      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();
                  //SQL
                  String sql = "select * from users where id =1";
                  resultSet =statement.executeQuery(sql);  //查询完毕会返回一个结果集
                  while (resultSet.next()){
                      System.out.println(resultSet.getString("NAME"));
                  }
              } catch (SQLException e) {
                  e.printStackTrace();
              } finally {
                  jdbcUtils.release(connection,statement,resultSet);
              }
          }
      }
      

      SQL注入的问题

      存在漏洞,会被攻击,导致数据泄露

    PreparedStatement对象

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

    1. 增删改

      package com.qiu.demo02;
      
      import com.qiu.demo01.utils.jdbcUtils;
      import java.util.Date;
      import java.sql.*;
      
      public class TestPreparedStatement {
          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  先写,不执行
                  //手动给参数赋值
                  preparedStatement.setInt(1,5);
                  preparedStatement.setString(2,"qiu");
                  preparedStatement.setString(3,"123456");
                  preparedStatement.setString(4,"12386@qq.com");
                  preparedStatement.setDate(5,new java.sql.Date(new Date().getTime()));
      
                  //执行
                  int i = preparedStatement.executeUpdate();
                  if (i>0){
                      System.out.println("插入成功");
                  }
              } catch (SQLException e) {
                  e.printStackTrace();
              } finally {
                  jdbcUtils.release(connection,preparedStatement,resultSet);
              }
          }
      }
      
    2. 查询

      package com.qiu.demo02;
      
      import com.qiu.demo01.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 = ?";
                  preparedStatement = connection.prepareStatement(sql); //预编译
                  preparedStatement.setInt(1,1); //传递参数
                  //执行
                  resultSet = preparedStatement.executeQuery();
                  while (resultSet.next()){
                      System.out.println(resultSet.getString("NAME"));
                  }
              } catch (SQLException e) {
                  e.printStackTrace();
              } finally {
                  jdbcUtils.release(connection,preparedStatement,resultSet);
              }
          }
      }
      
      

    IDEA连接数据库

    1599643216514

    连接成功后可以选择数据库

    1599643740019

    查看表信息:双击

    1599643803975

    更新数据 修改之后点击上面db按钮

    1599643894878

    写SQL语句的地方

    1599643995077

    事务

    要么都成功要么都失败

    ACID原则

    原子性:要么全部完成,要么都不完成

    一致性:总数不变

    隔离性:多个进程互不干扰

    持久性:一旦提交,就持久化到数据库

    隔离性的问题

    脏读:一个事务读取了另一个没有提交的事务

    不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变

    虚读幻读:在一个事务内,读取到了别人插入的数据,导致前后读出的结果不一致

    提交事务测试代码

    public static void main(String[] args) {
            Connection connection = null;
            PreparedStatement preparedStatement = null;
            ResultSet resultSet = null;
            try{
                connection = jdbcUtils.getConnection();
                
                connection.setAutoCommit(false);//关闭数据库自动提交,会自动开启事务
                String sql = "update account set money = money - 100 where name = 'A'";
                preparedStatement = connection.prepareStatement(sql);
                preparedStatement.executeUpdate();
    
                String sq2 = "update account set money = money + 100 where name = 'B'";
                preparedStatement = connection.prepareStatement(sq2);
                preparedStatement.executeUpdate();
    
                //业务完毕 提交事务
                connection.commit();
                System.out.println("操作成功");
            } catch (SQLException e) {
                try {
                    connection.rollback(); //如果失败 则回滚事务   如果不写默认也会回滚
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
                e.printStackTrace();
            } finally {
                jdbcUtils.release(connection,preparedStatement,resultSet);
            }
        }
    

    数据库连接池

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

    池化技术:准备一些预先的资源,过来就连接预先准备好的

    最小连接数:10

    最大连接数:100 业务最高承载上线

    等待超时: 100ms

    编写连接池,实现一个接口DataSource

    开源数据源实现

    dbcp

    c3p0

    Druid:阿里巴巴

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

    DBCP

    需要用到的jar包:commons-dbcp-1.4 commons-pool-1.6

    实现代码:

    • 数据库连接池
    //数据库连接池
    #连接设置
    driverClassName=com.mysql.cj.jdbc.Driver
    url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true&&serverTimezone=GMT%2B8
    username=root
    password=myPass@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_UNCOMMITTED
    
    • jdbcUtils_DBCP工具类

      package com.qiu.Demo5.utils;
      
      
      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 inputStream = jdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
                  Properties properties = new Properties();
                  properties.load(inputStream);
                  //创建数据源   工厂模式-->创建对象
                  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();
                  }
              }
          }
      }
      
    • 测试类

      package com.qiu.Demo5;
      
      import com.qiu.Demo5.utils.jdbcUtils_DBCP;
      import com.qiu.demo01.utils.jdbcUtils;
      
      import java.sql.*;
      
      public class Test {
          public static void main(String[] args) {
              Connection connection = null;
              PreparedStatement preparedStatement = null;
              ResultSet resultSet = null;
              try {
                  connection = jdbcUtils_DBCP.getConnection();
                  //SQL
                  String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`)"+
                                  "values (?,?,?,?,?)";
                  preparedStatement =connection.prepareStatement(sql);
                  //手动给参数赋值
                  preparedStatement.setInt(1,6);
                  preparedStatement.setString(2,"qiudajianga");
                  preparedStatement.setString(3,"123456");
                  preparedStatement.setString(4,"12386@qq.com");
                  preparedStatement.setDate(5,new java.sql.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,resultSet);
              }
          }
      }
      

    C3P0

    需要用到的jar包

    mchange-commons-java-0.2.20 c3p0-0.9.5.5

    • c3p0配置文件

      <?xml version="1.0" encoding="UTF-8"?>
      <c3p0-config>
          <!--
          c3p0的缺省(默认)配置
          如果在代码中"ComboPooledDataSource ds=new ComboPooledDataSource();"这样写就表示使用的是c3p0的缺省(默认)-->
          <default-config>
              <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
              <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&amp;characterEncoding=utf8&amp;uesSSL=true&amp;serverTimezone=UTC</property>
              <property name="user">root</property>
              <property name="password">myPass@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-config>
      
    • 工具类

      public class jdbcUtils_c3p0 {
          private static ComboPooledDataSource dataSource = null;
          static {
              try{
                  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();
                  }
              }
          }
      }
      
    • 测试代码:

      public class Test {
          public static void main(String[] args) {
              Connection connection = null;
              PreparedStatement preparedStatement = null;
              ResultSet resultSet = null;
              try {
                  connection = jdbcUtils_c3p0.getConnection();
                  //SQL
                  String sql = "insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`)"+
                                  "values (?,?,?,?,?)";
                  preparedStatement =connection.prepareStatement(sql);
                  //手动给参数赋值
                  preparedStatement.setInt(1,7);
                  preparedStatement.setString(2,"dada");
                  preparedStatement.setString(3,"123456");
                  preparedStatement.setString(4,"12386@qq.com");
                  preparedStatement.setDate(5,new java.sql.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,resultSet);
              }
          }
      }
      

    结论

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

  • 相关阅读:
    uni-app上拉加载
    uni-app 下拉刷新
    uni-app 的生命周期
    怎么看这个方法谁调用了?
    强推
    idea中spring热部署工具加载过慢问题彻底解决
    不能在此路径中使用此配置节。如果在父级别上锁定了该节,便会出现这种情况。锁定是默认设置的(overrideModeDefault="Deny"),或者是通过包含 overrideMode="Deny" 或旧有的 allowOverride="false" 的位置标记明确设置的。
    Springboot多数据源JdbcTemplate使用(非XA)
    关于Mysql报错:java.sql.SQLException: The statement (1) has no open cursor
    杂感记录
  • 原文地址:https://www.cnblogs.com/qiudajiang/p/13623538.html
Copyright © 2011-2022 走看看