1.数据库介绍

数据库是安装在操作系统,可以系统性的存储大量数据的软件.

作用:存储数据,管理数据

2.数据库的分类

  • SQL 关系型数据库

    • Mysql ,Ocacle,Sql Server,DB2,SQLite
    • 通过表和表,列和列的关系进行数据的存储
  • NOSQL 非关系型数据库

    • Redis,MongDB
    • 非关系型数据库,对象存储,类型JSON或者字典方式存储数据

3.命令行初见Mysql

1.登录

1
2
mysql -uroot -p123456
//-u用户名 -p密码

命令都是以;结尾的

2.查看数据库

1
show databases;

3.切换数据库

1
use xxx;

4.查看数据库中的所有表

1
show tables;

5.显示数据库中所有表的消息

1
describe student;

6.退出

1
exit;

4.操作数据库

小提示,如果你的表名或者字段是一个特殊字符需要用``包起来

数据库->数据表>数据

1.创建数据库

1
2
CREATE DATABASE hello;
CREATE DATABASE IF NOT EXISTS hello;

2.删除数据库

1
DROP DATABASE IF EXISTS hello;

5.数据库的列类型

数值

名称 存储数据的大小 占用的字节大小
tinyint 十分小的数据 1
smallint 较小的数据 2
mediumint 中等大小的数据 3
int 标准的整数(常用) 4
bigint 较大的数据 8
float 浮点数 4
double 浮点数(精度问题) 8
decimal 字符串形式的浮点数(金融) 9

字符串

名称 特点 长度范围
char 字符串固定大小的 0-255
varchar 可变字符串(常用) 0-65535
tinytext 微型文本 2^8-1
text 文本串 2^16-1

时间日期

名称 格式
date YYYY-MM-DD
time HH:MM:SS
datetime YYYY-MM-DD HH:MM:SS(常用)
timestamp 时间戳(1970.1.1到现在的毫秒数 常用)

null

  • 没有值,未知
  • ==注意,不要使用NULL进行运算

6.数据库的字段属性

Unsigned:

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

zerofill:

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

自增:

*  自动在是一条记录的基础上+1
*  通常来设置唯一的主键 index,必须是整数类型
*  可以自定义设置主键初始值和步长

非空 NULL not null:

  • 如果不给他赋值,就会报错

默认:

  • 设置默认的值

规范

每个表都必须存在以下五个字段

1
2
3
4
5
id 主键
`version` 乐观锁
is_delete 伪删除
gmt_create 创建时间
gmt_update 修改时间

