自学内容网 自学内容网

初始MYSQL数据库(8)—— JDBC编程

找往期文章包括但不限于本期文章中不懂的知识点:

个人主页:我要学编程(ಥ_ಥ)-CSDN博客

所属专栏: MYSQL

目录

JDBC的概念 

JDBC的使用

加载驱动包

建立连接

创建 statement 对象

定义并执行SQL语句 

处理结果集

关闭资源 

SQL注入

优化后的JDBC

JDBC编程的两种方式对比


前面,我们学习了MySQL数据库中SQL语句的基础语法并且在命令行上面也进行了一些实际的操作。现在我们就来学习使用IDEA去编写SQL语句来操作数据库。

JDBC的概念 

JDBC(Java DataBase Connectivity,Java数据库连接)是Java程序和数据库之间的桥梁,包含
了一套Java定义的用于执行SQL语句的接口,使开发者能够编写数据库的程序。JDBC的主要作用是:与数据库建立连接、发送SQL语句和处理数据库执行结果。关系示意图:

从上面我们知道了,JDBC其实就是一套Java源生的。而其的实现也与我们无关,是由数据库厂商自己提供的。我们只需要去使用即可。

接下来我们就来使用JDBC去操作数据库。

JDBC的使用

JDBC的使用可以大致概括为以下几个方面:

1、加载数据库厂商提供的驱动包。即实现了JDBC的接口

2、建立数据库连接

3、创建statement对象

4、编写SQL语句,并通过statement对象去执行SQL语句

5、接收结果集或者返回值

6、关闭资源

加载驱动包

加载驱动包的过程可以简单理解为在手机上安装一个软件的过程。 而安装软件的过程如下所示:

1、打开应用商店;

2、去应用商店里面找到该软件;

3、下载该软件。

4、安装该软件,在运行即可。

同理加载驱动包也是三个步骤:

1、打开Maven官网(驱动包全部在里面);

Maven官网,点我

2、找到与自己电脑中数据库版本相对应数据库;

那问题来了,怎么判断自己是哪个版本呢?

按 win + R 打开命令行,然后输入 mysql -uroot -p,接着摁下回车键,再输入密码,成功打开MySQL之后,就输入 select version(); 去查询此时正在运行的MySQL的版本是多少。

如果你和我一样是MySQL8.0.39的话,那么就是去第一个结果中找到MySQL8.3即可。

3、在该目录下,来到Maven所在目录,点击框内英文即可。 

 4、将复制的内容粘贴到Maven工程中。

这里首先得创建一个Maven工程。

然后只需要套个框架,在框架内粘贴我们复制的内容即可。 

 接下来就是正式的加载驱动包了,正如运行程序。

现在就已经将驱动包加载完成啦!下面就是要建立连接了。

建立连接

// 2. 获取数据库连接
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8" +
                    "&allowPublicKeyRetrieval=true&useSSL=false", "root", "123456");;

这里是固定写法:

即修改 3306/ 后面的值,即对应的数据库。root 就是用户名,123456 就是mysql的密码。 

创建 statement 对象

// 3. 创建Statement对象
Statement statement = connection.createStatement();

定义并执行SQL语句 

// 4. 定义SQL并执行SQL语句
System.out.print("请输入学生姓名:");
Scanner scanner = new Scanner(System.in);
// 接收用户的输入
String name = scanner.next();
// 把查询的列详细写出来
String sql = "select id, name, age, gender from student where name = '" + name + "'";
// 5. 执行SQL,获取查询结果
resultSet = statement.executeQuery(sql);

处理结果集

// 6. 对结果集进行遍历,获取数据
// 如果一下条有记录,返回true,没有则返回false
while (resultSet.next()) {
    // 获取学生的信息
    long stuId = resultSet.getLong(1); // 这里的1是指第一列
    String stuName = resultSet.getString(2);
    int stuAge = resultSet.getInt(3);
    byte stuGender = resultSet.getByte(4);
    System.out.println(MessageFormat.format("学生编号={0}, 学生姓名={1}, 学生年龄={2}, 
        学生性别={3}",stuId, stuName, stuAge, stuGender));
}

关闭资源 

// 依次释放资源,关闭连接
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();
    }
}

下面是综合的代码(包括捕获异常):

import java.sql.*;
import java.text.MessageFormat;
import java.util.Scanner;

