1.JDBC 的简介
JDBC (Java Data Base Connectivity,Java数据库连接)是 Java 程序和数据库之间的桥梁 , 包含了一套 Java 定义于执行 SQL 语句的接口 , 使开发者能够编写数据库的程序
JDBC 的主要作用 : 与数据库建立连接 , 发送 SQL 语句 , 处理数据库执行结果

JDBC 工作原理 : 加载驱动 , 建立连接 , 创建 Statement , 执行 SQL , 处理结果 , 关闭资源
2.为什么使用 JDBC
创建客户端操作数据库的过程 :
- 确定连接地址 , 添加端口号(数据源)
 - 连接到数据库服务(数据库连接)
 - 发送 SQL 语句(执行对象)
 - 得到返回结果集并显示(结果集)
 - 关闭连接(释放资源)
 
Java 采取的做法是把上述操作步骤定义了相应的接口 , 具体实现交给数据库厂商去做 , Java 程序员只需按照需要调用接口中的定义方法即可 , 这样不论使用什么数据库 , 都对 Java 程序没有任何影响 , 即使换一个数据库 , 也只需要换一下相应厂商的是是实现依赖
JDBC 使用过程可以概括为 : 加载数据库厂商的驱动包 , 建立连接 , 创建 Statement , 执行 SQL , 处理结果释放资源 , 关闭连接
3.使用 JDBC
3.1 创建 Maven 工程并配置国内镜像
- 修改 Maven 配置文件 , 在<mirrors></mirrors>标签中替换为以下代码
 
            
            
              java
              
              
            
          
          <mirrors>
    <!-- mirror
     | Specifies a repository mirror site to use instead of a given repository. The repository that
     | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used
     | for inheritance and direct lookup purposes, and must be unique across the set of mirrors.
     |
    -->
    <!-- 加入如下mirror节点 使用国内阿里云仓库镜像  开始 -->
    <mirror>
        <id>aliyunmaven</id>
        <mirrorOf>*</mirrorOf>
        <name>阿里云公共仓库</name>
        <url>https://maven.aliyun.com/repository/public</url>
    </mirror>
    <mirror>
        <id>central</id>
        <mirrorOf>*</mirrorOf>
        <name>aliyun central</name>
        <url>https://maven.aliyun.com/repository/central</url>
    </mirror>
    <mirror>
        <id>spring</id>
        <mirrorOf>*</mirrorOf>
        <name>aliyun spring</name>
        <url>https://maven.aliyun.com/repository/spring</url>
    </mirror>
    <!-- 加入如下mirror节点 使用国内阿里云仓库镜像  结束-->
</mirrors>
        - IDEA 中 Maven 的配置文件夹 : IDEA 安装目录/plugins/maven/lib/maven3/conf/settings.xml
 
3.2 获取 MySQL 的驱动包
在 mvnrepository.com 下搜索 MySQL , 找到最新的驱动包

3.3 修改 pom.xml 文件
在 maven 工程中的 pom.xml 中的**<dependencies></denpendencies>**标签中添加 MySQL 依赖
如图所示:

4.创建工程
            
            
              java
              
              
            
          
          import java.sql.*;
import java.text.MessageFormat;
import java.util.Scanner;
public class test1 {
    public static void main(String[] args) {
        //分离出来方便关闭资源
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            // 1. 加载数据库厂商提供的驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 2. 获取数据库连接
            connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/bit_1027?characterEncoding=utf8" +
                                                     "&allowPublicKeyRetrieval=true&useSSL=false","root","119856");
            // 3. 创建 statement对象
            statement = connection.createStatement();
            // 4. 定义SQL并执行SQL语句
            System.out.println("请输入学生姓名");
            Scanner scanner = new Scanner(System.in);
            //接收用户输入
            String name = scanner.next();
            String sql = "select id,name,gender ,age,class_id from students where name = '"+name+"'";
            // 5. 执行SQL, 获取查询结果集
            resultSet = statement.executeQuery(sql);
            // 6. 对结果集进行遍历 , 获取数据
            while(resultSet.next()){
                int id = resultSet.getInt(1);
                String stuname = resultSet.getString(2);
                String stugender = resultSet.getString(3);
                int age = resultSet.getInt(4);
                int class_id = resultSet.getInt(5);
                System.out.println(MessageFormat.format("学生编号 = {0} , 学生姓名 = {1} , 性别 = {2} , 年龄 = {3}  , 班级编号 = {4}",id,stuname,stugender,age,class_id));
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        finally {
            // 依次从下往上释放资源,关闭连接
            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();
                }
            }
        }
    }
}
        
