数据库

数据库

什么是数据库

概念:按照数据结构来组织、存储和管理数据的仓库

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

数据库分类

关系型数据库(SQL)

  • MySQL,Oracle,Sql Server,DB2,SQLlite
  • 通过表和表之间,行和列之间的关系进行数据的存储

非关系型数据库(NoSQL)

  • Redis,MongDB
  • 对象存储,通过自身的属性来决定

DBMS(数据库管理系统)

  • 数据库的管理软件,科学有效地管理我们的数据,维护和获取数据

MySQL

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

SQL语句

mysql -u root -p123456 --命令行连接数据库

update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost'; --修改用户密码

flush privileges; --刷新权限

show databases; --查看所有的数据库

use 数据库名 --切换数据库

show tables; --查看数据库中所有的表

describe student; --显示数据库中所有的表的信息

create database westos --创建一个数据库

exit; --退出连接

操作数据库

1.创建数据库

CREATE DATABASE [IF NOT EXISTS] westos;

2.删除数据库

DROP DATABASE [IF EXISTS] westos;

3.使用数据库

USE `school`

4.查看数据库

SHOW DATABASES

数据库的列类型

数值

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

字符串

  • char 字符串固定大小的 0-255
  • varchar 可变字符串 0-65535 常用的 String
  • tinytext 微型文本 2^8-1
  • text 文本串 2^16-1 保存大文本

时间日期

java.util.Date

  • data YYYY-MM-DD 日期格式
  • time HH:mm:ss 时间格式
  • datetime YYYY-MM-DD HH:mm:ss 最常用的时间格式
  • timestamp 时间戳:1970.1.1到现在毫秒数
  • year 年份表示

null

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

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

Unsigned:

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

zerofull:

  • 0填充的
  • 不足的位数,使用0来填充

自增:

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

非空:

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

默认:

  • 设置默认的值
  • 例:sex,默认值为男,如果不指定该列的值,则会有默认的值

创建数据库表(重点)

格式:

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

数据表的类型

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

常规使用操作:

  • MYISAM:节约空间,速度较快
  • INNODB:安全性高,事务的处理,多表多用户操作
/*
在物理空间存在的位置
*/

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

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

修改和删除数据表字段

修改表

ALTER TABLE 旧表名 RENAME AS 新表名

增加表的字段

ALTER TABLE 表名 ADD 字段名 列属性

修改表的字段

ALTER TABLE 表名 MODIFY 字段名 列属性[] --修改约束
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列属性[] --字段重命名

删除表的字段

ALTER TABLE 表名 DROP 字段名

MySQL数据管理

外键(了解即可)

ALTER TABLE 表 ADD CONSTRAINT 约束名 FOREIRN KEY(作为外键的列) REFERENCEES 表名(字段名) --为创建好的表添加外键

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

最佳实践:利用程序去实现

DML语言(全部记住)

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

DML语言:数据操作语言

添加(INSERT)

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

注意事项:

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

修改(UPDATE)

update 表名 set colnum_name = value,[colnum_name = value...] where [条件]

条件:where子句 运算符 id等于某个值,大于某个值,在某个区间内修改

操作符会返回布尔值

操作符 含义 范围 结果
= 等于 5=6 false
<> 不等于 5<>6 true
> 大于 5>4 true
< 小于 5<6 false
<= 小等于 5<=3 false
>= 大等于 5>=3 true
BETWEEN...AND... 在某个范围内 [2,5]
AND 相当于&& 5>1 and 1>2 false
OR 相当于|| 5>1 or 1>2 true

注意事项:

  1. colnum_name是数据库的列,最好带上``
  2. 筛选的条件,如果没有指定,则会修改所有的列
  3. value,是一个具体的值,也可以是一个变量

删除(DELETE)

delete from 表名 [where 条件]

TRUNCATE

truncate `表名`

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

和 DELETE的相同点:都能删除数据,都不会删除表结构

不同点:

  1. TRUNCATE 重新设置 自增列 计数器会归零
  2. TRUNCATE 不会影响事务

DQL查询数据