public class JDBC_Demo1 {
    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/java?characterEncoding=utf8" +
                    "&allowPublicKeyRetrieval=true&useSSL=false", "root", "123456");
            // 3. 创建Statement对象
            statement = connection.createStatement();
            // 4. 定义SQL并执行SQL语句
            System.out.print("请输入学生姓名:");
            Scanner scanner = new Scanner(System.in);
            // 接收用户的输入
            String name = scanner.next();
            String sql = "select id, name, age, gender from student where name = '" + name + "'";
            // 5. 执行SQL,获取查询结果
            resultSet = statement.executeQuery(sql);
            // 6. 对结果集进行遍历,获取数据
            // 如果一下条有记录,返回true,没有则返回false
            while (resultSet.next()) {
                // 获取学生的信息
                long stuId = resultSet.getLong(1);
                String stuName = resultSet.getString(2);
                int stuAge = resultSet.getInt(3);
                byte stuGender = resultSet.getByte(4);
                System.out.println(MessageFormat.format("学生编号={0}, 学生姓名={1}, 学生年龄={2}, 学生性别={3}",
                        stuId, stuName, stuAge, stuGender));
            }

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

注意:try块 中定义的变量,其作用域只是在try块中,只有在全局的变量,才能在try-catch 中使用,并且在finally块中也是可以使用。(全局只是相对try-catch和finally而言的)

效果演示:

JDBC:

命令行:

上面这种方法虽然的确可以操作数据库,但是从性能来讲,还是比较差的。因为DriverManager每次调用getConnection方法都会初始化一个新的连接,使用完成后会关闭真实连接,导致资源浪费。因此 DataSource就闪亮登场了。DataSource使用了连接池的技术,会在初始化时创建一定数量的数据库连接,这些连接可以重复使用,关闭时并不是真正关闭连接,而是将连接归还给连接池,以供后续使用,有效地提高资源利用率和和性能。

SQL注入

Statement 用于执行静态SQL语句并返回执行结果,由于只能执行静态语句,所以这里会有一个问题,假设一个语句中需要动态的参数,比如where子句中的条件,那么只能通过字符串拼接的方式组装完成的SQL语句。就和我们上面写的一样,但如果此时有人写了一个必然成立的代码呢?

JDBC进行的检查就是将输入的SQL语句中的所有空格给去掉,从而进行语法检查。 

 上面这种情况叫做 SQL注入。

字符串拼接形式构造SQL语句时,如果不处理参数中的特殊字符就会造成SQL注入,这是一个非常
严重的安全性问题。别人可以通过这种手段把数据内容全部盗取,从而对公司、个人造成损失。

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应
用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

既然出现了这么严重的问题,肯定是需要解决的,因此便有了和编译代码的过程一样,进行预编译。在预编译阶段处理SQL注入的问题。

预编译SQL语句对象,SQL语句被预编译并存储在PreparedStatement对象中,可以使用该对象多次执行SQL语句,同时可以解决SQL注入问题。

优化后的JDBC

