目录
1、JDBC简介
2、JDBC应用
2.1 建立数据库连接
2.1.1 DriverManager静态方法获取连接
2.1.2 DataSource对象获取
2.2 获取SQL执行对象
2.2.1 SQL注入
2.2.2 Statement(执行静态SQL)
2.2.3 PreparedStatement(预处理的SQL执行对象)
2.3 执行SQL并返回结果
2.4 关闭连接,释放资源
3、示例
1、JDBC简介
JDBC(Java Data Base Connectivity,Java数据库连接)是Java程序和数据库之间的桥梁,包含 了⼀套Java定义的用于执行SQL语句的接口,使开发者能够编写数据库的程序。JDBC 的主要作用是: 与数据库建立连接、发送SQL语句和处理数据库执行结果。
正常来说,访问数据库需要经过以下几个重要步骤:
- 确定数据库服务器的地址,端口号(数据源)
- 建立连接,用户名、密码(不同的数据库以哪种协议建立连接)
- 发生要执行的SQL(以什么样的形式发送——需要考虑编码的格式(协议))
- 接收返回的结果(结果集(select)、受影响的行数(insert、...))(需要考虑一哪种协议解析结果)
- 关闭连接(释放资源,关闭连接)
JDBC是Java平台提供的接口,其中具体的实现是由不同的数据库厂商来完成的,数据库厂商实现完后将其封装在不同的方法中,Java开发者可以使用这些封装好的方法,操作数据库。
JDBC工作原理简洁地概括为:
- 加载驱动
- 建立连接
- 创建Statement(获取SQL执行对象)
- 执行SQL并返回结果
- 处理结果,关闭资源
2、JDBC应用
2.1 建立数据库连接
2.1.1 DriverManager静态方法获取连接
获取数据库连接的第一种方式:以驱动管理类DriverManager获取连接。
注意:该方式获取的是物理连接,即每次运行时都会重新打开一个客户端工具(随用随开)。
举个例子:比如公司中有了一个新业务,老板为完成这个业务招了十个员工,业务完成后立刻把他们炒了。过了一段时间,公司又有了一个新业务,老板又重新招人,完成业务又把他们开了。(随用随招)
因此,该方法效率低下,不推荐。
//1. 加载数据库厂商提供的驱动
//通过完全限定名记载指定的类到JVM
Class.forName("com.mysql.cj.jdbc.Driver");
//2. 获取数据库连接(物理连接)
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test2?characterEncoding=utf8&" +
"allowPublicKeyRetrieval=true&useSSL=false", "root", "111111");
2.1.2 DataSource对象获取
通过数据源DataSource对象获取,一个连接可以执行很多SQL,直到关闭数据源。
这种方式是通过一个连接池去管理很多个连接,当需要SQL执行时,就从连接池里拿出一个连接,用完后返还给连接池。
推荐在实际开发中使用这种方式。
//1. 定义MySQL数据源对象(连接池)
MysqlDataSource mysqlDataSource = new MysqlDataSource();
//2. 设置数据库连接串
mysqlDataSource.setURL("jdbc:mysql://127.0.0.1:3306/test2?characterEncoding=utf8" +
"&allowPublicKeyRetrieval=true&useSSL=false");
//用户
mysqlDataSource.setUser("root");
//密码
mysqlDataSource.setPassword("111111");
//定义JDBC的数据源对象
DataSource dataSource = mysqlDataSource;
//1. 通过数据源获得数据库连接对象
connection = dataSource.getConnection();
2.2 获取SQL执行对象
2.2.1 SQL注入
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应 用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
2.2.2 Statement(执行静态SQL)
Statement是用于执行静态SQL语句并返回执行结果的对象
注意:该方式存在SQL注入的安全问题!!!
如下图:该方式是通过字符串拼接的方式组装完成的SQL语句,字符串拼接形式构造SQL语句时,如果不处理参数中的特殊字符就会造成SQL注入,这是⼀个非常严重的安全性问题。
//定义Statement
Statement statement = null;
//3. 创建Statement对象
statement = connection.createStatement();
//4. 定义SQL并执行SQL语句
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要查询的学生的姓名:");
//接收用户输入
String name = scanner.next();
String sql = "select student_id, sn, name, mail, class_id from student where name = '" + name + "'";
//5. 执行SQL并获取查询结果
resultSet = statement.executeQuery(sql);
2.2.3 PreparedStatement(预处理的SQL执行对象)
预编译SQL语句对象,SQL语句被预编译并存储在PreparedStatement对象中,可以使用该对象多次执行SQL语句,同时可以解决SQL注入问题。
动态参数用占位符 ? 表示。
简单以上文SQL注入的输入为例,该方式直接将用户输入的内容全部当做name的值(利用占位符),直接在数据表中通过输入值来查找相应记录。
注意:使用真实值替换占位符时,使用setString/setLong/....方法,编号从1开始,有多少个参数就设置多少个值。
//定义要执行的SQL
String sql = "select student_id, sn, name, mail, class_id from student where name = ?";
Scanner scanner = new Scanner(System.in);
//2. 获取预处理SQL执行对象
statement = connection.prepareStatement(sql);
//接收用户输入
System.out.println("请输入要查询的学生的姓名:");
String name = scanner.next();
//3. 用真实值替换占位符
statement.setString(1, name);
2.3 执行SQL并返回结果
执行select语句,返回结果集(ResultSet结果集接收):
//定义结果集对象
ResultSet resultSet = null;
//4. 执行SQL,获取结果集对象
resultSet = statement.executeQuery();
//迭代打印结果集
//最初游标位于第⼀⾏之前,调⽤next⽅法将游标移动到下⼀⾏,
//当ResultSet中没有更多的数据⾏时返回false
while (resultSet.next()) {
long stuId = resultSet.getLong(1);
String stuSn = resultSet.getString(2);
String stuName = resultSet.getString(3);
String stuMail = resultSet.getString(4);
long stuClassId = resultSet.getLong(5);
System.out.println(MessageFormat.format("学生编号={0}, 学号={1}, 姓名={2}, 邮箱={3}, 班级编号={4}",
stuId, stuSn, stuName, stuMail, stuClassId));
}
执行update、delete、insert操作,返回所影响的行数:
int row = statement.executeUpdate();
2.4 关闭连接,释放资源
自下而上,依次关闭。
// 依次释放资源,关闭连接
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
3、示例
封装数据库连接、资源关闭方法:
package org.example.utils;
import com.mysql.cj.jdbc.MysqlDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* Created with IntelliJ IDEA.
* Description:封装方法
* User: dings
* Date: 2024-09-21
* Time: 14:53
*/
public class DBUtils {
private static DataSource dataSource = null;
private static MysqlDataSource mysqlDataSource = new MysqlDataSource();
private static String URL = "jdbc:mysql://127.0.0.1:3306/test2?characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false";
private static String USER = "root";
private static String PASSWORD = "111111";
private DBUtils() {}//构造方法私有化
static {
mysqlDataSource.setURL(URL);
mysqlDataSource.setUser(USER);
mysqlDataSource.setPassword(PASSWORD);
dataSource = mysqlDataSource;
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void close(ResultSet resultSet, Statement statement, Connection connection) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
实现记录的插入:
package org.example;
import org.example.utils.DBUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;
/**
* Created with IntelliJ IDEA.
* Description:向表中插入记录
* User: dings
* Date: 2024-09-21
* Time: 15:06
*/
public class Demo03_insert {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement statement = null;
try {
connection = DBUtils.getConnection();
String sql = "insert into student (sn, name, mail, class_id) values (?,?,?,?)";
statement = connection.prepareStatement(sql);
Scanner scanner = new Scanner(System.in);
System.out.println("请输入学号:");
String sn = scanner.next();
System.out.println("请输入姓名:");
String name = scanner.next();
System.out.println("请输入邮箱:");
String mail = scanner.next();
System.out.println("请输入班级编号:");
Long classId = Long.valueOf(scanner.next());
//编号从1开始
statement.setString(1, sn);
statement.setString(2, name);
statement.setString(3, mail);
statement.setLong(4, classId);
int row = statement.executeUpdate();
if(row == 1) {
System.out.println("插入成功!");
}else {
System.out.println("插入失败!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
// 释放资源,关闭连接
DBUtils.close(null, statement, connection);
}
}
}
END