• JDBC快速入门


    一、JDBC 概述


    • JDBC(全称:Java Database Connectivity)Java数据库连接,就是使用Java语言操作关系型数据库的一套API。
    • Sun公司为Java设计了一套操作所有关系型数据库的API(位于java.sql和javax.sql包下);然后各个数据库厂商去实现这套接口,并提供相应的数据库驱动jar包;这样我们可以使用这套接口(JDBC)编程,其实真正执行的代码是驱动jar包中的实现类。
    • 好处:各个数据库厂商使用相同的接口,这样Java代码不需要针对不同数据库分别开发;然后我们可以随时替换底层数据库,它访问数据库的Java代码基本不变。

    在这里插入图片描述


    二、JDBC快速入门


    1. 开发环境搭建

    1、创建一个普通的空项目

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aQx9d6Ru-1669516492142)(JDBC.assets/image-20220718173544643.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XozNJduP-1669516492142)(JDBC.assets/image-20220718173708368.png)]

    2、配置JDK版本

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1P5zM0ot-1669516492143)(JDBC.assets/image-20220718173857728.png)]

    3、创建一个子模块(jdbc快速入门的程序在这里面写)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C1vebDFa-1669516492147)(JDBC.assets/image-20220718174129296.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A2jdOHMA-1669516492148)(JDBC.assets/image-20220718174238449.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lysBxsk6-1669516492149)(JDBC.assets/image-20220718174300017.png)]

    4、导入jar包

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oxmcqCqS-1669516492149)(JDBC.assets/image-20220718194901077.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NoxJXCh5-1669516492150)(JDBC.assets/image-20220718195342220.png)]


    2. 使用JDBC访问数据库

    JDBC操作数据库步骤如下:

    • 注册驱动
    • 获取数据库连接对象 (Connection)
    • 定义SQL语句
    • 获取执行SQL的对象 (Statement)
    • 执行SQL
    • 处理集并返回结果(ResultSet)
    • 释放资源

    1、创建数据库和表:

    CREATE DATABASE `jdbc_test` DEFAULT CHARSET utf8mb4;
    
    CREATE TABLE `account`(
      `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID',
      `name` varchar(20) NOT NULL COMMENT '姓名',
      `salary` int(11)  COMMENT '薪资',
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2、编写Java程序:

    package com.baidou.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.Statement;
    
    /**
     * JDBC快速入门
     *
     * @author 白豆五
     * @version 2022/7/18 19:56
     * @since JDK8
     */
    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/jdbc_test?useSSL=false";
            String user = "root";
            String password = "123456";
            Connection conn = DriverManager.getConnection(url, user, password);
    
            // 3、定义sql语句
            String sql = "insert into account(name,salary) values('王强',10000)";
    
            // 4、获取执行sql的对象 Statement
            Statement stmt = conn.createStatement();
    
            // 5、执行sql
            int count = stmt.executeUpdate(sql);
    
            // 6、处理结果
            // 打印受影响的行数
            System.out.println(count);
            System.out.println(count>0?"插入成功":"插入失败");
    
            // 7、释放资源
            stmt.close();
            conn.close();
        }
    }
    
    • 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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UrHX2XS4-1669516492150)(JDBC.assets/image-20220718203614831.png)]

    控制输出结果如下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XNEX2S7U-1669516492151)(JDBC.assets/image-20220718203124299.png)]

    表中的数据:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hXxuqe5C-1669516492151)(JDBC.assets/image-20220718203225862.png)]


    三、JDBC-API详解


    1. DriverManager


    • DriverManager:驱动管理类。
    • 作用:注册驱动、获取数据库的连接。

    1、注册驱动

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7sp0OB2e-1669516492151)(JDBC.assets/image-20220718204431218.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QNMH8yBc-1669516492152)(JDBC.assets/image-20220718204849139.png)]

    例如: MySQL数据库驱动类 com.mysql.jdbc.Driver,实现了java.sql.Driver接口。

    查看源码发现底层是通过DriverManager.registerDriver()来注册驱动的。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a3XFzNB5-1669516492152)(JDBC.assets/image-20220718205216879.png)]

    但是我们直接使用DriverManager.registerDriver(new Driver())注册驱动属于硬编码方式(如果我不想用mysql,换成SqlServer 我还得导入别的厂商提供的包而且每次都得修改源代码),另外它注册了两次驱动(一次是类加载的时候、一次是new Driver对象的时候)。

    所以我们可以通过反射的方式来加载这个数据库驱动类Class.forName(xxx.Driver)

    扩展:

    • MySQL5之后的驱动包,可以省略注册驱动 Class.forName("xxx");的步骤。(但是能写还是尽量写上,避免版本兼容问题)
    • 自动加载jar包中META-INF/services/java.sql.Driver文件中的驱动类。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WBcg8WWX-1669516492152)(JDBC.assets/image-20220718210415594.png)]


    2、获取数据库的连接

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bH8oLAvT-1669516492152)(JDBC.assets/image-20220718205718991.png)]

    getConnection()方法中需要传递3个参数,分别是urluserpassword

    (1)url

    • url,统一资源定位符(网络中某个资源的绝对地址)。

    • url由哪几个部分组成:协议,ip,port,资源名。

    比如我们访问百度的主页:

    http://39.156.66.18:80/index.html
    
    http://          通信协议
    39.156.66.18     服务器ip地址
    80               端口,80是Web服务的默认端口
    index.html       是服务器上某个资源名
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    mysql5.7和8.0驱动中url:

    // mysql5.7
    // userSSL=false: 禁用安全连接方式,关闭警告问题。  
    // useUnicode=true&characterEncoding=utf-8: 字符集编码
    url=jdbc:mysql://127.0.0.1:3306/mydatabase?useSSL=false&useUnicode=true&characterEncoding=utf-8
    
    // mysql8.0,
    // 需要配置时区:serverTimezone=GMT%2B8 东八区GMT+8
    url=jdbc:mysql://127.0.0.1:3306/mydatabase?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (2)user:用户名

    (3)password:密码

    扩展:

    • 如果连接的是本地MySQL并且端口号是默认的3306,可以简化为:jdbc:mysql:///db
    • 在url中拼接 userSSL=false,禁用安全连接方式,解决警告问题。
      • 例如:jdbc:mysql://127.0.0.1:3306/mydatabase?useSSL=false,如果后面还要拼接参数如要用 & 隔开。

    2. Connection


    • Connection:数据库连接对象。
    • 作用:获取执行SQL的对象、管理事务。

    2.1 获取执行SQL的对象

    a. 普通执行SQL对象:

    Statement createStatement()
    
    • 1

    b. 预编译的执行SQL对象:(防止SQL注入)

    PreparedStatement prepareStatement(String sql)
    
    • 1

    c. 执行存储过程的对象

    CallableStatement prepareCall(String sql)
    
    • 1

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M5CkEbg5-1669516492153)(JDBC.assets/image-20220718214205257.png)]


    2.2 事务管理

    作用:保证在一个事务中的所有SQL要么全部执行成功,要么全部不执行。

    a. MySQL 事务管理:

    开启事务:BEGIN; 或者 START TRANSACTION;
    提交事务:COMMIT;
    回滚事务:ROLLBACK;
    
    # MySQL默认自动提交事务
    
    • 1
    • 2
    • 3
    • 4
    • 5

    b. JDBC事务管理:Connection接口中定义了3个对应的方法:

    开启事务: setAutoCommit(boolean autoCommit): true为自动提交事务;false为手动提交事务,即为开启事务。
    
    提交事务:commit()
    回滚事务: rollback()
    
    • 1
    • 2
    • 3
    • 4

    示例:

    package com.baidou.jdbc;
    
    import java.sql.*;
    /**
     * Connection,事务操作
     *
     * @author 白豆五
     * @version 2022/7/18 21:50
     * @since JDK8
     */
    public class JDBC_Connection {
        public static void main(String[] args) throws Exception {
            // 1、注册驱动
            // Class.forName("com.mysql.jdbc.Driver");
    
            // 2、获取连接
            String url = "jdbc:mysql:///jdbc_test?useSSL=false";
            String user = "root";
            String password = "123456";
            Connection conn = DriverManager.getConnection(url, user, password);
    
            // 3、定义sql语句
            String sql1 = "insert into account(name,salary) values('李四',5000)";
            String sql2 = "insert into account(name,salary) values('王五',4004)";
    
            // 4、获取执行sql的对象 Statement
            Statement stmt = conn.createStatement();
    
            try {
                /* 开启事务 */
                conn.setAutoCommit(false); // 手动提交事务
                // 5、执行sql
                int count1 = stmt.executeUpdate(sql1);
                // 6、处理结果
                System.out.println(count1);// 打印受影响的行数
    
                // 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();
        }
    }
    
    • 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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qVdBdtXw-1669516492153)(JDBC.assets/image-20220718220025835.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NtbgdrdP-1669516492153)(JDBC.assets/image-20220718220042542.png)]


    3. Statement


    • Statement:执行SQL的对象

    • 作用:执行SQL语句

    1、执行DML、DDL语句

    int executeUpdate(sql)
    
    返回值: (1)DML语句影响的行数
           (2)DDL语句执行后,执行成功也可能返回0
    
    • 1
    • 2
    • 3
    • 4

    2、执行DQL语句

    ResultSet executeQuery(sql)
    
    返回值: ResultSet结果集对象
    
    • 1
    • 2
    • 3

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PTPKCRYn-1669516492154)(JDBC.assets/image-20220719070429457.png)]


    4. ResultSet


    • ResultSet:结果集对象
    • 作用:封装了DQL查询语句的结果

    获取查询结果:

    // next功能: (1)将游标从当前位置向下移动一行(2)判断当前行是否有效
    boolean next() 
    返回值:   true:有效行,当前行有数据 
    	  	 false:	无效行,当前行无数据 
    
    • 1
    • 2
    • 3
    • 4
    xxx getXxx(参数) //获取指定类型数据
    
      xxx是数据类型,例如 int getInt(参数)String getString(参数) 
      
      参数(重载方法):  int: 列的编号,从1开始
          	      String: 列的名称
    
    	例如获取表中id的数据:
    	int id = getInt("id");
    	int id = getInt(1);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GnYKAtmf-1669516492154)(JDBC.assets/image-20220719072523534.png)]

    ResultSet使用步骤:

    • 游标向下移动一行,并判断该行否有数据:next()
    • 获取数据: getXxx(参数)
    // 通过循环遍历结果集对象
    while(rs.next()){ // rs.next()将游标从当前位置向下移动一行并判断当前行是否有效
        // 获取数据
        rs.getXxx(参数);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    示例:

    /**
     * ResultSet的使用
     *
     * @author 白豆五
     * @version 2022/7/19 7:42
     * @since JDK8
     */
    public class JDBC_ResultSet {
        public static void main(String[] args) throws Exception {
            // 1、注册驱动
            // Class.forName("com.mysql.jdbc.Driver");
    
            // 2、获取连接
            String url = "jdbc:mysql:///jdbc_test?useSSL=false";
            String user = "root";
            String password = "123456";
            Connection conn = DriverManager.getConnection(url, user, password);
    
            // 3、定义sql语句
            String sql = "select * from account";
    
            // 4、获取执行sql的对象 Statement
            Statement stmt = conn.createStatement();
    
            // 5、执行sql并返回结果集
            ResultSet rs = stmt.executeQuery(sql);
    
            // 6、处理结果
            while (rs.next()) { // 通过循环取出ResultSet中的所有数据
                System.out.print("id:" + rs.getInt("id") + "\t");
                System.out.print("name:" + rs.getString("name") + "\t");
                System.out.print("salary:" + rs.getInt("salary") + "\t");
                System.out.println();
            }
        
           /*
                // 进行读取一次 判断是否有数据
                if (rs.next()) {
                    System.out.println("id:" + rs.getInt(1) );
                }
            */
            
    
            // 7、释放资源
            rs.close();
            stmt.close();
            conn.close();
        }
    }
    
    • 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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GwzQqA36-1669516492155)(JDBC.assets/image-20220719075752992.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e9k9kjNx-1669516492155)(JDBC.assets/image-20220719075603999.png)]


    5. PreparedStatement


    5.1 SQL注入问题

    通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。

    示例:模拟sql注入问题

    数据库SQL:

    CREATE TABLE `user`  (
      `id` int NOT NULL AUTO_INCREMENT COMMENT 'ID',
      `username` varchar(20) NOT NULL COMMENT '用户名',
      `password` varchar(20) NOT NULL COMMENT '密码',
      PRIMARY KEY (`id`)
    );
    
    insert into user(username,password) values('张三','123');
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    db.properties:

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://127.0.0.1:3306/jdbc_test?useSSL=false&useUniCode=true&characterEncoding=utf-8
    user=root
    password=123456
    
    • 1
    • 2
    • 3
    • 4

    Java程序:

    /**
     * 模拟SQL注入问题
     *
     * @author 白豆五
     * @version 2022/7/19 8:14
     * @since JDK8
     */
    public class JDBC_SQLinjection {
        /*
            功能:实现用户登录
            需求:接收用户输入的用户名和密码;
                验证用户名和密码;
                返回给用户提示信息
         */
        public static void main(String[] args) {
            Map<String, String> userLoginInfo = initUI();
            boolean loginSuccess  = login(userLoginInfo);
            System.out.println(loginSuccess?"登录成功":"用户名或密码有误");
    
        }
    
        /**
         * @param userLoginInfo 用户登录信息
         * @return false表示失败, true表示成功
         */
        private static boolean login(Map<String, String> userLoginInfo) {
            Connection conn = null;
            Statement stmt = null;
            ResultSet rs = null;
            // 定义一个红绿灯表示用户登录和失败
            boolean loginSuccess = false;
    
            try {
                // 创建配置类对象
                Properties properties = new Properties();
                // 通过类加载器获取类路径下的db.properties这个资源
                InputStream is = ClassLoader.getSystemResourceAsStream("db.properties");
                // 加载资源到配置类中
                properties.load(is);
                // 关闭流
                is.close();
    
                // 通过属性获取配置文件里的书
                String driver = properties.getProperty("driver");
                String url = properties.getProperty("url");
                String user = properties.getProperty("user");
                String password = properties.getProperty("password");
    
    
                // 注册驱动
                Class.forName(driver);
                // 获取连接
                conn = DriverManager.getConnection(url, user, password);
                // 获取数据库操作对象
                stmt = conn.createStatement();
                // 定义sql
                String sql = "select * from user where username='" + userLoginInfo.get("username")
                        + "' and password='" + userLoginInfo.get("password") + "'";
    
                // 执行sql
                rs = stmt.executeQuery(sql);
    
                // 如果结果集中有数据表示登录成功
                if (rs.next()) {
                    loginSuccess = true;
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 是否资源
                close(rs,stmt,conn);
            }
            return loginSuccess;
    
        }
    
        /**
         * 初始化界面
         *
         * @return 用户名和密码等信息
         */
        public static Map<String, String> initUI() {
            Scanner sc = new Scanner(System.in);
            System.out.println("用户名:");
            String username = sc.nextLine();
            System.out.println("密码:");
            String password = sc.nextLine();
            // 定义一个Map,存储用户信息
            Map<String, String> userLoginInfo = new HashMap<>();
            userLoginInfo.put("username", username);
            userLoginInfo.put("password", password);
            return userLoginInfo;
        }
    
        /**
         * 释放资源
         *
         * @param rs   结果集对象
         * @param stmt 执行sql的对象
         * @param conn 数据库连接对象
         */
        public static void close(ResultSet rs, Statement stmt, Connection conn) {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
                if (conn != null) {
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    • 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
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u8V33nsK-1669516492155)(JDBC.assets/image-20220719085344240.png)]

    分析:只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。

    解决方案:

    1、可以利用正则表达式来防止用户输入一些非法字符,例如(‘=’,‘or’等)

    2、将Statment换成PreparedStatement(预编译的数据库操作对象);

    PreparedStatement的原理:首先将SQL语句传入PreparedStatement中,然后把传入到SQL语句中的参数用?(占位符)来代替,然后该SQL语句会进行检查和编译(让mysql操作,耗时操作),之后将获取的参数通过PreparedStatement中的set()方法传入编译后的SQL语句中,这样SQL语句就会先被编译再进行传值,最后在执行一下,就解决了SQL注入问题。

    示例:使用PreparedStatement解决SQL注入问题

    /**
     * 使用PreparedStatement解决SQL注入问题
     *
     * @author 白豆五
     * @version 2022/7/19 8:14
     * @since JDK8
     */
    public class JDBC_SQLinjection2 {
        /*
            功能:实现用户登录
            需求:接收用户输入的用户名和密码;
                验证用户名和密码;
                返回给用户提示信息
         */
        public static void main(String[] args) {
            Map<String, String> userLoginInfo = initUI();
            boolean loginSuccess  = login(userLoginInfo);
            System.out.println(loginSuccess?"登录成功":"用户名或密码有误");
    
        }
    
        /**
         * @param userLoginInfo 用户登录信息
         * @return false表示失败, true表示成功
         */
        private static boolean login(Map<String, String> userLoginInfo) {
            Connection conn = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            // 定义一个红绿灯表示用户登录和失败
            boolean loginSuccess = false;
    
            try {
                // 创建配置类对象
                Properties properties = new Properties();
                // 通过类加载器获取类路径下的db.properties这个资源
                InputStream is = ClassLoader.getSystemResourceAsStream("db.properties");
                // 加载资源到配置类中
                properties.load(is);
                // 关闭流
                is.close();
    
                // 通过属性获取配置文件里的书
                String driver = properties.getProperty("driver");
                String url = properties.getProperty("url");
                String user = properties.getProperty("user");
                String password = properties.getProperty("password");
    
    
                // 注册驱动
                Class.forName(driver);
                // 获取连接
                conn = DriverManager.getConnection(url, user, password);
    
                // 定义sql
                String sql = "select * from user where username=? and password=?";
                // 获取数据库操作对象
                ps = conn.prepareStatement(sql); // 先编译后传值
                // 给占位符传值
                ps.setString(1,userLoginInfo.get("username"));
                ps.setString(2,userLoginInfo.get("password"));
    
    
                // 执行sql
                rs = ps.executeQuery();
    
                // 如果结果集中有数据表示登录成功
                if (rs.next()) {
                    loginSuccess = true;
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                // 是否资源
                close(rs,ps,conn);
            }
            return loginSuccess;
    
        }
    
        /**
         * 初始化界面
         *
         * @return 用户名和密码等信息
         */
        public static Map<String, String> initUI() {
            Scanner sc = new Scanner(System.in);
            System.out.println("用户名:");
            String username = sc.nextLine();
            System.out.println("密码:");
            String password = sc.nextLine();
            // 定义一个Map,存储用户信息
            Map<String, String> userLoginInfo = new HashMap<>();
            userLoginInfo.put("username", username);
            userLoginInfo.put("password", password);
            return userLoginInfo;
        }
    
        /**
         * 释放资源
         *
         * @param rs   结果集对象
         * @param ps 执行sql的预编译对象
         * @param conn 数据库连接对象
         */
        public static void close(ResultSet rs, PreparedStatement ps, Connection conn) {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (ps != null) {
                    ps.close();
                }
                if (conn != null) {
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    }
    
    • 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
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5iS3g9xD-1669516492156)(JDBC.assets/image-20220719090611974.png)]


    5.2 PreparedStatement

    • PreparedStatement:预编译的SQL执行对象。

    • 作用:预编译SQL并执行SQL语句

    1、获取PreparedStatement 对象

    // SQL语句中的参数值,使用?占位符替代
    String sql = "select * from user where username = ? and password = ?";
    
    //通过Connection对象获取,并传入对应的sql语句,然后进行预编译操作
    PreparedStatement pstmt = conn.prepareStatement(sql);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2、设置参数值

    ps.setXxx(参数1,参数2) //给?赋值 
        Xxx:数据类型; 如 ps.setInt (参数1,参数2)、ps.setString (参数1,参数2)
    	设置参数:
    		参数1: ?的位置编号,从1开始  (index)
            参数2: ?的值               (value)  
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3、执行SQL

    ps.ecuteUpdate(); // 不需要再传递sql
    
    • 1

    5.3 PreparedStatement的CRDU操作

    import org.junit.Test;
    
    import java.sql.*;
    
    /**
     * @ClassName: JDBCTest05
     * @Description: 使用PreparedStatement完成增删改查功能
     * @Author: baidou
     * @Date: 2022/2/2 09:06
     * Version: 1.0
     */
    public class JDBCTest05 {
    
        Connection conn;
        PreparedStatement pstmt;
    
        /**
         * 获取数据库连接对象
         *
         * @return connection
         * @throws SQLException, ClassNotFoundException
         */
        public static Connection getConnection() throws SQLException, ClassNotFoundException {
            Class.forName("com.mysql.jdbc.Driver");
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbctest?useSSL=false&useUniCode=true&characterEncoding=utf-8", "root", "123456");
            return connection;
        }
    
        /**
         * 获取数据库的预处理对象
         *
         * @param connection 数据库连接对象
         * @param sql        我们传入的sql语句
         * @param obj        占位符的参数
         * @return
         * @throws SQLException
         */
        public static PreparedStatement getPreparedStatement(Connection connection, String sql, Object[] obj) throws SQLException {
            PreparedStatement pstmt = connection.prepareStatement(sql);
            if (obj != null) {
                for (int i = 0; i < obj.length; i++) {
                    pstmt.setObject(i + 1, obj[i]);
                }
            }
            return pstmt;
        }
    
        /**
         * 释放资源
         *
         * @param rs
         * @param ps
         * @param conn
         * @throws SQLException
         */
        public static void close(ResultSet rs, PreparedStatement ps, Connection conn) throws SQLException {
            if (rs != null) {
                rs.close();
            }
            if (ps != null) {
                ps.close();
            }
            if (conn != null) {
                conn.close();
            }
    
        }
    
        @Test
        public void add() throws SQLException, ClassNotFoundException {
            conn = JDBCTest05.getConnection();
            String sql = "insert into user(name,password,vocation) values(?,?,?)";
            Object[] obj = {"大灰狼", "2333", "战士"};
            pstmt = JDBCTest05.getPreparedStatement(conn, sql, obj);
            int count = pstmt.executeUpdate();
            System.out.println(count > 0 ? "添加成功" : "添加失败");
            JDBCTest05.close(null, pstmt, conn);
        }
    
        @Test
        public void delete() throws SQLException, ClassNotFoundException {
            conn = JDBCTest05.getConnection();
            String sql = "delete from user where id=?";
            pstmt = JDBCTest05.getPreparedStatement(conn, sql, new Object[]{4});
            int count = pstmt.executeUpdate();
            System.out.println(count > 0 ? "删除成功" : "删除失败");
            JDBCTest05.close(null, pstmt, conn);
        }
    
        @Test
        public void update() throws SQLException, ClassNotFoundException {
            conn = JDBCTest05.getConnection();
            String sql = "update user set name=?,password=? where name=?";
            Object[] obj = {"铠甲勇士", "0000", "zhangsan"};
            pstmt = JDBCTest05.getPreparedStatement(conn, sql, obj);
            int count = pstmt.executeUpdate();
            System.out.println(count > 0 ? "修改成功" : "修改失败");
            JDBCTest05.close(null, pstmt, conn);
        }
    
        @Test
        public void list() throws SQLException, ClassNotFoundException {
            conn = JDBCTest05.getConnection();
            String sql = "select * from user";
            pstmt = JDBCTest05.getPreparedStatement(conn, sql, null);
            ResultSet rs = pstmt.executeQuery();
            while (rs.next()) {
                System.out.println("id:" + rs.getObject(1) + "\t用户名:" + rs.getObject(2) + "\t密码:" + rs.getObject(3) + "\t职业:" + rs.getObject(4));
            }
            JDBCTest05.close(rs, pstmt, conn);
        }
    }
    
    • 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
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112

    扩展:通过日志查看PreparedStatement原理

    1、开启预编译功能

    我们之前使用PreparedStatement只是预防SQL注入问题,并没有达到预编译的效果,如果想开启预编译功能在数据库url后面拼接如下参数即可。(这样sql模板相同,会提升性能效率)

    useServerPrepStmts=true
    
    • 1

    在这里插入图片描述


    2、在my.ini中配置MySQL执行日志(重启mysql服务后生效)

    log-output=FILE
    general-log=1
    general_log_file="D:\mysql.log"
    slow-query-log=1
    slow_query_log_file="D:\mysql_slow.log"
    long_query_time=2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    • 执行SQL语句,查看 D:\mysql.log 文件。

    示例:查看preparedstatment执行原理

    sql脚本:

    use test;
    
    create table account(
    	id int primary key auto_increment,
    	name varchar(20),
    	money double
    );
    
    insert into account values(null,'张三',1000);
    insert into account values(null,'李四',1000);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    测试代码:

    package com.baidou.jdbc;
    
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    
    /**
     * @author 白豆五
     * @version 2022/11/28 22:29
     * @since JDK8
     */
    public class JDBCTest1 {
        public static void main(String[] args) throws Exception {
            // 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
    
            // 获取数据库连接对象
            String url = "jdbc:mysql:///test?useSSL=false&useServerPrepStmts=true";
            String username = "root";
            String password = "123456";
            Connection conn = DriverManager.getConnection(url, username, password);
            // System.out.println(conn);
    
            // 定义SQL语句
            // 查看指定用户的账户信息
            String sql = "select * from  account where name=?";
    
            // 获取预编译对象PrepareStatement
            PreparedStatement pstmt = conn.prepareStatement(sql);//预编译
            pstmt.setString(1, "张三");
    
            // 延迟加载方便查看日志的预编译结果
            Thread.sleep(1000);
    
            // 执行sql并返回结果集
            ResultSet rs = pstmt.executeQuery();
    
            // 判断指定的账户信息是否存在
            if (rs.next()){
                System.out.println("姓名:"+rs.getString("name")+"\t余额:"+rs.getDouble("money"));
            }else {
                System.out.println("账户信息不存在");
            }
    
            System.out.println("------------------");
    
    
            // sql模板相同,就不用再次进行预编译了
            pstmt.setString(1, "李四五");
            rs = pstmt.executeQuery();
    
            // 判断指定的账户信息是否存在
            if (rs.next()){
                System.out.println("姓名:"+rs.getString("name")+"\t余额:"+rs.getDouble("money"));
            }else {
                System.out.println("账户信息不存在");
            }
    
    
        }
    }
    
    
    • 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

    在这里插入图片描述

    在这里插入图片描述

    总结:

    • 在获取PreparedStatement对象时,将sql语句发送给mysql服务器进行检查,编译(这些步骤很耗时)
    • 执行时就不用再进行这些步骤了,速度更快。
    • 如果sql模板一样,则只需要进行一次检查、编译。

    四、数据库连接池


    1. 数据库连接池简介


    • 数据库连接池,它就是个容器,用来负责分配、管理数据库连接(Connection)。
    • 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。
    • 释放空闲时间超过最大空闲时间的数据库连接,避免因为没有释放数据库连接而引起的数据库连接遗漏问题。
    • 使用数据库连接池的好处:
      • 资源重用。
      • 提升系统响应速度。
      • 避免数据连接遗漏。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gWeTUphG-1669516492156)(JDBC.assets/image-20220719095350923.png)]


    数据库连接池的实现

    1、标准接口:javax.sql.DataSource

    • 官方(SUN公司)为数据库连接池提供了一套标准接口,由第三方组织实现此接口。
    • 核心方法:Connection getConnection(),获取连接。

    在这里插入图片描述


    2、常见的数据库连接池:

    • DBCP
    • C3P0
    • Druid
    • HikariCP,SpringBoot内置的连接池。

    2. Durid


    • Druid(德鲁伊),是阿里开源的数据库连接池项目(文少)。
    • 项目地址:https://github.com/alibaba/druid
    • 优点:功能强大、性能优秀、是Java语言最好的数据库连接池之一。

    Druid使用步骤:

    1、导入jar包 druid-1.1.12.jar。

    2、定义配置文件。

    3、加载配置文件。

    4、获取数据库连接池对象。

    5、获取连接。


    druid.properties配置文件:

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/jdbc_test?useSSL=false&useServerPrepStmts=true
    username=root
    password=123456
    # 初始化连接数量
    initialSize=5
    # 最大连接数
    maxActive=10
    # 最大等待时间
    maxWait=3000
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    测试代码:

    package com.baidou.druid;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.io.FileInputStream;
    import java.sql.Connection;
    import java.util.Properties;
    
    /**
     * Druid的使用
     *
     * @author 白豆五
     * @version 2022/7/19 10:28
     * @since JDK8
     */
    public class DruidDemo {
        public static void main(String[] args) throws Exception {
            //加载properties配置文件
            Properties prop = new Properties();
            // System.out.println(System.getProperty("user.dir"));
            prop.load(new FileInputStream("jdbc_demo2/src/druid.properties"));
            // 获取连接池对象
            DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);//工厂模式
            // 获取数据库连接
            Connection conn = dataSource.getConnection();
            System.out.println("conn = " + conn);
            conn.close(); //归还(装饰者模式/代理模式)
        }
    }
    
    • 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

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BjeTZjcH-1669516492157)(JDBC.assets/image-20220719111306421.png)]

  • 相关阅读:
    跟着架构师学习大型网站架构的技术细节:前端架构需要解决的问题
    李宏毅2022机器学习HW4 Speaker Identification上(Dataset &Self-Attention)
    【网络安全】网络安全协议和防火墙
    怎样批量查询网站是否被搜狗收录?批量查询网站搜狗收录的详细教程
    CompletableFuture-通用异步编程
    小程序商城拓展中小商户“线上时间”,系统化思维
    Python数据容器——集合的相关操作
    深入探究RTOS的任务调度
    Lego-Laom算法深度解析
    cario库——C++画图
  • 原文地址:https://blog.csdn.net/qq_46921028/article/details/128061363