目标数据库 :

① 建立数据库连接

② 创建 Statement

④ 执行 SQL 语句
- 执行 select 查询时返回的是一个结果集 , 用 ResultSet 接收
 - 执行 nsert , update , delete 操作时 , 返回的是受影响的行数 , 用 int 类型接收
 
- executeQuery() , 执行返回的是一个结果集 , 通常用于 select 操作
 - executeUpdate() , 执行结果是一个整型 , 通常用于 insert , update , delete 操作
 

⑤ 处理结果集
- 如果返回的是一个结果集 , 则需要遍历这个集合获取对应列的值
 

⑥ 释放资源
- 在整个数据库访问的过程中的对象都需要释放 , 包括 : ResultSet,Statement和Connection, 后创建的先释放
 

5. 优化 1
1.SQL 注入
SQL注⼊即是指web应⽤程序对⽤⼾输⼊数据的合法性没有判断或过滤不严,攻击者可以在web应 ⽤程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现⾮法操 作,以此来实现欺骗数据库服务器执⾏⾮授权的任意查询,从⽽进⼀步得到相应的数据信息
示例 : 将 正常的条件值替换为
            
            
              java
              
              
            
          
          'or/**/1=1;# 
        
- 此操作仅用于理解 SQL 注入的危害,实际开发中必须使用 PreparedStatement****参数化查询 (如之前建议的代码修改),从根源上杜绝注入风险。
 - 未经授权对系统进行注入测试属于违法行为,务必遵守法律法规和道德规范。
 

解决方案
使用 PreparedStatement 预编译 SQL 语句对象 , 实现参数化查询 , 杜绝 SQL 注入分享 , 符合企业级开发规范
通过 Connection 对象获取到 PreparedStatement 对象 , 需要传入 SQL 模板 , 动态参数用占位符 ? 表示

2.如果要执行别的 SQL 语句需要重复打开客户端
使用 DriverManager 每次调用 getConnection 方法都会初始化一个新的连接 , 使用完后会关闭真实连接 , 导致资源浪费

解决方案
使用 DataSource 连接池技术 , 会在初始化时 创建一定数量的数据库连接 , 这些连接可以重复使用 , 关闭时并不是真正关闭 , 而是将连接归还给连接池 , 以供后续使用 , 有效提高资源利用率和性能

            
            
              java
              
              
            
          
          import com.mysql.cj.jdbc.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.Scanner;
