JavaWeb--JDBC:概述,DriverManager,Connection,Statement,ResultSet,PreparedStatement、数据库连接池--2022年9月23日

第一节   JDBC 概述

  1、JDBC 概念

    JDBC 就是使用 java 语言操作关系数据库的一套 API

    全称:(Java DataBase Connectivity)Java 数据库连接

  2、JDBC 本质

    官方(sun 公司)定义的一套操作所有关系型数据库的规则,即接口

    各个数据库厂商去实现这套接口,提供数据库驱动 jar 包,其实就是实现类

    我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动 jar 包中的实现类

    

    

  3、JDBC 好处

    各数据厂商使用相同的接口,Java 代码不需要针对不同数据库分别开发

    可随时替换底层数据库,访问数据库的 Java 代码基本不变

    

第二节    JDBC 快速入门

  1、Java 操作数据库的流程

    

  2、编写代码步骤

    A、创建工程,导入驱动 jar 包

    B、注册驱动:Class.forName("com.mysql.jdbc.Driver");// 把这个类加载进内存

    C、获取连接:Connection conn = DriverManager.getConnection(url,username,password);//Java 代码需要发送 SQL 给 MySQL 服务端,就需要先建立连接

    D、定义 SQL 语句:String sql = "update.....";

    E、获取执行 SQL 对象:statement stmt = conn.createStatement();// 执行 SQL 语句需要 SQL 执行对象,而这个执行对象就是 Statement 对象

    F、执行 SQL:stmt.executeUpdate(sql);

    G、处理返回结果

    H、释放资源

    示例

/**
* JDBC 快速入门
*/
public class JDBCDemo {
public static void main(String[] args) throws
Exception {
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接
String url =
"jdbc:mysql://127.0.0.1:3306/db1";
String username = "root";
String password = "1234";
Connection conn =
DriverManager.getConnection(url, username,
password);
//3. 定义 sql
String sql = "update account set money =
2000 where id = 1";
//4. 获取执行 sql 的对象 Statement
Statement stmt = conn.createStatement();
//5. 执行 sql
int count = stmt.executeUpdate(sql);//受影响
的行数
//6. 处理结果
System.out.println(count);
//7. 释放资源
stmt.close();
conn.close();}
}

第三节   JDBC-API 详解

  1、DriverManager(驱动管理类)

    A、注册驱动

      Class.forName("com.mysql.jdbc.Driver");

      之前的入门案例,注册驱动是上面这么写的,但其实 DriverManager 类也提供了一个方法可以注册驱动:static void registerDriver(Driver driver)-- 使用 DriverManager 注册给定的驱动程序

      

 

 

       

    B、获取数据库连接 -- 参数说明

      static Connection getConnection(String url,String user,String password)-- 尝试建立与给定数据库 URL 的连接

      url:连接路径

        

 

 

       user:用户名

      password:密码

  2、Connection(数据库连接对象)

    A、Connection 作用

      获取执行 SQL 的对象

      管理事务

    B、获取执行对象

      普通执行对象:Statement createStatement()

      预编译 SQL 的执行 SQL 对象,防止 SQL 注入:PreparedStatement prepareStatement(sql)

      执行存储过程的对象:CallableStatement prepareCall(sql)

    C、事务管理

      JDBC 事务管理的方法:

      void setAutoCommit(boolean autoCommit)-- 参与 autoCommit 表示是否自动提交事务,true 表示自动提交事务,false 表示手动提交事务。所以开启事务需将参数设置为 false

      void commit()-- 提交事务

      void rollback()-- 回滚事务

/**
* JDBC API 详解:Connection
*/
public class JDBCDemo3_Connection {
public static void main(String[] args) throws
Exception {
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接:如果连接的是本机 mysql 并且端口是默认
的 3306 可以简化书写
String url = "jdbc:mysql:///db1?
useSSL=false";
String username = "root";
String password = "1234";
Connection conn =
DriverManager.getConnection(url, username,
password);
//3. 定义 sql
String sql1 = "update account set money =
3000 where id = 1";
String sql2 = "update account set money =
3000 where id = 2";
//4. 获取执行 sql 的对象 Statement
Statement stmt = conn.createStatement();
try {
// ============开启事务==========
conn.setAutoCommit(false);
//5. 执行 sql
int count1 = stmt.executeUpdate(sql1);//
受影响的行数
//6. 处理结果
System.out.println(count1);
int i = 3/0;
//5. 执行 sql
int count2 = stmt.executeUpdate(sql2);//
受影响的行数
//6. 处理结果
System.out.println(count2);
// ============提交事务==========
//程序运行到此处,说明没有出现任何问题,则需求提
交事务
conn.commit();} catch (Exception e) {
// ============回滚事务==========
//程序在出现异常时会执行到这个地方,此时就需要回
滚事务
conn.rollback();
e.printStackTrace();}
//7. 释放资源
stmt.close();
conn.close();}
}

  3、Statement 对象:执行 SQL 语句

    A、执行 DDL、DML 语句:int executeUpdate(String sql)

    B、执行 DQL 语句:ResultSet executeQuery(String sql)

    执行 DML 语句

