Java数据库连接——JDBC
目录
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. 加载数据库厂商提供的驱动//通过完全限定名记载指定的类到JVMClass.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开始,有多少个参数就设置多少个值。
//定义要执行的SQLString 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