public class test2 {
    public static void main(String[] args) {
        //定义MySQL数据源对象
        MysqlDataSource mysqlDataSource = new MysqlDataSource();
        //设置数据库连接串
        mysqlDataSource.setURL("jdbc:mysql://127.0.0.1:3306/bit_1027?characterEncoding=utf8" +
                               "&allowPublicKeyRetrieval=true&useSSL=false");
        //用户名
        mysqlDataSource.setUser("root");
        //密码
        mysqlDataSource.setPassword("119856");
        //定义JDBC的数据源对象
        DataSource dataSource = mysqlDataSource;
        //定义连接对象
        Connection connection = null;
        //定义预处理SQL执行对象
        PreparedStatement statement = null;
        //定义结果集对象
        ResultSet resultSet = null;
        try {
            // 1.通过数据源获取数据库连接
            connection = dataSource.getConnection();
            // 2.获取预处理SQL执行对象
            //定义要执行的SQL
            String sql = "select id,name,gender ,age,class_id from students where name = ?";
            statement = connection.prepareStatement(sql);
            //接收用户输入
            System.out.println("请输入学生姓名");
            Scanner scanner = new Scanner(System.in);
            String name = scanner.next();
            // 3.用真实姓名替代占位符
            statement.setString(1,name);
            // 4.执行SQL ,获取结果集
            resultSet = statement.executeQuery();
            //遍历结果集
            while(resultSet.next()){
                int id = resultSet.getInt(1);
                String stuname = resultSet.getString(2);
                String stugender = resultSet.getString(3);
                int age = resultSet.getInt(4);
                int class_id = resultSet.getInt(5);
                System.out.println(MessageFormat.format("学生编号 = {0} , 学生姓名 = {1} , 性别 = {2} , 年龄 = {3}  , 班级编号 = {4}",id,stuname,stugender,age,class_id));
            }
            //            while(resultSet.next()){
            //                int id = resultSet.getInt("id");
            //                String stuname = resultSet.getString("name");
            //                String stugender = resultSet.getString("gender");
            //                int age = resultSet.getInt("age");
            //                int class_id = resultSet.getInt("class_id");
            //                // 打印逻辑不变
            //            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        finally {
            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();
                }
            }
        }
    }
}
        6. 优化 2
1. 封装
将 获取数据库连接 和 释放资源,关闭连接 等 , 封装起来
            
            
              java
              
              
            
          
          package util;
import com.mysql.cj.jdbc.MysqlDataSource;
import com.mysql.cj.xdevapi.Collection;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DBUtil {
    //数据源
    private static DataSource dataSource = null;
    //数据库连接对象
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/bit_1027?characterEncoding=utf8" +
    "&allowPublicKeyRetrieval=true&useSSL=false";
    //用户名
    private static final String USER = "root";
    //密码
    private static final String PASSWORD = "119856";
    //当类加载到JVM时 , 执行数据源的初始化
    static {
        MysqlDataSource mysqlDataSource = new MysqlDataSource();
        mysqlDataSource.setURL(URL);
        mysqlDataSource.setUser(USER);
        mysqlDataSource.setPassword(PASSWORD);
        dataSource = mysqlDataSource;
    }
    //构造方法私有化 ,防止new这个对象
    private DBUtil() {
    }
    //获取数据库连接
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    //释放资源 , 关闭连接
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        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();
            }
        }
    }
}
        示例 : 执行一条插入语句
            
            
              java
              
              
            
          
          import util.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;
public class insertTest1 {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement statement= null;
        //插入语句不需要定义结果集 (insert返回的是受影响的行数)
        try {
            // 1.获取数据库连接
            connection = DBUtil.getConnection();
            // 2.定义SQL语句
            String sql = "insert into students(name,gender,age,class_id) values (?,?,?,?);";
            // 3.定义SQL预处理对象
            statement = connection.prepareStatement(sql);
            // 4.接收用户参数
            System.out.println("请输入姓名");
            Scanner scanner = new Scanner(System.in);
            String name = scanner.next();
            System.out.println("请输入性别");
            String gender = scanner.next();
            System.out.println("请输入年龄");
            Integer age = Integer.valueOf(scanner.next());
            System.out.println("请输入班级");
            Integer class_id = Integer.valueOf(scanner.next());
            // 5.用真实数据填充占位符
            statement.setString(1,name);
            statement.setString(2,gender);
            statement.setInt(3,age);
            statement.setInt(4,class_id);
            // 执行插入操作并获取受影响行数
            int rows = statement.executeUpdate();
            System.out.println("插入成功,受影响行数:" + rows);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        finally {
            //释放资源,关闭连接
            DBUtil.close(null,statement,connection);
        }
    }
}