7.创建数据表

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE IF NOT EXISTS `student`(
`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
`name` VARCHAR(20) NOT NULL DEFAULT '匿名' COMMENT '姓名',
`pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
`birthday` DATETIME DEFAULT NULL COMMENT '出事日期',
PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

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

查看创建数据库的语句

1
SHOW CREATE DATABASE xxx;

查看创建数据表的定义语句

1
2

SHOW CREATE TABLE xxx;

显示表的结构

1
DESC xxx;

8.数据库引擎的区别

  • INNODB 默认使用
  • MYISAM 早些年使用的
特性 MYISAM INNODB
事务支持 不支持 支持
数据行锁定 不支持 支持
外键约束 不支持 支持
全文索引 支持 不支持
表空间的大小 较小 较大,为两倍

常规使用操作:

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

在物理空间存在的位置

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

本质还是文件存储

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

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

设置数据库的字符集

1
CHARSET=utf8

9.修改和删除表

1.修改表名

1
ALTER TABLE student RENAME lorin

2.增加表的字段

1
ALTER TABLE student ADD age INT(11)

3.修改表的字段

  • 重命名
1
ALTER TABLE student CHANGE age age1 int(1)
  • 修改约束
1
ALTER TABLE student MODIFY age VARVHAR(11)

4.删除表的字段

1
ALTER TABLE student DROP age

5.删除表

1
DROP TABLE IF EXISTS student

注意:

  • 字段名为了安全使用``包裹起来
  • 注释 –
  • sql关键字大小写不敏感,建议大家写小写

10.MySQL数据库管理

1.外键(了解即可)

保证数据库的一致性,但是设置数据库层的外键会导致逻辑混乱.建议在应用层解决.

1
2
3
4
-- 定义外键key
KEY `FK_gradeid` (`gradeid`)
-- 给这个外键添加约束(执行引用) 到哪个表的那个键
CONSTRAINT `FK_gradeid` FOREIGN KEY ( `gradeid` ) REFERENCES `grade` ( `gradeid` )
1
2
-- 创建好表折后添加
ALTER TABLE `student` ADD CONSTRAINT `FK_gradeid` FOREIGN KEY(`gradeid`) REFERENCES `grade`(`gradeid`)

2.DML语言(全部记住)

1.添加

1
2
3
4
5
-- INSERT INTO 表名([字段1,字段2]) VALUES('值1','值2')
-- 插入一条
INSERT INTO `grade` (`gradename`) VALUES('大四')
-- 插入多条
INSERT INTO `grade` (`gradename`) VALUES('大一'),('大三')

2.更新

1
2
3
4
5
6
7
-- UPDATE `表名` SET `字段`='codelorin' WHERE 字段=1;
UPDATE `grade` SET `gradename`='codelorin' WHERE gradeid=1;
-- UPDATE `表名` SET `字段`='codelorin'
-- 这样会修改所有的值

-- 修改多个
UPDATE `grade` SET `gradename`='codelorin',xxx=xxx WHERE gradeid=1;

where子句

操作符 含义 范围 结果
= 等于 5=6 false
<>或者!= 不低于 5<>6 true
> 大于
< 小于
<= 小于等于
BETWEEN…AND… 在某个范围内 [2,5]闭区间
AND && 5>1and 1>2 false
OR ||

注意:

  • 数据库的列尽量带上``
  • 条件没有指定会修改所有的列
  • value是一个具体的值也可以是变量(一般用于时间)
1
2
UPDATE `grade` SET `birthday`=CURRENT_TIME WHERE gradeid=1;
-- CURRENT_TIME当前时间

3.删除

1
2
3

--
DELETE FROM grade WHERE gradeid =1
1
2
3
4
5
-- 删除所有数据
TRUNCATE `grade`
TRUNCATE `表名`
-- 删除所有数据
DELETE FROM grade

DELETE和TRUNCATE的区别

  • 相同点:都能删除数据,都不会删除表结构
  • 不同点:
    • TRUNCATE重新设置自增列 计数器会归零
    • TRUNCATE 不会影响事务

3.DQL查询数据(最重点)

Data Query LANGUAGE: 数据库查询语言

  • 最重要,频率最高的语句

完整语法

1
2
3
4
5
6
7
select xxx from xxx
join
where
group by 分组
having 过滤
order by 排序
limit 限制

1.全部

1
SELECT * FROM `subject`

2.制定字段

1
SELECT * FROM `subject` WHERE `gradeid`=1

3. 别名

1
2
-- AS起别名
SELECT `studentno` AS 学号,`loginpwd` AS 密码 FROM student

4.函数

Concat 连接字符串

1
SELECT CONCAT('姓名:',studentname) AS 新名字 FROM student

系统版本

1
SELECT VERSION()

5.变量

自增

1
SELECT @@auto_increment_increment FROM `grade` 

6.去重

distinct

1
SELECT DISTINCT `studentno` FROM result

7.where条件子句

逻辑运算符

  • and &&
  • or ||
  • not !

8.模糊查询

重点

运算符 语法 描述
IS NULL a is null 如果操作符为NULL,结果为真
IS NOT NULL a is not null 如果操作符不为NULL,结果为真
BETWEEN a between b and c 若a在b和c之间,则结果为真
LIKE a like b a相似匹配b
IN a in(a1,a2) 假设a在a1或a2其中的某一个,结果为真

like结合 %(匹配0到任意一个字符) _(一个字符)

9.联表查询

JOIN

on和where的区别

on 是关联条件,where 则是查询条件

image-20210429234331783

  • inner join

    1
    SELECT s.studentno,`studentname`,`subjectno`,`studentresult` FROM student as s INNER JOIN result as r WHERE s.studentno=r.studentno
  • left join

    1
    SELECT s.studentno,`studentname`,`subjectno`,`studentresult` FROM student as s left JOIN result as r ON s.studentno=r.studentno
  • right join

    1
    SELECT s.studentno,`studentname`,`subjectno`,`studentresult` FROM student as s RIGHT JOIN result as r ON s.studentno=r.studentno
操作 描述
inner join 两个表的数据都可以拿到
left join 会从左表返回所有的值,即使右表中没有匹配
right join 会从右表中返回所有的值,即使左表没有匹配

没有匹配实践

1
SELECT s.studentno,`studentname`,`subjectno`,`studentresult` FROM student as s RIGHT JOIN result as r ON s.studentno=r.studentno where studentresult is NULL

10.自连接

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

父类

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

子类

pid categoryid categoryname
3 4 数据库
2 8 办公信息
3 6 web开发
5 7 pas技术
1
SELECT a.`categoryname` as 'father',b.`categoryname` as 'son' FROM `category` as a,`category` as b WHERE a.`categoryid`=b.`pid`

11.分页和排序

order by ASC升序 DESC降序

1
SELECT * from result ORDER BY studentresult DESC

分页 limit(下标,要几个)

规律: (n-1)*page size,page size

1
2
SELECT * from result
LIMIT 0,2

12.子查询

where这个值是计算出来的

1
2
3
select xx from xx where xx=(
select xx form xx where
)

13.函数

绝对值

​ ABS()

向上取整

​ CEILING()

向下取整

​ FLOOR()

0-1的随机数

​ RAND()

判断一个数的符号 负数返回-1正数返回1

​ SIGN()

字符串长度

​ CHAR_LENGTH()

字符串拼接

​ CONCAT()

当前时间

CURRENT_DATE()

当前日期

​ CURRENT()

当前时间

​ NOW()

本地时间

​ LOCALTIME()

14.聚合函数

函数名称 描述
COUNT() 计数
SUM() 求和
AVG() 平均值
MAX() 最大值
MIN() 最小值

count()

1
2
3
count(xx) 会忽略null
count(*) 不会忽略null
count(1) 不会忽略null

分组过滤 group by

having

1
SELECT studentno,SUM(studentresult) 总成绩 FROM `result` GROUP BY studentno HAVING studentno!=1002

15.数据库级别的md5加密

md5(xxx)

总结

select 去重 字段 from 表

xx join 连接表 on 等值判断

where

group by 分组

having 过滤

order by 排序

limit 限制

11.事务

要不都成功,要不都失败

将一组sql放入一个批次中去执行

事务原则: ACID原则 脏读,幻读

  • 原子性
    • 要不都成功,要不都失败
    A给B转账,B收到A转的钱,要不都成功,要不都失败
  • 一致性
    • 事务前后的数据完整性要保持一致
    A有800元,B有200元无论怎么转,总价是1000
  • 持久性
    • 事务结束后数据不随着外部原因导致数据丢失
    事务没有提交就恢复到原状,提交了就持久化到数据库
  • 隔离性
    • 多个用户访问数据库时,为每个用户开启事务,事务间互相隔离

隔离的问题

脏读:

指一个事务读取了另外一个事务没有提交的数据

不可重复读:

一个事务多次读取结果不同,不一定是错误可能是场合不同

虚读(幻读):

一个事物读取别的事务插入的数据,导致前后读取不一致

事务在sql层面的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
set autocommit = -- 0 关闭事务
set autocommit = -- 1 开启事务(默认的)

start transaction -- 标记一个事务的开启,一下执行的所有sql都在一个事务内

insert xx

-- 提交 持久化(成功)
commit
-- 回滚 回到原来的状态(失败)
rollback
-- 事务结束
set autocommit = 1 -- 自动提交

-- 了解
savepoint -- 设置一个事务的保存点
rollback to savepoint -- 回滚到保存点
release savepoint -- 撤销保存点

12.索引

索引是帮助mysql实现高效获取数据的数据结构

1.索引的分类

  • 主键索引(PRIMARY KEY)
    • 唯一的标识,不可重复
  • 唯一索引(UNIQUE KEY)
    • 避免重复的列出现,唯一索引可以重复,多个列标识位
  • 常规索引(KEY/INDEX)
    • 默认的,index key关键字来设定
  • 全文索引(FullText)
    • 快速定位数据

用法:

索引名(列名)

1
alter table xxx add fulltext index `studentName` (`studentName`)

2.分析sql执行状况

1
2
explain xxxsql语句
explain select * from xx where match(studentName) against('liu')

插入100万条数据测试

1
2
3
4
5
6
7
8
9
10
11
12
CREATE TABLE `app_user` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) DEFAULT '' COMMENT '用户昵称',
`email` VARCHAR(50) NOT NULL COMMENT '用户邮箱',
`phone` VARCHAR(20) DEFAULT '' COMMENT '手机号',
`gender` TINYINT(4) UNSIGNED DEFAULT 0 COMMENT '性别(0:男;1:女)',
`password` VARCHAR(100) NOT NULL COMMENT '密码',
`age` TINYINT(4) DEFAULT 0 COMMENT '年龄',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT = 'app用户表'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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),'19224305@qq.com',123456789,FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100));
SET i=i+1;
END WHILE;
RETURN i;
END;

