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

ops/2024/10/22 15:30:57/

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

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

所属专栏: MYSQL

目录

JDBC%E7%9A%84%E6%A6%82%E5%BF%B5%C2%A0-toc" style="margin-left:0px;">JDBC的概念 

JDBC%E7%9A%84%E4%BD%BF%E7%94%A8-toc" style="margin-left:0px;">JDBC的使用

加载驱动包

建立连接

创建 statement 对象

定义并执行SQL语句 

处理结果集

关闭资源 

SQL注入

JDBC-toc" style="margin-left:0px;">优化后的JDBC

JDBC%E7%BC%96%E7%A8%8B%E7%9A%84%E4%B8%A4%E7%A7%8D%E6%96%B9%E5%BC%8F%E5%AF%B9%E6%AF%94-toc" style="margin-left:0px;">JDBC编程的两种方式对比


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

JDBC%E7%9A%84%E6%A6%82%E5%BF%B5%C2%A0" style="background-color:transparent;">JDBC的概念 

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

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

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

JDBC%E7%9A%84%E4%BD%BF%E7%94%A8">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工程。

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

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

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

建立连接

java">// 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 对象

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

定义并执行SQL语句 

java">// 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);

处理结果集

java">// 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));
}

关闭资源 

java">// 依次释放资源,关闭连接
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 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,没有则返回falsewhile (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" style="background-color:transparent;">优化后的JDBC

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

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 JDBC_Demo2 {public static void main(String[] args) {// 定义mysql数据源对象MysqlDataSource mysqlDataSource = new MysqlDataSource();// 设置数据连接串// urlmysqlDataSource.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();// 定义执行的SQLString 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语句,并且不能再是拼接字符串的方式了,所以这里就用到了占位符。然后再让我们自己输入数据去填充占位符,接着就可以直接进行查询了。 

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

大体操作是一致的。

java">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();// 设置数据连接串// URLmysqlDataSource.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%E7%BC%96%E7%A8%8B%E7%9A%84%E4%B8%A4%E7%A7%8D%E6%96%B9%E5%BC%8F%E5%AF%B9%E6%AF%94" style="background-color:transparent;">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编程 的学习之旅就到此结束啦!我们下一期再一起学习吧!


http://www.ppmy.cn/ops/118945.html

相关文章

Vue转React中JSX小结

Vue转 React 的过程中,首先需要了解 React 中的 JSX(JavaScript XML)。它在 React 中扮演着类似于 Vue 模板语法的角色。以下是详细而全面的 JSX 总结,帮助你快速上手。 1. 什么是 JSX? JSX介绍 JSX 是一种 JavaScri…

版本发布 | IvorySQL 3.4 发版

[发行日期:2024年9月26日] IvorySQL 3.4基于PostgreSQL 16.4,并修复了多个问题。更多信息请参考文档网站。 >>>新版本体验链接: https://docs.ivorysql.org/cn/ivorysql-doc/v3.4/v3.4/1.html 1 增强功能 >>>PostgreSQL…

毕业设计——springboot+netty+websocket实现一个简单的ChatGpt机器人聊天

作品详情 WebSocket是html5开始浏览器和服务端进行全双工通信的网络技术。在该协议下,与服务端只需要一次握手,之后建立一条快速通道,开始互相传输数据,实际是基于TCP双向全双工,比http半双工提高了很大的性能&#x…

企业微信hook协议接口,群发,标签,客户管理。

服务提供了丰富的API和SDK,可以在企微的功能之上进行应用开发和功能扩展 自建应用可以调用企微hook或协议提供的接口来实现数据交互,可以直接调用hook或协议接口提供的功能来进行消息的发送与接收、用户管理、应用管理等操作,通过接口可以实…

华为源NAT技术与目的NAT技术

1)源NAT对报文源地址进行转换,分为NAT NO-PAT,NAPT,EASY-IP,三元组NAT; (1)NAT NO-PAT原理: no-port address translation:非端口地址转换:只转换地址,不转换端口&…

C++ 数据类型分类

在C中,数据类型可以大致分为内置类型(Built-in Types)、标准库类型(Standard Library Types)和自定义类型(User-Defined Types)三大类。 内置类型(Built-in Types) 内置…

《程序猿之Redis缓存实战 · 字符串类型》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数…

c语言200例 067

大家好,欢迎来到无限大的频道 今天给大家带来的是c语言200例 题目要求: 设计一个共用体类型,使其成员包含多种数据类型,根据不同的数据类型,输出不同的结果 要设计一个共用体(union)类型&…