/**
* 执行 DML 语句
* @throws Exception
*/
@Test
public void testDML() throws Exception {
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接:如果连接的是本机 mysql 并且端口是默认的
3306 可以简化书写
String url = "jdbc:mysql:///db1?
useSSL=false";
String username = "root";
String password = "1234";
Connection conn =
DriverManager.getConnection(url, username,
password);
//3. 定义 sql
String sql = "update account set money = 3000
where id = 1";
//4. 获取执行 sql 的对象 Statement
Statement stmt = conn.createStatement();
//5. 执行 sql
int count = stmt.executeUpdate(sql);//执行完
DML 语句,受影响的行数
//6. 处理结果
//System.out.println(count);
if(count > 0){
System.out.println(" 修改成功~");}else{
System.out.println(" 修改失败~");}
//7. 释放资源
stmt.close();
conn.close();}

    执行 DQL 语句

/**
* 执行 DDL 语句
* @throws Exception
*/
@Test
public void testDDL() throws Exception {
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接:如果连接的是本机 mysql 并且端口是默认的 3306 可以简化书写
String url = "jdbc:mysql:///db1?
useSSL=false";
String username = "root";
String password = "1234";
Connection conn =
DriverManager.getConnection(url, username,
password);
//3. 定义 sql
String sql = "drop database db2";
//4. 获取执行 sql 的对象 Statement
Statement stmt = conn.createStatement();
//5. 执行 sql
int count = stmt.executeUpdate(sql);//执行完
DDL 语句,可能是 0
//6. 处理结果
System.out.println(count);
//7. 释放资源
stmt.close();
conn.close();}

  4、ResultSet

    A、作用:封装了 SQL 查询语句的结果:ResultSet executeQuery(sql)-- 执行 SQL 语句,返回 ResultSet 对象

    B、ResultSet 对象提供的操作查询结果的方法:

    

 

 

     

 

 

     C、实现代码

/**
* 执行 DQL
* @throws Exception
*/
@Test
public void testResultSet() throws Exception {
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接:如果连接的是本机 mysql 并且端口是默认的
3306 可以简化书写
String url = "jdbc:mysql:///db1?useSSL=false";
String username = "root";
String password = "1234";
Connection conn =
DriverManager.getConnection(url, username,
password);
//3. 定义 sql
String sql = "select * from account";
//4. 获取 statement 对象
Statement stmt = conn.createStatement();
//5. 执行 sql
ResultSet rs = stmt.executeQuery(sql);
//6. 处理结果, 遍历 rs 中的所有数据
/* // 6.1 光标向下移动一行,并且判断当前行是否有数据
while (rs.next()){//6.2 获取数据 getXxx()
int id = rs.getInt(1);
String name = rs.getString(2);
double money = rs.getDouble(3);
System.out.println(id);
System.out.println(name);
System.out.println(money);
System.out.println("--------------");
}*/
// 6.1 光标向下移动一行,并且判断当前行是否有数据
while (rs.next()){
//6.2 获取数据 getXxx()
int id = rs.getInt("id");
String name = rs.getString("name");
double money = rs.getDouble("money");
System.out.println(id);
System.out.println(name);
System.out.println(money);
System.out.println("--------------");
}
//7. 释放资源
rs.close();
stmt.close();
conn.close();}

  5、PreparedStatement

    A、PreparedStatement 作用:

      预编译 SQL 语句并执行:预防 SQL 注入问题

    B、SQL 注入

      SQL 注入是通过操作输入来修改事先定义好的 SQL 语句,用以达到执行代码对服务器进行攻击的方法

    C、代码模拟 SQL 注入问题

@Test
public void testLogin() throws Exception {
//2. 获取连接:如果连接的是本机 mysql 并且端口是默认的
3306 可以简化书写
String url = "jdbc:mysql:///db1?useSSL=false";
String username = "root";
String password = "1234";
Connection conn =
DriverManager.getConnection(url, username,
password);
// 接收用户输入 用户名和密码
String name = "sjdljfld";
String pwd = "' or'1'='1";
String sql = "select * from tb_user where
username = '"+name+"'and password ='"+pwd+"'";
// 获取 stmt 对象
Statement stmt = conn.createStatement();
// 执行 sql
ResultSet rs = stmt.executeQuery(sql);
// 判断登录是否成功
if(rs.next()){
System.out.println("登录成功 ~");}else{
System.out.println("登录失败 ~");}
//7. 释放资源
rs.close();
stmt.close();
conn.close();}

    

    D、获取 PreparedStatement 对象

// SQL 语句中的参数值,使用?占位符替代
String sql = "select * from user where username =
? and password = ?";
// 通过 Connection 对象获取,并传入对应的 sql 语句
PreparedStatement pstmt =
conn.prepareStatement(sql);

    E、设置参数值

    

 

     F、执行 SQL 语句

    

    G、使用 PreparedStatement 改进