SELECT mock_data() -- 执行此函数 生成一百万条数据
1
2
3
4
-- 创建索引查看时间差别,数据结构yyds
SELECT * FROM app_user WHERE `name`='用户999999'
CREATE INDEX id_app_user_name ON app_user(`name`)

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

索引的数据结构

Btree: InnoDB默认的数据类型

13.权限管理

1.用户管理

本质是修改mysql下的user表

  • 创建
1
CREATE USER lorin IDENTIFIED BY '123456'
  • 修改当前用户
1
set password = password('123456')
  • 修改制定用户
1
set password for lorin=password('123456')
  • 修改用户名
1
rename user xxx to xxxx
  • 授予用户全部权限
1
GRANT ALL PRIVILEGES ON *.* TO xxx
  • 查询权限
1
SHOW GRANTS FOR root @localhost
  • 撤销权限
1
REMOVE ALL PRIVILEGES ON *.* FROM xxx

14.数据库的备份

保证重要的数据不丢失

数据转移

数据库命令行备份

1
mysqldump -hlocalhost -uroot -p123456 数据库名称 表 > D:/1.sql

数据库加载

1
2
source d:/1.sql
mysql -uroot -p123456 库名<备份文件

15.数据库的设计

当数据库比较复杂的时候

设计数据库步骤:

  • 收集信息,分析需求
    • 用户表(用户登录注销,用户个人信息,写博客,创建分类)
    • 分类表(文章分类,谁创建的)
    • 文章表(文章的信息)
    • 友链表(友链信息)
  • 标识实体(把需求落实到每个字段)
  • 标识实体之间的关系
    • 写博客:user->blog
    • 创建分类: user->category
    • 关注:user->user
    • 友链: links
    • 评论: user->user->blog

