01.mysql相关
1.数据库的基本概念
1. 数据库的英文单词: DataBase 简称 : DB
2. 什么数据库?用于存储和管理数据的仓库。
3. 数据库的特点:
1. 持久化存储数据的。其实数据库就是一个文件系统
2. 方便存储和管理数据
3. 使用了统一的方式操作数据库 -- SQL
4.数据库管理系统基本组成:数据库、数据表、开发语言SQL
5.选择mysql的优势:1.技术优势:mysql是开源的,因此使用mysql不用依赖封闭的数据库而受牵制。
2.成本优势:mysql是开源的,使用它的成本小。
3.跨平台性。
4.操作简单,支持多线程,快速且安全,性价比高。
5.mysql的集群功能。
2.数据表:
1.数据表:列、索引、触发器
列:属性列,创建表时必须制定名字和类型。
索引:建立的顺序,提供快速访问数据的途径且可监督表的结构。
触发器:用户定义的事务命令集合,当对一个表中的数据进行插入、删除、更新时,这组命令就会自动执行,用来确保数据的完整性与安全性。
7.数据表的三个范式:
第一范式:确保每列的原子性
第二范式:确保每列都和主键相关
第三范式:确保每列都和主键直接相关,而不是间接相关
3.SQL
1.什么是SQL?
Structured Query Language:结构化查询语言
其实就是定义了操作所有关系型数据库的规则。每一种数据库操作的方式存在不一样的地方,称为“方言”。
2. SQL分类
1) DDL(Data Definition Language)数据定义语言
用来定义数据库对象:数据库,表,列等。关键字:create, drop,alter 等
2) DML(Data Manipulation Language)数据操作语言
用来对数据库中表的数据进行增删改。关键字:insert, delete, update 等
3) DQL(Data Query Language)数据查询语言
用来查询数据库中表的记录(数据)。关键字:select, where 等
4) DCL(Data Control Language)数据控制语言(了解)
用来定义数据库的访问权限和安全级别,及创建用户。关键字:GRANT, REVOKE 等
DDL:操作数据库、表
1. 操作数据库:CRUD
1. C(Create):创建
* 创建数据库:
* create database 数据库名称;
* 创建数据库,判断不存在,再创建:
* create database if not exists 数据库名称;
* 创建数据库,并指定字符集
* create database 数据库名称 character set 字符集名;
2. R(Retrieve):查询
* 查询所有数据库的名称:
* show databases;
* 查询某个数据库的字符集:查询某个数据库的创建语句
* show create database 数据库名称;
3. U(Update):修改
* 修改数据库的字符集
* alter database 数据库名称 character set 字符集名称;
4. D(Delete):删除
* 删除数据库
* drop database 数据库名称;
* 判断数据库存在,存在再删除
* drop database if exists 数据库名称;
5. 使用数据库
* 查询当前正在使用的数据库名称
* select database();
* 使用数据库
* use 数据库名称;
2. 操作表
1. C(Create):创建
1. 语法:
create table 表名(
列名1 数据类型1,
列名2 数据类型2,
....
列名n 数据类型n
);
* 注意:最后一列,不需要加逗号(,)
* 数据库类型:
1. int:整数类型
2. double:小数类型
3. date:日期,只包含年月日,yyyy-MM-dd
4. datetime:日期,包含年月日时分秒 yyyy-MM-dd HH:mm:ss
5. timestamp:时间错类型 包含年月日时分秒 yyyy-MM-dd HH:mm:ss
* 如果将来不给这个字段赋值,或赋值为null,则默认使用当前的系统时间,来自动赋值
6. varchar:字符串
* name varchar(20):姓名最大20个字符
* zhangsan 8个字符 张三 2个字符
* 创建表
create table student(
id int,
name varchar(32),
age int ,
score double(4,1),
birthday date,
insert_time timestamp
);
* 复制表:
* create table 表名 like 被复制的表名;
2. R(Retrieve):查询
* 查询某个数据库中所有的表名称
* show tables;
* 查询表结构
* desc 表名;
3. U(Update):修改
1. 修改表名
alter table 表名 rename to 新的表名;
2. 修改表的字符集
alter table 表名 character set 字符集名称;
3. 添加一列
alter table 表名 add 列名 数据类型;
4. 修改列名称 类型
alter table 表名 change 列名 新列别 新数据类型;
5. 删除列
alter table 表名 drop 列名;
4. D(Delete):删除
* drop table 表名;
* drop table if exists 表名 ;
DML:增删改表中数据
1. 添加数据:
* 语法:
* insert into 表名(列名1,列名2,...列名n) values(值1,值2,...值n);
* 注意:
1. 列名和值要一一对应。
2. 如果表名后,不定义列名,则默认给所有列添加值
insert into 表名 values(值1,值2,...值n);
3. 除了数字类型,其他类型需要使用引号(单双都可以)引起来
2. 删除数据:
* 语法:
* delete from 表名 [where 条件]
* 注意:
1. 如果不加条件,则删除表中所有记录。
2. 如果要删除所有记录
1. delete from 表名; -- 不推荐使用。有多少条记录就会执行多少次删除操作
2. TRUNCATE TABLE 表名; -- 推荐使用,效率更高 先删除表,然后再创建一张一样的表。
3. 修改数据:
* 语法:
* update 表名 set 列名1 = 值1, 列名2 = 值2,... [where 条件];
* 注意:
1. 如果不加任何条件,则会将表中所有记录全部修改。
DQL:单表查询;
1. 语法:
select
字段列表
from
表名列表
where
条件列表
group by
分组字段
having
分组之后的条件
order by
排序
limit
分页限定
2. 基础查询
1. 多个字段的查询
select 字段名1,字段名2... from 表名;
* 注意:
* 如果查询所有字段,则可以使用*来替代字段列表。
2. 去除重复:
* distinct
3. 计算列
* 一般可以使用四则运算计算一些列的值。(一般只会进行数值型的计算)
* ifnull(表达式1,表达式2):null参与的运算,计算结果都为null
* 表达式1:哪个字段需要判断是否为null
* 如果该字段为null后的替换值。
4. 起别名:
* as:as也可以省略
3. 条件查询
1. where子句后跟条件
2. 运算符
* > 、< 、<= 、>= 、= 、<>
* BETWEEN...AND
* IN( 集合)
* LIKE:模糊查询
* 占位符:
* _:单个任意字符
* %:多个任意字符
* IS NULL
* and 或 &&
* or 或 ||
* not 或 !
-- 查询年龄大于20岁
SELECT * FROM student WHERE age > 20;
-- 查询年龄大于等于20 小于等于30
SELECT * FROM student WHERE age >= 20 && age <=30;
SELECT * FROM student WHERE age >= 20 AND age <=30;
SELECT * FROM student WHERE age BETWEEN 20 AND 30;
-- 查询年龄22岁,18岁,25岁的信息
SELECT * FROM student WHERE age = 22 OR age = 18 OR age = 25
SELECT * FROM student WHERE age IN (22,18,25);
-- 查询英语成绩为null
SELECT * FROM student WHERE english = NULL; -- 不对的。null值不能使用 = (!=) 判断
SELECT * FROM student WHERE english IS NULL;
-- 查询英语成绩不为null
SELECT * FROM student WHERE english IS NOT NULL;
-- 查询姓马的有哪些? like
SELECT * FROM student WHERE NAME LIKE '马%';
-- 查询姓名第二个字是化的人
SELECT * FROM student WHERE NAME LIKE "_化%";
-- 查询姓名是3个字的人
SELECT * FROM student WHERE NAME LIKE '___';//三条下划线
-- 查询姓名中包含德的人
SELECT * FROM student WHERE NAME LIKE '%德%';
DQL:高级语句
1. 排序查询
* 语法:order by 子句
* order by 排序字段1 排序方式1 ,排序字段2 排序方式2...
* 排序方式:
* ASC:升序,默认的。
* DESC:降序。
* 注意:
* 如果有多个排序条件,则当前边的条件值一样时,才会判断第二条件。
2. 聚合函数:将一列数据作为一个整体,进行纵向的计算。
1. count:计算个数
1. 一般选择非空的列:主键
2. count(*)
2. max:计算最大值
3. min:计算最小值
4. sum:计算和
5. avg:计算平均值
* 注意:聚合函数的计算,排除null值。
解决方案:
1. 选择不包含非空的列进行计算
2. IFNULL函数
3. 分组查询:
1. 语法:group by 分组字段;
2. 注意:
1. 分组之后查询的字段:分组字段、聚合函数
2. where 和 having 的区别?
1. where 在分组之前进行限定,如果不满足条件,则不参与分组。having在分组之后进行限定,如果不满足结果,则不会被查询出来
2. where 后不可以跟聚合函数,having可以进行聚合函数的判断。
-- 按照性别分组。分别查询男、女同学的平均分
SELECT sex , AVG(math) FROM student GROUP BY sex;
-- 按照性别分组。分别查询男、女同学的平均分,人数
SELECT sex , AVG(math),COUNT(id) FROM student GROUP BY sex;
-- 按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组
SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex;
-- 按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组,分组之后。人数要大于2个人
SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex HAVING COUNT(id) > 2;
SELECT sex , AVG(math),COUNT(id) 人数 FROM student WHERE math > 70 GROUP BY sex HAVING 人数 > 2;
4. 分页查询
1. 语法:limit 开始的索引,每页查询的条数;
SELECT * FROM student LIMIT 0,3; -- 第1页
SELECT * FROM student LIMIT 3,3; -- 第2页
SELECT * FROM student LIMIT 6,3; -- 第3页
多表查询
* 查询语法:
select
列名列表
from
表名列表
where....
* 准备sql
# 创建部门表
CREATE TABLE dept(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(20)
);
INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部');
# 创建员工表
CREATE TABLE emp (
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(10),
gender CHAR(1), -- 性别
salary DOUBLE, -- 工资
join_date DATE, -- 入职日期
dept_id INT,
FOREIGN KEY (dept_id) REFERENCES dept(id) -- 外键,关联部门表(部门表的主键)
);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男',7200,'2013-02-24',1);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男',3600,'2010-12-02',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008-08-08',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011-03-14',1);
* 笛卡尔积:
* 有两个集合A,B .取这两个集合的所有组成情况。
* 要完成多表查询,需要消除无用的数据
* 多表查询的分类:
1. 内连接查询:
1. 隐式内连接:使用where条件消除无用数据
* 例子:
-- 查询所有员工信息和对应的部门信息
SELECT * FROM emp,dept WHERE emp.`dept_id` = dept.`id`;
-- 查询员工表的名称,性别。部门表的名称
SELECT emp.name,emp.gender,dept.name FROM emp,dept WHERE emp.`dept_id` = dept.`id`;
SELECT
t1.name, -- 员工表的姓名
t1.gender,-- 员工表的性别
t2.name -- 部门表的名称
FROM
emp t1,
dept t2
WHERE
t1.`dept_id` = t2.`id`;
2. 显式内连接:
* 语法: select 字段列表 from 表名1 [inner] join 表名2 on 条件
* 例如:
* SELECT * FROM emp INNER JOIN dept ON emp.`dept_id` = dept.`id`;
* SELECT * FROM emp JOIN dept ON emp.`dept_id` = dept.`id`;
3. 内连接查询:
1. 从哪些表中查询数据
2. 条件是什么
3. 查询哪些字段
2. 外链接查询:
1. 左外连接:
* 语法:select 字段列表 from 表1 left [outer] join 表2 on 条件;
* 查询的是左表所有数据以及其交集部分。
* 例子:
-- 查询所有员工信息,如果员工有部门,则查询部门名称,没有部门,则不显示部门名称
SELECT t1.*,t2.`name` FROM emp t1 LEFT JOIN dept t2 ON t1.`dept_id` = t2.`id`;
2. 右外连接:
* 语法:select 字段列表 from 表1 right [outer] join 表2 on 条件;
* 查询的是右表所有数据以及其交集部分。
* 例子:
SELECT * FROM dept t2 RIGHT JOIN emp t1 ON t1.`dept_id` = t2.`id`;
3. 子查询:
* 概念:查询中嵌套查询,称嵌套查询为子查询。
-- 查询工资最高的员工信息
-- 1 查询最高的工资是多少 9000
SELECT MAX(salary) FROM emp;
-- 2 查询员工信息,并且工资等于9000的
SELECT * FROM emp WHERE emp.`salary` = 9000;
-- 一条sql就完成这个操作。子查询
SELECT * FROM emp WHERE emp.`salary` = (SELECT MAX(salary) FROM emp);
* 子查询不同情况
1. 子查询的结果是单行单列的:
* 子查询可以作为条件,使用运算符去判断。 运算符: > >= < <= =
*
-- 查询员工工资小于平均工资的人
SELECT * FROM emp WHERE emp.salary < (SELECT AVG(salary) FROM emp);
2. 子查询的结果是多行单列的:
* 子查询可以作为条件,使用运算符in来判断
-- 查询'财务部'和'市场部'所有的员工信息
SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部';
SELECT * FROM emp WHERE dept_id = 3 OR dept_id = 2;
-- 子查询
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部');
3. 子查询的结果是多行多列的:
* 子查询可以作为一张虚拟表参与查询
-- 查询员工入职日期是2011-11-11日之后的员工信息和部门信息
-- 子查询
SELECT * FROM dept t1 ,(SELECT * FROM emp WHERE emp.`join_date` > '2011-11-11') t2
WHERE t1.id = t2.dept_id;
-- 普通内连接
SELECT * FROM emp t1,dept t2 WHERE t1.`dept_id` = t2.`id` AND t1.`join_date` > '2011-11-11'
4.约束
* 概念: 对表中的数据进行限定,保证数据的正确性、有效性和完整性。 * 分类: 1. 主键约束:primary key 2. 非空约束:not null 3. 唯一约束:unique 4. 外键约束:foreign keyauto_increment
5. 自动增长:
6. 默认值 :dafault value
一. 非空约束:not null,值不能为null
1. 创建表时添加约束
CREATE TABLE stu(
id INT,
NAME VARCHAR(20) NOT NULL -- name为非空
);
2. 创建表完后,添加非空约束
ALTER TABLE stu MODIFY NAME VARCHAR(20) NOT NULL;
3. 删除name的非空约束
ALTER TABLE stu MODIFY NAME VARCHAR(20);
二.唯一约束:unique,值不能重复
三. 主键约束:primary key。
注意:
1. 含义:非空且唯一
2. 一张表只能有一个字段为主键
3. 主键就是表中记录的唯一标识
四.自动增长:
五.外键约束:foreign key,让表于表产生关系,从而保证数据的正确性。
1. 在创建表时,可以添加外键
* 语法:
create table 表名(
....
外键列
CONSTRAINT 外键名称 FOEEIGN KEY (外键列名称) REFERENCE 主表名称(主表列名称)
);
2. 删除外键
ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;
3. 创建表之后,添加外键
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称);
4. 级联操作
1. 添加级联操作
语法:ALTER TABLE 表名 ADD CONSTRAINT 外键名称
FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称) ON UPDATE CASCADE ON DELETE CASCADE ;
2. 分类:
1. 级联更新:ON UPDATE CASCADE
2. 级联删除:ON DELETE CASCADE
5.数据库的设计
1. 多表之间的关系
1. 分类:
1. 一对一(了解):
* 如:人和身份证:一个人只有一个身份证,一个身份证只能对应一个人
2. 一对多(多对一):
* 如:部门和员工:一个部门有多个员工,一个员工只能对应一个部门
3. 多对多:
* 如:学生和课程:一个学生可以选择很多门课程,一个课程也可以被很多学生选择
2. 实现关系:
1. 一对多(多对一):
* 如:部门和员工
* 实现方式:在多的一方建立外键,指向一的一方的主键。
2. 多对多:
* 如:学生和课程
* 实现方式:多对多关系实现需要借助第三张中间表。中间表至少包含两个字段,这两个字段作为第三张表的外键,分别指向两张表的主键
3. 一对一(了解):
* 如:人和身份证
* 实现方式:一对一关系实现,可以在任意一方添加唯一外键指向另一方的主键。
2. 数据库设计的范式
* 概念:设计数据库时,需要遵循的一些规范。要遵循后边的范式要求,必须先遵循前边的所有范式要求
设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。
* 分类:
1. 第一范式(1NF):每一列都是不可分割的原子数据项
2. 第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于码(在1NF基础上消除非主属性对主码的部分函数依赖)
* 几个概念:
1. 函数依赖:A-->B,如果通过A属性(属性组)的值,可以确定唯一B属性的值。则称B依赖于A
例如:学号-->姓名。 (学号,课程名称) --> 分数
2. 完全函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定需要依赖于A属性组中所有的属性值。
例如:(学号,课程名称) --> 分数
3. 部分函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定只需要依赖于A属性组中某一些值即可。
例如:(学号,课程名称) -- > 姓名
4. 传递函数依赖:A-->B, B -- >C . 如果通过A属性(属性组)的值,可以确定唯一B属性的值,在通过B属性(属性组)的值可以确定唯一C属性的值,则称 C 传递函数依赖于A
例如:学号-->系名,系名-->系主任
5. 码:如果在一张表中,一个属性或属性组,被其他所有属性所完全依赖,则称这个属性(属性组)为该表的码
例如:该表中码为:(学号,课程名称)
* 主属性:码属性组中的所有属性
* 非主属性:除过码属性组的属性
3. 第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)
6.存储引擎
1.定义:存储引擎就是表的类型。存储引擎指定了表的类型,即如何存储和索引数据,是否支持事务,决定了表在计算机中存储的方式。
2.查看mysql支持的引擎:SHOW ENGINES;
3.怎么修改默认的存储引擎:
修改my.cnf或者my.ini文件的配置,在mysqld组下添加:default-storage-engine=INNODB,再重新启动mysql即可。
常见的存储引擎:
InnoDB:1.被mysql支持。
2.提供事务、回滚、崩溃修复能力和并发控制的事务安全。
3.提供外键约束、支持自动增长
优点:提供事务、回滚、崩溃修复能力和并发控制。缺点:读写效率稍差,占用空间相对较大。MyISAM:
1.被mysql支持
2.支持静态、动态、压缩中三种存储格式。
优点:占用空间小、处理速度快
缺点:不支持事务和并发性
MEMORY:
1.被mysql支持
2.使用存储在内存中的内容来创建表,所有数据都放在内存中。
优点:处理速度快
缺点:安全性不高
7.事务
当多个用户访问同一份数据,一个用户在更改数据的过程中可能有其他用户同时发起了更改请求,为保证数据库记录的更新从一个一致性状态变更为另一个一致性状态,我们引进了事务的概念。
Transaction :在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
- 为什么要有事务?
为保证数据库记录的更新从一个一致性状态变更为另一个一致性状态,我们引进了事务的概念。
使用命令行方式演示事务。
-
开启事务
start transaction;
-
提交或者回滚事务
commit: 提交事务, 数据将会写到磁盘上的数据库 ;rollback : 数据回滚,回到最初的状态。
-
关闭自动提交功能。
使用代码方式演示事务
代码里面的事务,主要是针对连接来的。
通过conn.setAutoCommit(false )来关闭自动提交的设置。
提交事务 conn.commit();
回滚事务 conn.rollback();
事务的特性
- 原子性
指的是事务中包含的逻辑,不可分割。
- 一致性
指的是事务执行前后。数据完整性
- 隔离性
指的是事务在执行期间不应该受到其他事务的影响
- 持久性
指的是事务执行成功,那么数据应该持久保存到磁盘上。
事务总结
-
在代码里面会使用事务
conn.setAutoCommit(false); conn.commit(); conn.rollback(); -
事务只是针对连接连接对象,如果再开一个连接对象,那么那是默认的提交。
-
事务是会自动提交的。
需要了解的
安全隐患
一.读
1.脏读
一个事务读到了另一个事务更新但未提交更新的数据。理解:在读未提交的隔离级别下,1.A事务开启,B事务开启,2.B事务修改数据但还未提交,A事务读取该数据。读到了没有提交的数据,这就是脏读。
2.不可重复读
一个事务在另一个事务更新数据的前后都读了该数据,造成更新前后两次查询结果不一致。注意:这里强调的是读到的数据不一致,与幻读有区别。理解:在读已提交的隔离级别下,1.A事务开启,B事务开启,2.A第一次读该数据,B事务修改数据并且提交,
A事务第二次读取该数据。在A事务内,前后两次读到的数据不一致,这就是不可重复读。
3.幻读
一个事务第一次没读到一条数据,第二次读到了另一个事务插入的数据 ,造成前后查询结果不一致。注意,这里强调的是第一次没有读到,第二次读到了。理解:在可重复读的隔离级别下,1.A事务开启,B事务开启,2.A事务读指定数据,发现表中没有该条
数据,3.B事务添加该条数据并且提交,4.A事务插入该数据,发现数据已经存在添加失败。在A事务内,第一次读取发现表中没有该数据,插入时发现表中又有了该条数据,这就是幻读。
不可重复读与幻读的区别:
相同点:都是在同一个事务中出现两次读取不一致的错误。
不同点:不可重复读强调两次都读到了,只是读到的不一样;幻读强调第一次没读到,第二次插入时读到了导致插入失败。
二.写 1.丢失更新。
设置及查看Mysql的事务隔离级别
1.查看
SELECT @@tx_isolation
2.设置
2.1所有级别
1)read uncommitted : 未提交 :哪个问题都不能解决
2)read committed:提交读:可以解决脏读 ---- oracle默认的
3)repeatable read:可重读读:可以解决脏读 和 不可重复读 ---mysql默认的
4)serializable:可串行化:可以解决 脏读 不可重复读 和 幻读---相当于锁表
2.2 设置
设置mysql的隔离级别:set session transaction isolation level 设置事务隔离级别
隔离级别
未提交读
引发问题: 脏读
提交读
解决: 脏读 , 引发: 不可重复读
可重复读
解决: 脏读 、 不可重复读 , 引发: 幻读
可串行化
解决: 脏读、 不可重复读 、 幻读。
理解:如果有一个连接的隔离级别设置为了串行化 ,那么谁先打开了事务, 谁就有了先执行的权利, 谁后打开事务,谁就只能得着,等前面的那个事务,提交或者回滚后,才能执行。 但是这种隔离级别一般比较少用。 容易造成性能上的问题。 效率比较低。
- 按效率划分,从高到低
读未提交 > 读已提交 > 可重复读 > 可串行化
- 按拦截程度 ,从高到底
可串行化 > 可重复读 > 读已提交 > 读未提交
丢失更新
第一类丢失更新 (通过设置隔离级别可以防止 Repeatable Read)
A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能造成很严重的问题,通过下面的账户取款转账就可以看出来:

第二类丢失更新 (需要应用程序控制,乐观锁)
A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失:

解决丢失更新
- 悲观锁:认为在读取时大概率会发生修改操作
可以在查询语句后加入语句: for update
- 乐观锁:认为在读取时大概率不会发生修改操作
要求程序员自己控制。
8.索引
1.索引:是建立在表上的,是对数据库表中的一列或多列的值进行排序的一种结构,其作用是提高对表中数据的查询速度。相当于新华字典的音序表。
优点:提高检索数据的速度
缺点:创建和维护索引需要耗费时间和空间。表现在:1.索引占用一定的物理空间;2.增加、删除、修改数据时,要响应地动态的维护索引
注意:索引提高了查询速度,但是会影响插入速度:因为向有索引的表中插入数据时,数据库系统会按照索引进行排序,降低了插入速度。
当插入大量数据时,最好的方法是:先删除索引,然后插入数据,再创建索引。
2.索引分类:
普通索引、唯一索引、全文索引、单列索引、多列索引、空间索引、
三.索引设计原则:
1.选择唯一性索引
2.为经常需要排序、分组、联合操作的字段建立索引
3.为经常作为查询条件的字段建立索引
4.限制索引的数目
5.尽量使用数据量少的索引
6.删除不再使用或很少使用的索引
四.
1.普通索引
在建表时创建:在建表语句内的最后添加:INDEX [indexname](数据表的哪个字段);如:INDEX index_stuno(stuno).
建表完成再添加:CREATE INDEX [indexname] ON tablename(哪一个字段);如:CREATE INDEX index_stuno ON stu(stuno).
2.唯一索引
在建表时创建:在建表语句内的最后添加:UNIQUE INDEX [indexname](数据表的哪个字段);如:UNIQUE INDEX index_stuno(stuno).
建表完成再添加:CREATE UNIQUE INDEX [indexname] ON tablename(哪一个字段);如:CREATE UNIQUE INDEX index_stuno ON stu(stuno).
3.全文索引
在建表时创建:在建表语句内的最后添加:FULLINDEX [indexname](数据表的哪个字段);如:FULLINDEX index_stuno(stuno).
建表完成再添加:CREATE INDEX [indexname] ON tablename(哪一个字段);如:CREATE FULLINDEX index_stuno ON stu(stuno).
8.JDBC
1.JDBC:
全称:JAVA Database Connectivity :java 数据库连接。
理解:java拥有一套独立的数据库连接和操作API,任何第三方数据库厂商通过实现这套API来提供java程序连接数据库的支持,这套API就叫JDBC。
为什么会出现JDBC?
SUN公司提供的一种数据库访问规则、规范, 由于数据库种类较多,并且java语言使用比较广泛,sun公司就提供了一种规范,让其他的数据库提供商去实现底层的访问规则。 我们的java程序只要使用sun公司提供的jdbc驱动即可访问具体的数据库。
2.使用JDBC的基本步骤
1. 加载驱动
1 Class.forName("com.mysql.jdbc.Driver");
2. 建立连接
1 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/student", "root", "root");// 参数一: 协议 + 访问的数据库 , 参数二: 用户名 , 参数三: 密码。
3. 创建statement
1 Statement stmt = conn.createStatement();
4. 执行sql 查询 ,得到结果集
1 String sql = "select * from t_stu";
2 ResultSet rs = st.executeQuery(sql);
5. 遍历结果集
1 while(rs.next()){
2 int id = rs.getInt("id");
3 String name = rs.getString("name");
4 int age = rs.getInt("age");
5 System.out.println("id="+id + "===name="+name+"==age="+age);
6 }
6. 释放资源:先开后关
1 if (rs != null) {
2 rs.close();
3 }
4
5 if (stmt != null) {
6 stmt.close();
7 }
8
9 if (conn != null) {
10 conn.close();
11 }
3. 使用properties配置文件
1. 在src底下声明一个文件 jdbc.properties ,里面的内容如下:
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost/student
name=root
password=root
2. 在工具类里面,使用静态代码块,读取属性
1 //1. 创建一个属性配置对象
2 Properties properties = new Properties();
3 InputStream is = new FileInputStream("jdbc.properties"); //对应文件位于工程根目录
4
5 //2.载入
6 properties.load(is);
7
8 //3.读取属性
9 driverClass = properties.getProperty("driverClass");
10 url = properties.getProperty("url");
11 name = properties.getProperty("name");
12 password = properties.getProperty("password");
13
4.使用Statement执行SQL语句
有三种执行SQL的方法:代码省略注册、连接等步骤
1.executeQuery():用于查询.该方法返回ResultSet对象,可以直接遍历
1 String sql="selec*from stu";
2 ResultSet rs=stmt.executeQuery(sql);
2.execute():用于查询。该方法返回boolean类型,判断不为空再通过Statement对象创建ResultSet对象再遍历
1 //4.执行SQL,返回结果集
2 String sql="selec*from stu";
3 boolean flag=stmt.execute(sql);
4 //5.遍历结果集
5 if(flag==true) {
6 ResultSet rs=stmt.getResultSet();
7 while(rs.next()) {
8 //对不同的表有不同的操作
9 }
3.executeUpdate():用于插入、修改、删除
这个操作更改了数据库,重新执行execute或者executeQuery查询即可
5.PrepareStatement
推荐使用该对象替换前面的statement对象。原因是:
1. 它是Statement的子类,相比较statement, 提供预处理功能,对SQL语句预先检查语法, 在sql语句里面使用 ? 占位符来替代后续要传递进来的变量,然后再提供具体的参数执行,效率高
2.有效防止SQL注入
1 String sql = "insert into t_user values(null , ? , ?)";
2 PepareStatement ps= conn.prepareStatement(sql);//创建对象,预先检查
3
4 ps.setString(1, userName); //给占位符赋值 从左到右数过来,永远你是1开始。
5 ps.setString(2, password);
6
7 ps.executeUpdate();
总结就是:
1.
PrepareStatement创建语句是:PepareStatement ps= conn.prepareStatement(sql);
Statement创建语句是:Statement ps= conn.CreateStatement();
2.
execute、executequery、executeUpdate方法用法上:
PrepareStaement的用的时候不传入sql语句,因为已经预先检查过语法了,为无参方法
PrepareStaement的用的时候传入sql语句,为有参方法
3.
推荐使用PrepareStatement
9.数据库连接池
1. 概念:其实就是一个容器(集合),存放数据库连接的容器。
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
2. 好处:
1. 节约资源
2. 用户访问高效
3. 实现:
1. 标准接口:DataSource javax.sql包下的
1. 方法:
* 获取连接:getConnection()
* 归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接
2. 一般我们不去实现它,有数据库厂商来实现
开源连接池
DBCP
-
导入jar文件
-
不使用配置文件:
1 public void testDBCP01(){ 2 Connection conn = null; 3 PreparedStatement ps = null; 4 try { 5 //1. 构建数据源对象 6 BasicDataSource dataSource = new BasicDataSource(); 7 dataSource.setDriverClassName("com.mysql.jdbc.Driver"); 8 dataSource.setUrl("jdbc:mysql://localhost/bank"); 9 dataSource.setUsername("root"); 10 dataSource.setPassword("root"); 11 12 //2. 得到连接对象 13 conn = dataSource.getConnection(); 14 String sql = "insert into account values(null , ? , ?)"; 15 ps = conn.prepareStatement(sql); 16 ps.setString(1, "admin"); 17 ps.setInt(2, 1000); 18 ps.executeUpdate(); 19 } catch (SQLException e) { 20 e.printStackTrace(); 21 }finally { 22 JDBCUtil.release(conn, ps); 23 } 24 }
-
使用配置文件方式:
1 Connection conn = null; 2 PreparedStatement ps = null; 3 try { 4 BasicDataSourceFactory factory = new BasicDataSourceFactory(); 5 Properties properties = new Properties(); 6 InputStream is = new FileInputStream("src//dbcpconfig.properties"); 7 properties.load(is); 8 DataSource dataSource = factory.createDataSource(properties); 9 10 //2. 得到连接对象 11 conn = dataSource.getConnection(); 12 String sql = "insert into account values(null , ? , ?)"; 13 ps = conn.prepareStatement(sql); 14 ps.setString(1, "liangchaowei"); 15 ps.setInt(2, 100); 16 ps.executeUpdate(); 17 } catch (Exception e) { 18 e.printStackTrace(); 19 }finally { 20 JDBCUtil.release(conn, ps); 21 }
C3P0
不使用配置文件方式
1 Connection conn = null; 2 PreparedStatement ps = null; 3 try { 4 //1. 创建datasource 5 ComboPooledDataSource dataSource = new ComboPooledDataSource(); 6 //2. 设置连接数据的信息 7 dataSource.setDriverClass("com.mysql.jdbc.Driver"); 8 dataSource.setJdbcUrl("jdbc:mysql://localhost/bank"); 9 dataSource.setUser("root"); 10 dataSource.setPassword("root"); 11 12 //2. 得到连接对象 13 conn = dataSource.getConnection(); 14 String sql = "insert into account values(null , ? , ?)"; 15 ps = conn.prepareStatement(sql); 16 ps.setString(1, "admi234n"); 17 ps.setInt(2, 103200); 18 ps.executeUpdate(); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 }finally { 22 JDBCUtil.release(conn, ps); 23 }

浙公网安备 33010602011771号