@Test
public void testPreparedStatement() throws
Exception {
//2. 获取连接:如果连接的是本机 mysql 并且端口是默认的
3306 可以简化书写
String url = "jdbc:mysql:///db1?useSSL=false";
String username = "root";
String password = "1234";
Connection conn =
DriverManager.getConnection(url, username,
password);
// 接收用户输入 用户名和密码
String name = "zhangsan";
String pwd = "' or'1'='1";
// 定义 sql
String sql = "select * from tb_user where
username = ? and password = ?";
// 获取 pstmt 对象
PreparedStatement pstmt =
conn.prepareStatement(sql);
// 设置?的值
pstmt.setString(1,name);
pstmt.setString(2,pwd);
// 执行 sql
ResultSet rs = pstmt.executeQuery();
// 判断登录是否成功
if(rs.next()){
System.out.println("登录成功 ~");}else{
System.out.println("登录失败 ~");}
//7. 释放资源
rs.close();
pstmt.close();
conn.close();}

    执行上面语句就可以发现不会出现 SQL 注入漏洞问题了。那么 PreparedStatement 又是如何解决的呢?它是将特殊字符进行了转义,转义的 SQL 如下

    

 

     H、PreparedStatement 原理

    

 

     上图解释:

      将 sql 语句发送到 MySQL 服务器端

      MySQL 服务端会对 sql 语句进行如下操作

        检查 SQL 语句,检查 SQL 语句的语法是否正确

        编译 SQL 语句。将 SQL 语句编译成可执行的函数。检查 SQL 和编译 SQL 花费的时间比执行 SQL 的时间还要长。如果我们只是重新设置参数,那么检查 SQL 语句和编译 SQL 语句将不需要重复执行,这样就提高了性能。

        执行 SQL 语句

    I、可通过查询日志看一下原理

    

 

     测试代码如下:

/**
* PreparedStatement 原理
* @throws Exception
*/
@Test
public void testPreparedStatement2() throws
Exception {
//2. 获取连接:如果连接的是本机 mysql 并且端口是默认的
3306 可以简化书写
// useServerPrepStmts=true 参数开启预编译功能
String url = "jdbc:mysql:///db1?
useSSL=false&useServerPrepStmts=true";
String username = "root";
String password = "1234";
Connection conn =
DriverManager.getConnection(url, username,
password);
// 接收用户输入 用户名和密码
String name = "zhangsan";
String pwd = "' or'1'='1";
// 定义 sql
String sql = "select * from tb_user where
username = ? and password = ?";
// 获取 pstmt 对象
PreparedStatement pstmt =
conn.prepareStatement(sql);
Thread.sleep(10000);
// 设置?的值
pstmt.setString(1,name);
pstmt.setString(2,pwd);
ResultSet rs = null;
// 执行 sql
rs = pstmt.executeQuery();
// 设置?的值
pstmt.setString(1,"aaa");
pstmt.setString(2,"bbb");
// 执行 sql
rs = pstmt.executeQuery();
// 判断登录是否成功
if(rs.next()){
System.out.println("登录成功 ~");}else{
System.out.println("登录失败 ~");}
//7. 释放资源
rs.close();
pstmt.close();
conn.close();}

    

第四节   数据库连接池

  1、数据库连接池简介

    数据库连接池是个容器,负责分配、管理数据库连接(Connection)

    它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个

    释放空闲时间长超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏

    好处:1. 资源重用  2. 提升系统响应速度  3. 避免数据库连接遗漏

  2、数据库连接池实现

  

  3、常见的数据库连接池

    DBCP

    C3P0

    Druid-- 德鲁伊(目前使用更多,性能比上面两个要好)

      Druid 连接池是阿里巴巴开源的数据库连接池项目

      功能强大,性能优秀,是 Java 语言最好的数据库连接池之一

  4、Druid 使用

    导入 jar 包 druid-1.1.12.jar

    定义配置文件

    加载配置文件

    获取数据库连接池对象

    获取连接

配置文件如下
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql:///db1? useSSL=false&useServerPrepStmts=true username=root password=1234 # 初始化连接数量 initialSize=5 # 最大连接数 maxActive=10 # 最大等待时间 maxWait=3000
/**
* Druid数据库连接池演示
*/

public
class DruidDemo { public static void main(String[] args) throws Exception { //1. 导入 jar 包 //2. 定义配置文件 //3. 加载配置文件 Properties prop = new Properties(); prop.load(new FileInputStream("jdbcdemo/src/druid.properties")); //4. 获取连接池对象 DataSource dataSource = DruidDataSourceFactory.createDataSource(prop); //5. 获取数据库连接 Connection Connection connection = dataSource.getConnection(); System.out.println(connection); //获取到了连接 后就可以继续做其他操作了 //System.out.println(System.getProperty("user.dir") );} }