【Java从零到架构师第二季】【07】JDBC FOR MySQL


持续学习&持续更新中…

学习态度:守破离


什么是JDBC

如何通过Java操作数据库

在这里插入图片描述

JDBC是属于JavaSE的一部分

在这里插入图片描述

下载MySQL的JDBC实现

在这里插入图片描述

地址链接:

解释:https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-versions.html
下载:https://dev.mysql.com/downloads/connector/j/

JDBC细节

JDBC——MySQL的url格式

在这里插入图片描述

JDBC版本与JavaSE版本

在这里插入图片描述

注意:从Java SE 6(JDBC4.0)开始我们就不用显示的注册驱动程序了。

JDBC使用步骤

在这里插入图片描述

最基本的JDBC程序——MySQL驱动包6.x之前

public class Main {
    
    

    private static final String DRIVER_MYSQL = "com.mysql.jdbc.Driver";
    private static final String mysqlURL = "jdbc:mysql://localhost:3306/xmg";
    private static final String user = "root";
    private static final String password = "root";

    /* JDBC属于JavaSE的一部分 具体看图示Java.png */
    /* 最基本的JDBC访问MySQL程序 */
    public static void main(String[] args) throws Exception {
    
    
        /* 步骤一: 将Driver注册到DriverManager 加载驱动 */
        // 方式一 推荐使用 装载MySQL驱动类
        Class.forName(DRIVER_MYSQL);
        //  方式二 不推荐使用
        //  DriverManager.registerDriver(new Driver());

        /* 步骤二:利用DriverManager创建数据库连接 获取数据库连接对象 */
        Connection connection = DriverManager.getConnection(mysqlURL, user, password);

        /* 步骤三:利用Connection创建SQL语句对象 */
        Statement statement = connection.createStatement();

        /* 步骤四:执行SQL语句 SQL语句不用加;*/
        final String sql = "INSERT INTO customer (id, name, phone, company_id) VALUES (101, 'lphahaha', '11122223333', 1)";
        statement.execute(sql);

        /* 步骤五:关闭释放资源 */
        statement.close();
        connection.close();
    }

}

也可以使用try-with-resources来简写代码,确保关闭释放资源

    public static void main(String[] args) {
    
    

        try {
    
    
            Class.forName(DRIVER_MYSQL);
        } catch (ClassNotFoundException e) {
    
    
            System.err.println("加载驱动失败!");
            e.printStackTrace();
        }

        // try-with-resources
        try (
                Connection connection = DriverManager.getConnection(mysqlURL, user, password);
                Statement statement = connection.createStatement()
        ) {
    
    
            final String sql = "INSERT INTO customer (id, name, phone, company_id) VALUES (101, 'lphahaha', '11122223333', 1)";
            statement.execute(sql);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }

    }

又因为从Java SE 6(JDBC4.0)开始我们就不用显示的注册驱动程序。

所以代码可以简化如下:

public static void main(String[] args) throws Exception{
    
    
        try (
                Connection connection = DriverManager.getConnection(mysqlURL, user, password);
                Statement statement = connection.createStatement()
        ) {
    
    
            final String sql = "INSERT INTO customer (name, phone, company_id) VALUES ('niuniuniu', '66666666666', 2)";
            statement.execute(sql);
        }
    }

最基本的JDBC程序——MySQL驱动包6.x开始及之后

public class Main {
    
    

    private static final String DRIVER_MYSQL = "com.mysql.cj.jdbc.Driver";
    private static final String mysqlURL = "jdbc:mysql://localhost:3306/xmg?serverTimezone=UTC";
    private static final String user = "root";
    private static final String password = "root";

    public static void main(String[] args) {
    
    
        try {
    
    
            // Java SE 6开始就不用显示加载驱动了
            // 这儿显示加载驱动的目的是为了学习这块的知识点
            Class.forName(DRIVER_MYSQL);
        } catch (ClassNotFoundException e) {
    
    
            System.out.println("加载驱动失败!");
        }

        try (
                Connection connection = DriverManager.getConnection(mysqlURL, user, password);
                Statement statement = connection.createStatement()
        ) {
    
    
            final String sql = "INSERT INTO customer (id, name, phone, company_id) VALUES (104, 'hahaha', '44444444444', 3)";
            statement.execute(sql);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }
 }

Statement常用API

在这里插入图片描述

ResultSet常用API

在这里插入图片描述

注意:

  • 游标刚开始没有指向任何记录,指向的是第一条记录之前的东西,当调用next方法之后,它才会指向第一条记录。

  • 使用resultSet.getXXX(int columnIndex)获取数据时,columnIndex是从1开始的(columnIndex指的是表的第几列)。

  • 使用resultSet.getXXX(String columnLabel)获取数据时,columnLabel不一定是表的列名(如果查询的时候使用了别名,那么此时的columnLabel就是别名)。

使用上述API查询表中的数据