DQL(Data Query Language) --数据查询语言

  • 所有的查询操作都用它 select
  • 简单的查询,复杂的查询它都能做
  • 数据库中最核心的语言,最重要的语句
  • 使用频率最高的语句
SELECT [ALL | DISTINCT]
{* | table.* | [table.filed1[as alist1][,table.field2][as alist2]][,...]]}
FROM table_name [as table_alias]
	[left | right | inner join table_name2] --联合查询
	[where...] --指定结果需满足的条件
	[group by...] --指定结果按照哪几个字段来分组
	[having] --过滤分组的记录必须满足的次要条件
	[order by...] --指定查询记录按一个或多个条件排序
	[limit {[offset,]row_count | row_countOFFSET offset}]; --指定查询的记录从哪条到哪条

指定查询字段

查询全部数据
SELECT 字段 FROM 表

查询指定字段
SELECT `StudentNo`,`StudentName` FROM student

AS 可以给字段起别名,也可以给表起别名
SELECT `StudentNo` AS 学号,`StudentName` AS 学生姓名 FROM student AS s

函数 Concat(a,b)
SELECT CONCAT(姓名:',StudentName) AS 新名字 FROM student

去重(DISTINCT)

SELECT * FROM result --查询全部的考试成绩
SELECT `StudentNo` FROM result --查询有哪些同学参加了考试
SELECT DISTINCT `StudentNo` FROM result --发现重复数据,去重

WHERE条件子句

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

逻辑运算符:

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

模糊查询:比较运算符

运算符 语法 描述
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 SQL匹配,如果a匹配b,则结果为真
In a in (a1,a2,a3...) 假设a在a1,或者a在a2...其中的某一个值中,结果为真

联表查询

操作 描述 含义
Inner join 如果表中至少有一个匹配,,就返回行 内连接INNER JOIN/JOIN是最常用的连接操作。从数学的角度讲就是求两个表的交集
left join 会从左表返回所有的值,即使右表中没有匹配 左连接LEFT JOIN的含义就是求两个表的交集外加左表剩下的数据,左连接从左表(A)产生一套完整的记录,与匹配的记录(右表(B)) .如果没有匹配,右侧将包含null
right join 会从右表返回所有的值,即使左表中没有匹配 右连接RIGHT JOIN就是求两个表的交集外加右表剩下的数据
select * from blog inner join blog_type on blog.title_type=blog_type.id;
select * from blog join blog_type on blog.title_type=blog_type.id;
select * from blog,blog_type where blog.title_type=blog_type.id;
select * from blog left join blog_type on blog.title_type=blog_type.id;
select * from blog left join blog_type on blog.title_type=blog_type.id where blog_type.id is null;

分页和排序

排序:

  1. 在MySql中,使用 ASC 或 DESC 关键字来设置查询结果是按升序或降序排列。

  2. 尤其非常特别重要:默认按升序(ASC)排列。

  3. order by 后可加2个(或多个)字段,字段之间用英文逗号隔开。

  4. 若A用升序,B用降序,SQL该这样写:order by A ASC, B DESC; 默认同理,也可以这样写:order by A, B DESC;

  5. 若A、B都用降序,必须用两个DESC,order by A DESC, B DESC;

  6. 多个字段时,优先级按先后顺序而定。

分页:

SELECT * FROM table LIMIT 5,10; --检索记录行 6-15
SELECT * FROM table LIMIT 95,-1; --检索记录行 96-last
SELECT * FROM table LIMIT 5; --检索前 5 个记录行
换句话说,LIMIT n 等价于 LIMIT 0,n。

子查询

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

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

MySQL函数

常用函数

SELECT ABS() --绝对值
SELECT CELLING() --向上取整
SELECT FLOOR() --向下取整
SELECT RAND() --返回一个0~1之间的随机数
SELECT SIGN() --判断一个数的符号,负数返回-1,正数返回1

字符串函数

SELECT CHAR_LENGTH('即使再小的帆也能远航') --字符串长度
SELECT CONCAT('我','爱','你') --拼接字符串
SELECT INSERT('我爱编程',1,2,'超级热爱') --查询,从某个位置开始替换某个长度
SELECT LOWER('Guoyan') --小写字母
SELECT UPPER('Guoyan') --大写字母
SELECT INSERT('Guoyan','y') --返回第一次出现的字串的索引
SELECT REPLACE('坚持就能成功','坚持','努力') --替换出现的指定字符串
SELECT SUBSTR('坚持就能成功',3,2) --返回指定的子字符串(源字符串,截取的位置,截取的长度)
SELECT REVERSE('马上') --反转

时间和日期函数

SELECT CURRENT_DATE() --获取当前日期
SELECT CURDATE() --获取当前日期
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 SYSTEM_USER()
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 student; --Count(1),不会忽略null值,本质,计算行数

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

主要增强算法复杂度和不可逆性

UPDATE 表名 SET 字段名=MD5(字段名) WHERE id = 1

事务

什么是事务

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

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

原子性(Atomicity)

要么都成功,要么都失败

一致性(Consistency)

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

持久性(Durability)

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

隔离性(Isolation)

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

隔离所导致的一些问题:

脏读:

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

不可重复读:

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

虚读(幻读):

是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致

测试事务实现转账

--mysql是默认开启事务自动提交的
SET autocommit = 0 --关闭
SET autocommit = 1 --开启(默认的)

--手动处理事务
SET auticommit = 0 --关闭自动提交

--事务开启
START TRANSACTION --标记一个事务的开始,从这个之后的sql都在同一个事务内

INSERT xx
INSERT xx

--提交:持久化(成功)
COMMINT
--回滚
ROLLBACK

--事务结束
SET autocommit = 1 --开启自动提交

SAVEPOINT 保存点名 --设置一个事务的保存点
ROLLBACK TO SAVEPOINT 保存点名 --回滚到保存点
RELEASE SAVEPOINT 保存点名 --撤销保存点

索引

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构

索引的分类

  • 主键索引(PRIMARY KEY)
    • 唯一的标识,主键不可重复,只能有一列作为主键
  • 唯一索引(UNIQUE KEY)
    • 避免重复的列出现,唯一索引可以重复,多个列都可以标识唯一索引
  • 常规索引(KEY/INDEX)
    • 默认的,index,key关键字来设置
  • 全文索引(FULLTEXT)
    • 在特定的数据库下才有,MyISAM
    • 快速定位数据
--索引的使用
--1.在创建表的时候给字段增加索引
--2.创建完毕后,增加索引

--显示所有的索引信息
SHOW INDEX FROM student

--增加一个全文索引(索引名) 列名
ALTER TABLE school.student ADD FULLTEXT INDEX `studentName`(`studentNAme`);

--EXPLAIN分析sql执行的情况
EXPLAIN SELECT * FROM student; --非全文索引
EXPLAIN SELECT * FROM student WHERE MATCH(studentName) AGAINST('刘');

索引原则

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

索引的数据结构

Hash类型的索引

Btree:InnoDB的默认数据结构

阅读:CodingLabs - MySQL索引背后的数据结构及算法原理

权限管理和备份

用户管理

image-20231102163348864

SQL命令操作

用户表:mysql.user

本质:对这张表进行增删改查

--创建用户
CREATE USER 用户名 INDENTIFIED BY '密码'

--修改密码(修改当前用户密码)
SET PASSWORD = PASSWORD('密码')

--修改密码(修改指定用户密码)
SET PASSWORD FOR 用户名 = PASSWORD('密码')

--重命名
RENAME USER 原来的名字 TO 新的名字

--用户授权
--ALL PRIVILEGES 全部的权限(除了给别人授权,其他的都能干)
GRANT ALL PRIVILEGES ON *.* TO 用户名

--查询权限
SHOW GRANTS FOR 用户名 --查看指定用户的权限

--撤销权限
REMOVE ALL PRIVILEGES ON *.* FROM 用户名

MySQL备份

作用:

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

方式:

  • 直接拷贝物理文件
  • 在Navicat可视化工具手动导出
  • 使用mysqldump命令行导出
--导出
# mysqldump -h 主机 -u 用户名 -p 密码 数据库 表名 > 物理磁盘位置/文件夹

--导入
#登录的情况下
# source 备份文件

规范数据库设计

糟糕的数据库设计:

  • 数据冗余,浪费空间
  • 数据库插入和删除都会麻烦,异常(屏蔽使用物理外键)
  • 程序的性能差

良好的数据库设计:

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

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

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

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

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

三大范式

第一范式(1NF):要求数据库表的每一列都是不可分割的源自数据项(原子性:保证每一列不可再分)

第二范式(2NF):在满足第一范式的前提下,每张表只描述一件事情

第三范式(3NF):在满足第一范式和第二范式的前提下,第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关

规范性和性能的问题:

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

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

JDBC(重点)

第一个JDBC程序

  1. 创建一个普通项目
  2. 导入数据库驱动
  3. 编写测试代码
import java.sql.*;

//我的第一个jdbc程序
public class JdbcFirstDemo {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1.加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

        //2.用户信息和url
        String url = "jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&characterEncoding=utf8&userSSL=true&serverTimezone=UTC ";
        String username = "root";
        String password = "123456";

        //3.连接成功,数据库对象
        //connection 代表数据库
        Connection connection = DriverManager.getConnection(url, username, password);

        //4.执行SQL的对象
        Statement statement = connection.createStatement();

        //5.
        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();
    }
}

