找往期文章包括但不限于本期文章中不懂的知识点:
个人主页:我要学编程(ಥ_ಥ)-CSDN博客
所属专栏: MYSQL
目录
前面,我们学习了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官网(驱动包全部在里面);
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编程 的学习之旅就到此结束啦!我们下一期再一起学习吧!