现在我们来写优化后的代码:

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 JDBC_Demo2 {
    public static void main(String[] args) {
        // 定义mysql数据源对象
        MysqlDataSource mysqlDataSource = new MysqlDataSource();
        // 设置数据连接串
        // url
        mysqlDataSource.setURL("jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8" +
                "&allowPublicKeyRetrieval=true&useSSL=false");
        // 用户名
        mysqlDataSource.setUser("root");
        // 密码
        mysqlDataSource.setPassword("123456");


        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        // 上面是厂商给我们提供的实现类,我们要使用Java层面的数据源接口
        DataSource dataSource = mysqlDataSource; // 向上转型
        try {
            // 从数据源中获取连接
            connection = dataSource.getConnection();
            // 定义执行的SQL
            String sql = "select id, name, age, gender from student where name = ?"; // 使用占位符
            // 对SQL语句进行预编译
            preparedStatement = connection.prepareStatement(sql);
            // 接收输入数据
            System.out.print("请输入学生姓名:");
            Scanner scanner = new Scanner(System.in);
            String name = scanner.next();
            // 用真实数据代替占位符
            preparedStatement.setString(1,name);
            // 执行SQL语句并接收结果集
            resultSet = preparedStatement.executeQuery(); // 这里不要传入SQL了
            while (resultSet.next()) {
                long stuId = resultSet.getLong(1);
                String stuName = resultSet.getString(2);
                int stuAge = resultSet.getInt(3);
                byte stuGender = resultSet.getByte(4);
                System.out.println(MessageFormat.format("学生编号{0},学生姓名{1}, 学生年龄{2}, 学生性别{3}"
                        ,stuId, stuName, stuAge, stuGender));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

上面的代码主要有两个优化的地方:

1、获取数据库连接不再是单独申请,而是在数据源中进行索取。

由于JDBC只是一组接口,因此我们没有办法直接创建数据源对象,只能通过mysql厂商提供的数据源对象来进行向上转型。而前面的获取数据连接时,是每一次都需要写入URL等数据,而数据源只需要获取一次即可,因此我们就是直接通过mysql提供的数据源对象进行设置URL等数据。

2、在优化数据源的基础上,我们还需要对SQL语句进行预编译。因此得先写一个SQL语句,并且不能再是拼接字符串的方式了,所以这里就用到了占位符。然后再让我们自己输入数据去填充占位符,接着就可以直接进行查询了。 

上面我们写的代码都是查询操作,现在我们来写一个简单的新增操作。

大体操作是一致的。

import com.mysql.cj.jdbc.CallableStatement;
import com.mysql.cj.jdbc.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;

public class JDBC_Demo3 {
    public static void main(String[] args) {
        // 定义mysql数据源对象
        MysqlDataSource mysqlDataSource = new MysqlDataSource();
        // 设置数据连接串
        // URL
        mysqlDataSource.setURL("jdbc:mysql://127.0.0.1:3306/java?characterEncoding=utf8" +
                "&allowPublicKeyRetrieval=true&useSSL=false");
        // 用户名
        mysqlDataSource.setUser("root");
        // 密码
        mysqlDataSource.setPassword("123456");

        // 上面是厂商给我们提供的实现类,我们要使用Java层面的数据源接口
        DataSource dataSource = mysqlDataSource;
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            // 建立连接
            connection = dataSource.getConnection();
            // 定义SQL并进行预编译
            String sql = "insert into student values (?,?,?,?)";
            preparedStatement = connection.prepareStatement(sql);
            // 接收输入
            Scanner scanner = new Scanner(System.in);
            System.out.print("请输入新增学生编号:");
            long id = Long.parseLong(scanner.next()); // 不会出现问题
            System.out.print("请输入新增学生姓名:");
            String name = scanner.next();
            System.out.print("请输入新增学生年龄:");
            int age = Integer.parseInt(scanner.next());
            System.out.print("请输入新增学生性别:");
            byte gender = Byte.parseByte(scanner.next());
            // 替换占位符
            preparedStatement.setLong(1,id); // 默认从1开始的
            preparedStatement.setString(2, name);
            preparedStatement.setInt(3,age);
            preparedStatement.setByte(4,gender);
            // 执行SQL语句,并接收结果集
            int line = preparedStatement.executeUpdate(); // 这里返回的是受影响的行数
            if (line == 1) {
                System.out.println("新增成功!");
            } else {
                System.out.println("新增失败!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 依次关闭资源
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

效果演示:

注意:

1、这里之所以全部改用next 方法,是因为其他的方法可能会出现一些预料不到的问题。

2、占位符可以有多个,是从1按照顺序开始的。

JDBC编程的两种方式对比

现在我们就来总结一下JDBC的简单使用过程。

第一种方式(不推荐使用):

1、加载数据库厂商提供的驱动包。注意:一个项目中只需加载一次,就类似于一个手机上面只能安装一个同样的应用程序(虚拟机除外)。

2、建立连接。

3、创建Statement对象

4、通过Statement对象执行SQL语句

5、处理结果集

6、关闭资源。

第二种方式(推荐使用):

1、 创建数据源对象。

        (a):使用数据库厂商提供的数据源,创建对象,设置连接串。

        (b):用Java提供的接口去接收,进行向上转型。

2、建立连接。

3、定义SQL语句,进行预编译。

4、接收真实数据,代替占位符。

5、执行SQL语语,并接收返回值。

6、关闭资源。

第二种方式的优点:

1、主要是针对SQL注入的情况,进行了预编译优化。

2、对于第一种方式频繁的建立连接,关闭资源做了优化:定义一个数据源,每次建立连接都是通其中的某一个建立连接,并且当使用完成之后,也不会真的把资源关闭,而是还给了数据源。

好啦!本期 初始MYSQL数据库(8)—— JDBC编程 的学习之旅就到此结束啦!我们下一期再一起学习吧!


原文地址:https://blog.csdn.net/2301_80854132/article/details/142602454

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!