步骤总结:

  1. 加载驱动
  2. 连接数据库DriverManager
  3. 获得执行sql的对象Statement
  4. 获得返回的结果集
  5. 释放连接
//数据库设置自动提交
connection.setAutoCommit();
//事务提交
connection.commit();
//事务回滚
connection.rollback();

statement执行sql对象,Preparement执行sql对象

statement.executeQuery(); //查询操作返回ResultSet
statement.execute(); //执行任何sql
statement.executeUpdate(); //更新,插入,删除都是用这个,返回一个受影响的行数

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

resultSet.getObject(); //在不知道列类型的情况下使用
//如果知道列的类型就使用指定的类型
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
resultSet.getObject();
...

遍历,指针

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

Statement对象

代码实现:

  1. 提取工具类
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

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

    static {
        try{
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);

            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            //1.驱动只用加载一次
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

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

    //释放连接资源
    public static void release(Connection conn, Statement st, ResultSet rs){
        if (rs != null){
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (st != null){
            try {
                st.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (conn != null){
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}
  1. 编写增删改的方法(executeUpdate)
import com.gy.lesson02.utils.JdbcUtils;

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

public class TestInsert {
    public static void main(String[] args) {

        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();
            String sql = "INSERT INTO users (`id`,`name`,`password`,`email`,`birthday`)" +
                    "VALUES(9,'yyy','2222','333@qq.com','2000.02.22')";
            int i = st.executeUpdate(sql);
            if (i > 0){
                System.out.println("插入成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}
import com.gy.lesson02.utils.JdbcUtils;

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

public class TestDelete {
    public static void main(String[] args) {

        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();
            String sql = "DELETE FROM users WHERE id=9";
            int i = st.executeUpdate(sql);
            if (i > 0){
                System.out.println("删除成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}
import com.gy.lesson02.utils.JdbcUtils;

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

public class TestUpdate {
    public static void main(String[] args) {

        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();
            String sql = "UPDATE users SET `name`='yyy',`email`='33333@qq.com' WHERE id = 1";
            int i = st.executeUpdate(sql);
            if (i > 0){
                System.out.println("更新成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}
  1. 编写查的方法(executeQuery)
import com.gy.lesson02.utils.JdbcUtils;

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

public class TestSelect {
    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try{
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();

            String sql = "select * from users where id = 1";
            //查询完毕会返回一个结果集
            rs = st.executeQuery(sql);
            if (rs.next()){
                System.out.println(rs.getString("name"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

SQL注入的问题

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

import com.gy.lesson02.utils.JdbcUtils;

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

public class SQL注入 {
    public static void main(String[] args) {
        login(" 'or '1=1"," 'or '1=1");
    }

    //登录业务
    public static void login(String username,String password){
        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+"'";
            //查询完毕会返回一个结果集
            rs = st.executeQuery(sql);
            if (rs.next()){
                System.out.println(rs.getString("name"));
                System.out.println(rs.getString("password"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

PreparedStatement对象

PreparedStatement可以防止SQL注入,效率更好

  1. 新增
import com.gy.lesson02.utils.JdbcUtils;

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

public class TestInsert {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try{
            conn = JdbcUtils.getConnection();

            //区别:使用?占位符代替参数
            String sql = "INSERT INTO users (`id`,`name`,`password`,`email`,`birthday`)values(?,?,?,?,?)";
            ps = conn.prepareStatement(sql);

            //手动给参数赋值
            ps.setInt(1,5);
            ps.setString(2,"ggg");
            ps.setString(3,"123123");
            ps.setString(4,"44444@qq.com");
            //注意点:sql.Date 数据库
            //new Date().getTime() 获得时间戳
            ps.setDate(5,null);

            //执行
            int i = ps.executeUpdate();
            if (i>0){
                System.out.println("插入成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,ps,null);
        }
    }
}
  1. 删除
import com.gy.lesson02.utils.JdbcUtils;

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

public class TestDelete {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try{
            conn = JdbcUtils.getConnection();

            //区别:使用?占位符代替参数
            String sql = "DELETE FROM users WHERE id=?";
            ps = conn.prepareStatement(sql);

            //手动给参数赋值
            ps.setInt(1,5);

            //执行
            int i = ps.executeUpdate();
            if (i>0){
                System.out.println("删除成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,ps,null);
        }
    }
}
  1. 更新
import com.gy.lesson02.utils.JdbcUtils;

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

public class TestUpdate {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try{
            conn = JdbcUtils.getConnection();

            //区别:使用?占位符代替参数
            String sql = "update users set `name`=? where id=?;";
            ps = conn.prepareStatement(sql);

            //手动给参数赋值
            ps.setString(1,"郭岩");
            ps.setInt(2,1);


            //执行
            int i = ps.executeUpdate();
            if (i>0){
                System.out.println("更新成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,ps,null);
        }
    }
}
  1. 查询
import com.gy.lesson02.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 conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try{
            conn = JdbcUtils.getConnection();
            String sql = "select * from users where id=?";

            ps = conn.prepareStatement(sql);

            ps.setInt(1,1);
            //执行
            rs = ps.executeQuery();
            if (rs.next()){
                System.out.println(rs.getString("name"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,ps,rs);
        }
    }
}
  1. SQL注入
import com.gy.lesson02.utils.JdbcUtils;

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

public class SQL注入 {
    public static void main(String[] args) {
        login("'' or 1=1","123456");
    }

    public static void login(String username,String password){
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try{
            conn = JdbcUtils.getConnection();
            //PreparedStatement防止SQL注入的本质,把传递进来的参数当作字符
            String sql = "select * from users where `name`=? and `password`=?";

            ps = conn.prepareStatement(sql);

            ps.setString(1,username);
            ps.setString(2,password);
            //执行
            rs = ps.executeQuery();
            if (rs.next()){
                System.out.println(rs.getString("name"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,ps,rs);
        }
    }
}

JDBC操作事务

import com.gy.lesson02.utils.JdbcUtils;

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

public class TestTransaction01 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            //关闭数据库的自动提交,开启事务
            conn.setAutoCommit(false);

            String sql1 = "update account set money= money-100 where name = 'A'";
            ps = conn.prepareStatement(sql1);
            ps.executeUpdate();
            String sql2 = "update account set money= money+100 where name = 'B'";
            ps = conn.prepareStatement(sql2);
            ps.executeUpdate();

            //业务完毕,提交事务
            conn.commit();
            System.out.println("成功");
        } catch (SQLException throwables) {
            try {
                //如果失败,则默认回滚
                conn.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,ps,rs);
        }
    }
}

数据库连接池

数据库连接-->执行完毕-->释放

连接-->释放 十分浪费系统资源

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

开源数据源实现

DBCP

C3P0

Druid:阿里巴巴

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

DBCP

需要用到的jar包

commons-dbcp-1.4

commons-pool-1.6

C3P0

需要用到的jar包

c3p0-0.9.5.5.jar

mchange-commons-java-0.2.19.jar

posted @ 2023-11-16 08:30  郭岩不会打代码  阅读(18)  评论(0)    收藏  举报