16.三大范式

数据规范化

1.第一范式

原子性:要求每一列都是不可分割的最小原子数据项

2.第二范式

前提满足第一范式

需要保证数据库的每一列都和主键相关,而不能只和主键的某一部分相关

每张表只做一件事

3.第三范式

满足第一,二范式

每一列数据都和主键直接相关而不能间接相关

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

  • 数据库性能在某些情况更加重要
  • 规范性
  • 故意给表增加一些冗余的字段(多表->单表)
  • 故意增加一些计算列(索引)

17.JDBC(重点)

数据库驱动

image-20210430135328117

1.导入数据库驱动

新建lib目录

image-20210430163719364

可以展开即是导入成功

2.代码demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.codeloirn.jdbc;

import java.sql.*;

/**
* @author CodeLorin
* @date 2021/1/30 16:40
*/
public class Demo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动(固定写法)
Class.forName("com.mysql.jdbc.Driver");

//2.用户信息和url
String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
//3.连接成功,数据库对象
String username = "root";
String password = "root";
Connection connection = DriverManager.getConnection(url, username, password);
//4.执行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("email=" + resultSet.getObject("email"));
System.out.println("birthday=" + resultSet.getObject("birthday"));
}
//6.释放连接
resultSet.close();
statement.close();
connection.close();
}
}

connection

代表数据库

1
2
3
connection.setAutoCommit();//设置自动提交
connection.commit();//事务提交
connection.rollback();//事务回滚

statement

执行sql

1
2
3
statement.executeQuery();//查询操作返回ResultSet
statement.execute();//执行任何sql
statement.executeUpdate();//更新插入删除,返回收到影响的行数

ResultSet

1
2
3
4
resultset.getObjest();//不知道类型

resultset.getString();//知道类型
resultset.getDate();

close 释放连接

1
2
3
resultSet.close();
statement.close();
connection.close();

3.CURD

1.create

1
2
3
4
5
6
Statement statement = connection.createStatement();
String sql = "insert into user() values()";
int num = statement.executeUpdate(sql);
if(num>0){
System.out.println("success!")
}

2.update

1
2
3
4
5
6
Statement statement = connection.createStatement();
String sql = "update user set name ='' where name =''";
int num = statement.executeUpdate(sql);
if(num>0){
System.out.println("success!")
}

3.read

1
2
3
4
5
6
Statement statement = connection.createStatement();
String sql = "SELECT * FROM users";
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {

}

4.delete

1
2
3
4
5
6
Statement statement = connection.createStatement();
String sql = "delete from user where id=1";
int num = statement.executeUpdate(sql);
if(num>0){
System.out.println("success!")
}

4.提取工具类

properties

1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=root

Utils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66


package com.codeloirn.jdbc2.utils;

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


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

static {
try {
InputStream inputStream = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(inputStream);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");

Class.forName(driver);


} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}

//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}

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

}
}

main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

package com.codeloirn.jdbc2;

import com.codeloirn.jdbc2.utils.JdbcUtils;

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

public class JdbcStudy {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
String sql = "INSERT users VALUES(7,'codelorin',123,'admin@l.cn','1980-12-04')";

int i = st.executeUpdate(sql);
if (i > 0) {
System.out.println("success!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(conn, st, null);

}
}
}