    private static void test3() throws Exception {
    
    
        final String mysqlURL = "jdbc:mysql://localhost:3306/xmg?serverTimezone=UTC";
        final String user = "root";
        final String password = "root";
        final String sql = "SELECT * FROM customer";
        try (
                Connection connection = DriverManager.getConnection(mysqlURL, user, password);
                Statement statement = connection.createStatement();
                ResultSet resultSet = statement.executeQuery(sql)
        ) {
    
    
            while (resultSet.next()) {
    
    
                System.out.println(
                        "name : " + resultSet.getString("name") + " ;" +
                        "phone : " + resultSet.getString("phone") + " ;" +
                        "company id : " + resultSet.getInt("company_id")
                );
            }
        }
    }

使用上述API查询表中的数据(优化版)

在向数据库插入某个记录的时候,有可能该记录的某一列上的数据不存在,因此就没有向该列插入数据,导致该列的数据为NULL。

如果是这种情况的话,我们查询数据的时候就不应该使用基本类型,而是应该使用对象数据类型(假设表中某个INT类型的字段的数据为NULL,那么直接使用resultSet.getInt()的话,返回值会是0),而ResultSet又没有提供resultSet.getInteger()之类的API,因此我们可以使用resultSet.getObject(),查询出数据后可以进行判空操作,判空操作之后再继续下一步操作。

    private static void test4() throws Exception {
    
    
        final String mysqlURL = "jdbc:mysql://localhost:3306/xmg?serverTimezone=UTC";
        final String user = "root";
        final String password = "root";
        final String sql = "SELECT company_id myCpid FROM customer";
        try (
                Connection connection = DriverManager.getConnection(mysqlURL, user, password);
                Statement statement = connection.createStatement();
                ResultSet resultSet = statement.executeQuery(sql)
        ) {
    
    
            while (resultSet.next()) {
    
    
                Object myCpid = resultSet.getObject("myCpid");
                if (myCpid != null) {
    
    
                // int id = (Integer) myCpid;
                // 推荐使用对象类型
                    Integer id = (Integer) myCpid; 
                    // 执行相应的操作
                    // ......
                }
            }
        }
    }

举一个SQL注入问题的例子(使用Statement)

① 建表 & 插入数据:

DROP TABLE IF EXISTS user;
CREATE TABLE user (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name varchar(20) NOT NULL UNIQUE,
  pswd varchar(20) DEFAULT NULL
);

INSERT INTO user (name, pswd) VALUES ('123', '123');
INSERT INTO user (name, pswd) VALUES ('lpruoyu', 'lpruoyu');
INSERT INTO user (name, pswd) VALUES ('root', 'root');

② 编写Java代码(使用Statement来进行查询):

    private static void login(String name, String pswd) throws Exception {
    
    
        final String mysqlURL = "jdbc:mysql://localhost:3306/xmg?serverTimezone=UTC";
        final String user = "root";
        final String password = "root";
        final String sql = "SELECT * FROM user WHERE name = '" + name + "' AND pswd = '" + pswd + "'";
        try (
                Connection connection = DriverManager.getConnection(mysqlURL, user, password);
                Statement statement = connection.createStatement();
                ResultSet resultSet = statement.executeQuery(sql)
        ) {
    
    
            // 返回结果只可能有1条或者0条
            if (resultSet.next()) {
    
     // 返回值有一条记录 找到该用户
                System.out.println("登录成功!");
            } else {
    
     // 没有返回值 没有找到该用户
                System.out.println("登录失败,用户名或密码不正确!");
            }
        }
    }

如果用户将pswd传入' OR '1' = '1的话,就相当于执行下列的语句(此时name的值是什么已经无所谓了,我就什么都没写):

在这里插入图片描述

很明显,这样做的话,使用上述Java代码进行登录,每次都会登录成功(由于AND 优先级高于 OR,因此OR前面的name = '' AND pswd = ''是一个整体AOR后面的'1' = '1'是一个整体B,这时WHERE后面就有两部分组成:A OR B,而B是恒成立的,因此每次都会查询出结果。),这就导致了SQL Injection(SQl注入)问题,程序就会产生漏洞。

那么该如何修复这种问题呢?使用PreparedStatement!

PreparedStatement

在这里插入图片描述

使用PreparedStatement来优化上述例子,防止SQL Injection问题:

    private static void login(String name, String pswd) throws Exception {
    
    
        final String mysqlURL = "jdbc:mysql://localhost:3306/xmg?serverTimezone=UTC";
        final String user = "root";
        final String password = "root";
        final String sql = "SELECT * FROM user WHERE name = ? AND pswd = ?";
        try (
                Connection connection = DriverManager.getConnection(mysqlURL, user, password);
                PreparedStatement preparedStatement = connection.prepareStatement(sql);
        ) {
    
    
            preparedStatement.setString(1, name);
            preparedStatement.setString(2, pswd);
            ResultSet resultSet = preparedStatement.executeQuery();
            if (resultSet.next()) {
    
    
                System.out.println("登录成功!");
            } else {
    
    
                System.out.println("登录失败!");
            }

            // ResultSet没法使用try-with-resources
            // 所以ResultSet需要关闭
            resultSet.close();
        }
    }

参考

李明杰: Java从0到架构师②JavaEE技术基石.


本文完,感谢您的关注支持!


Guess you like

Origin blog.csdn.net/weixin_44018671/article/details/120798101