MySQL数据库(三)
MySQL数据库(三)
事务
什么是事务
事务原则:ACID原则 原子性,一致性,隔离性,持久性 (脏读,幻读...)
参考博客:https://blog.csdn.net/dengjili/article/details/82468576
原子性
要么都成功,要么都失败
一致性
事务前后数据完整性要一直,1000
持久性
事务一旦提交,被持久化到数据库中
隔离性
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
隔离所导致的一些问题
脏读
指一个事务读取了另外一个事务未提交的数据
不可重复读
在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)
幻读
是指在一个事务内读取到了别的事务插入的数据,导致前后读取数量总量不一致。
执行事务
-- MySQL默认开启事务自动提交
SET autocommit = 0 -- 关闭
SET autocommit = 1 -- 默认开启
-- 手动处理事务
SET autocommit = 0 -- 关闭自动提交
-- 事务开启
START TRANSACTION -- 标记一个事务的开始,从此之后sql都在同一个事务内
INSERT xx
INSERT xx
-- 提交:持久化(成功)
COMMIT
-- 回滚:回到原来的样子(失败)
ROLLBACK
-- 事务结束
SET autocommit = 1 -- 开启自动提交
SAVEPOINT 保存点名 -- 设置一个事务的保存点
ROLLBACK TO SAVEPOINT 保存点名 -- 回滚到保存点
RELEASE SAVEPOINT 保存点名 -- 撤销保存点
模拟事务
-- 模拟事务:转账
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'; -- A多500
COMMIT; -- 提交事务
ROLLBACK; -- 回滚
SET autocommit = 1; -- 恢复默认值
索引
索引是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构
索引的分类
在一个表中,主键索引只能有一个,唯一索引可以有多个
- 主键索引 (PRIMARY KEY)
-
- 唯一的标识,主键不可重复,只能有一个主键
- 唯一索引 (UNIQUE KEY)
-
- 避免重复的列出现,可以重复,多个列都可以标识为唯一索引
- 常规索引 (KEY/INDEX)
-
- 默认的,index,key关键字设置
- 全文索引 (FullText)
-
- 在特定的数据库引擎中才有
-
- 快速定位数据
基础语法
-- 索引的使用
-- 1.在创建表的时候给字段增加索引
-- 2.创建完毕后,增加索引
-- 显示所有的索引信息
SHOW INDEX FROM `student`
-- 增加一个索引
ALTER TABLE `student` ADD FULLTEXT INDEX `studentname`(`studentname`)
-- 分析sql执行状况
EXPLAIN SELECT * FROM `student` -- 非全文索引
EXPLAIN SELECT * FROM `student` WHERE MATCH(`studentname`) AGAINST('刘')
测试索引
-- 插入100万数据.
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`)VALUES(CONCAT('用户',i),'19224305@qq.com','123456789',FLOOR(RAND()*2));
SET i=i+1;
END WHILE;
RETURN i;
END;
DROP FUNCTION mock_data
SELECT mock_data(); -- 执行此函数 生成一百万条数据
100万条数据测试索引
-- 创建索引
-- CREATE 索引名 on 表(字段)
CREATE INDEX id_app_user_name ON `app_user`(`name`)
SELECT * FROM `app_user` WHERE `name` = '用户9999'
索引原则
- 索引不是越多越好
- 不要对进程变动数据加索引
- 小数据量的表不需要加所以
- 索引一般加载常用来查询的字段上
参考博客:http://blog.codinglabs.org/articles/theory-of-mysql-index.html
用户管理
用户管理
SQLyog可视化管理
点击上方小人进行管理,方便快捷
SQL命令操作
-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码'
CREATE USER kuangshen IDENTIFIED BY '123456'
-- 修改密码(当前用户)
SET PASSWORD = PASSWORD('123456')
-- 修改密码(指定用户)
SET PASSWORD FOR kuangshen = PASSWORD('111')
-- 重命名
RENAME USER kuangshen TO logan
-- 用户授权 ALL PRIVILEGES 全部的权限 ON 库.表 TO 用户
GRANT ALL PRIVILEGES ON *.* TO logan
MySQL备份
MySQL数据库备份方式
- 直接拷贝物理文件
- 在SQLyog可视化工具中到处
-
- 在想要导出的表或库中,右键选择备份或导出
- 命令行导出,使用命令mysqldump
# mysqldump -h主机 -u用户名 -p密码 数据库 表明 > 位置/文件名
mysqldump -hlocalhost -uroot -p123456 school student > D:/a.sql
规范数据库设计
为什么需要设计数据库
糟糕的数据库设计:
- 数据冗余,浪费空间
- 数据库插入和删除都会麻烦,异常
- 程序的性能差
良好的数据库设计: - 节省内存空间
- 保证数据库的完整性
- 方便开发系统
软件开发中,关于数据库的设计: - 分析需求:分析业务和需要处理的数据库需求
- 概要设计L设计关系图E-R图
设计数据库的步骤:(个人博客)
- 收集信息,分析需求
-
- 用户表(登录,注销,个人信息,博客,创建分类)
-
- 分类表(文章分类,谁创建的)
-
- 文章表(文章的信息)
-
- 评论表()
-
- 友链表(友链信息)
-
- 自定义表(系统信息,某个关键的字,或者一些主字段)
- 标识实体(把需求落地到每个字段)
- 标志实体之间的关系
-
- 写博客:user-->blog
-
- 创建分类:user-->catagory
-
- 关注:user-->user
-
- 友链:links
-
- 评论:user-user-blog
三大范式
为什么需要数据规范化?
- 信息重复
- 更新异常
- 插入异常
-
- 无法正常显示信息
- 删除异常
-
- 丢失有效信息
三大范式
第一范式
原子性:要求数据库表的每一列都是不可分割的原子数据项
第二范式
前提:满足第一范式
第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。
每张表只描述一件事情
第三范式
前提:满足第一第二范式
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
规范数据库的设计
规范性和性能
关联查询的表不得超过三张
- 考虑商业化的需求和目标,(成本,用户体验)数据库的性能更加重要
- 在规范性能问题时,需要适当的考虑规范性
- 故意给某些表增加一些冗余字段。
- 故意计算一些计算列(从大数据量降低为小数据量的查询:索引)
JDBC(重点)
第一个JDBC程序
下载数据库驱动jar包,并导入到lib文件夹中
编写测试代码
//我的第一个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
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String password = "123456";
//3.连接成功,返回数据库对象 Connection 代表数据库
Connection connection = DriverManager.getConnection(url,username,password);
//4.执行SQL的对象 Statement 执行sql的对象
Statement statement = connection.createStatement();
//5.执行SQL对象去执行SQL语句
String sql = "SELECT * FROM `users`";
ResultSet resultSet = statement.executeQuery(sql);//返回的结果集,结果集中封装了所有查询出的结果
while(resultSet.next()) {
System.out.println("id="+resultSet.getObject("id"));
System.out.println("name="+resultSet.getObject("NAME"));
System.out.println("password="+resultSet.getObject("PASSWORD"));
System.out.println("===============================");
}
//6.释放连接
resultSet.close();
statement.close();
connection.close();
}
}
步骤总结:
1.加载驱动
2.连接数据库DriverMannager
3.获取执行sql的对象 Statement
4.获得返回的结果集
5.释放连接
statement对象
jdbc中statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可
代码实现
1.提取工具类:
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static {
try {
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(in);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
//驱动只用加载一次
Class.forName(driver);
} catch (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();
}
}
}
}
"db.properties"文件内容
driver = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username = root
password = 123456
2.编写增删改方法
增:
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` VALUES" +
"(4,'logan','123456','logan@qq.com','1989-1-5')";
int i = statement.executeUpdate(sql);
if(i>0) System.out.println("插入成功!"+i);
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(connection,statement,resultSet);
}
}
}
删:
public class TestDelete {
public static void main(String[] args) {
ResultSet resultSet = null;
Statement statement = null;
Connection connection = null;
try {
connection = JdbcUtils.getConnection();
statement = connection.createStatement();
String sql = "DELETE FROM `users` WHERE id = 4";
int i = statement.executeUpdate(sql);
if(i>0) System.out.println("删除成功");
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(connection,statement,resultSet);
}
}
}
改:
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` = 'logan' WHERE id = 4";
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();
String sql = "SELECT `id`,`NAME`,`PASSWORD`" +
"FROM `users`" +
"WHERE id IN (1,3,4)";
resultSet = statement.executeQuery(sql);
while(resultSet.next()) {
System.out.println("id="+resultSet.getInt("id"));
System.out.println("name="+resultSet.getString("NAME"));
System.out.println("password="+resultSet.getString("PASSWORD"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(connection,statement,resultSet);
}
}
}
PreparedStatement对象
SQL注入问题
使用PreparedStatement对象可以防止SQL注入问题
增
public class TestInsert {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
//区别
String sql = "insert into `users` values(?,?,?,?,?)";
statement = connection.prepareStatement(sql);
//手动给参数赋值
statement.setInt(1,5); // 给id赋值
statement.setString(2, "qinjiang"); // 给name赋值
statement.setString(3, "123456"); // 给password赋值
statement.setString(4, "qinjiang@qq.com"); // 给email赋值
// 注意点 sql.Date 数据库
// util.Date Java
statement.setDate(5, new java.sql.Date(new Date().getTime())); // 给time赋值
//执行
int i = statement.executeUpdate();
if(i>0) System.out.println("插入成功");
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(connection,statement,resultSet);
}
}
}
删
public class TestDelete {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
String sql = "delete from `users` where id = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1,5);
int i = statement.executeUpdate();
if(i>0) System.out.println("删除成功");
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(connection,statement,resultSet);
}
}
}
改
public class TestUpdate {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
String sql = "update `users` set `NAME` = ? where id = ?";
statement = connection.prepareStatement(sql);
statement.setString(1,"秦将");
statement.setInt(2,5);
int i = statement.executeUpdate();
if(i>0) System.out.println("更改成功");
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(connection,statement,resultSet);
}
}
}
查
public class TestSelect {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
String sql = "SELECT `id`,`NAME`,`password` from `users` where id = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1,4);
resultSet = statement.executeQuery();
while(resultSet.next()) {
System.out.println(resultSet.getInt("id"));
System.out.println(resultSet.getString("name"));
System.out.println(resultSet.getString("password"));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
JDBC操作事务
public class TestTransaction1 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
//关闭事务自动提交,开启事务
connection.setAutoCommit(false);
String sql1 = "update `account` set `money` = `money`-1000 where `name` = 'A';";
String sql2 = "update `account` set `money` = `money`-1000 where `name` = 'B';";
statement = connection.prepareStatement(sql1);
statement.executeUpdate();
int a = 1/0;
statement = connection.prepareStatement(sql2);
statement.executeUpdate();
//提交事务
connection.commit();
System.out.println("转账成功");
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
JdbcUtils.release(connection,statement,resultSet);
}
}
}
数据库连接池
数据库链接--执行完毕--释放--链接--释放 十分浪费资源
池化技术:预先准备一些资源,过来就连接预先准备好的
最小连接数:10
最大连接数:15
等待超时:100ms
编写连接池,实现一个接口DataSource
开源数据源实现
DBCP
C3P0
Druid
使用了这些数据库连接池之后,在项目开发中就不需要编写链接数据库的代码了!
DBCP
需要用到的jar包
commons-dbcp-1.4 commons-pool-1.6