5.sql注入

安全问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.codeloirn.jdbc2;

import com.codeloirn.jdbc2.utils.JdbcUtils;

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

public class JdbcStudy {
public static void main(String[] args) {
String username = "'or' 1=1"; //' or ' 1=1 '-- '
String password = "'or' 1=1";
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
String sql = "SELECT * FROM `users` WHERE `name`='" + username + "' and `password` ='" + password + "'";
ResultSet resultSet = st.executeQuery(sql);
while (resultSet.next()) {
System.out.println(resultSet.getObject("name"));
System.out.println(resultSet.getObject("password"));
}

} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(conn, st, null);

}
}
}

由于sql的逻辑运算符的原因,存在sql注入漏洞,会被攻击导致数据泄露.

6.解决sql注入问题

增删改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.codeloirn.jdbc3;

import com.codeloirn.jdbc2.utils.JdbcUtils;

import java.sql.*;
import java.util.Date;


public class Test {
public static void main(String[] args) {
Connection conn = null;
//st
PreparedStatement st = null;

try {
conn = JdbcUtils.getConnection();
//预编译
String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`) VALUES(?,?,?,?,?)";
st = conn.prepareStatement(sql);
st.setInt(1, 10);
st.setString(2, "lorin");
st.setString(3, "123321");
st.setString(4, "codelorin@163.com");
st.setDate(5, new java.sql.Date(new Date().getTime()));

int i = st.executeUpdate();
if (i > 0) {
System.out.println("success!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(conn, st, null);
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.codeloirn.jdbc2;

import com.codeloirn.jdbc2.utils.JdbcUtils;

import java.sql.*;

public class JdbcStudy {
public static void main(String[] args) {
String username = "codelorin";
String password = "123";
Connection conn = null;
//st
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
//预编译
String sql = "SELECT * FROM `users` WHERE `name`=? and `password`=?";
st = conn.prepareStatement(sql);

st.setString(1, username);
st.setString(2, password);
rs = st.executeQuery();
while (rs.next()) {
System.out.println(rs.getObject("name"));
System.out.println(rs.getObject("password"));
}

} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtils.release(conn, st, null);

}
}
}

18.事务问题

ACID原则

原子性:要不都完成,要不都失败

一致性:总数不变

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

持久性:一旦提交不可逆,持久化到数据库了

隔离性的问题

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.codeloirn;

import com.codeloirn.jdbc2.utils.JdbcUtils;

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

/**
* @author CodeLorin
* @date 2021/4/30 23:56
*/
public class TransactionDemo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;

try {
conn = JdbcUtils.getConnection();
//关闭自动提交,开启事务
conn.setAutoCommit(false);
String sql1 = "update account set money = money-100 where name ='A'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
String sql2 = "update account set money = money+100 where name ='B'";
st = conn.prepareStatement(sql2);
st.executeUpdate();

//业务完成,提交事务
conn.commit();
System.out.println("success");

} catch (SQLException e) {
try {
//失败回滚
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
JdbcUtils.release(conn, st, null);
}
}
}

19.数据库连接池

数据库连接完毕,执行完毕,释放

连接释放十分浪费资源

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

最小连接数:10

最大连接数:15

等待超时:100ms

编写连接池,实现DataSource接口

DBCP

C3P0

Druid:阿里巴巴

使用这些数据库连接池,我们就不需要编写连接数据库的代码了!

DBCP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=root

#!-- 初始化连接 --
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

Dbcp工具类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package com.codeloirn.dbcp.utils;


import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;

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

/**
* @author CodeLorin
* @date 2021/4/30 17:41
*/
public class JdbcUtilsDbcp {


private static DataSource dataSource = null;

static {
try {
InputStream inputStream = JdbcUtilsDbcp.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 conn, Statement st, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

}
}

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.codeloirn.dbcp;

import com.codeloirn.dbcp.utils.JdbcUtilsDbcp;
import com.codeloirn.jdbc2.utils.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;

/**
* @author CodeLorin
* @date 2021/4/30 20:05
*/
public class Test {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;

try {
conn = JdbcUtilsDbcp.getConnection();
String sql = "INSERT INTO users(id,`name`,`password`,`email`,`birthday`) VALUES(?,?,?,?,?)";
st = conn.prepareStatement(sql);
st.setInt(1, 12);
st.setString(2, "lorin111");
st.setString(3, "123321");
st.setString(4, "codelorin@163.com");
st.setDate(5, new java.sql.Date(new Date().getTime()));

int i = st.executeUpdate();
if (i > 0) {
System.out.println("success!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JdbcUtilsDbcp.release(conn, st, null);
}
}
}