0x01 前言
JDBC(Java DataBase Connectivity,java 数据库连接)是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成。JDBC 提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
JDBC架构
JDBC API 支持两层和三层处理模型进行数据库访问,但在一般的 JDBC 体系结构由两层组成:
JDBC API:提供了应用程序对 JDBC 的管理连接。
JDBC Driver API:支持 JDBC 管理到驱动器连接。
JDBC API 的使用驱动程序管理器和数据库特定的驱动程序提供透明的连接到异构数据库。
JDBC 驱动程序管理器可确保正确的驱动程序来访问每个数据源。该驱动程序管理器能够支持连接到多个异构数据库的多个并发的驱动程序。
以下是JDBC结构图,它显示了驱动程序管理器方面的 JDBC 驱动程序和 Java 应用程序的位置。
常见的JDBC组件
DriverManager:这个类管理数据库驱动程序的列表。确定内容是否符合从 Java 应用程序使用的通信子协议正确的数据库驱动程序的连接请求。识别 JDBC 在一定子协议的第一个驱动器将被用来建立数据库连接。
Driver: 此接口处理与数据库服务器通信。很少直接直接使用驱动程序(Driver)对象,一般使用 DriverManager 中的对象,它用于管理此类型的对象。它也抽象与驱动程序对象工作相关的详细信息。
Connection:此接口与接触数据库的所有方法。连接对象表示通信上下文,即,与数据库中的所有的通信是通过此唯一的连接对象。
Statement:可以使用这个接口创建的对象的SQL语句提交到数据库。一些派生的接口接受除执行存储过程的参数。
ResultSet:这些对象保存从数据库后,执行使用
Statement
对象的 SQL 查询中检索数据。它作为一个迭代器,可以通过移动它来检索下一个数据。SQLException:这个类用于处理发生在数据库应用程序中的任何错误。
0x02 创建简单的一个 JDBC
构建 JDBC 应用程序涉及以下六个步骤:
导入包:需要包含包含数据库编程所需的 JDBC 类的包。大多数情况下,使用
import java.sql.*
就足够了。注册 JDBC 驱动程序:需要初始化驱动程序,以便可以打开与数据库的通信通道。
打开一个连接:需要使用
DriverManager.getConnection()
方法创建一个Connection
对象,它表示与数据库的物理连接。执行查询:需要使用类型为
Statement
的对象来构建和提交 SQL 语句到数据库。从结果集中提取数据:需要使用相应的
ResultSet.getXXX()
方法从结果集中检索数据。清理环境:需要明确地关闭所有数据库资源,而不依赖于 JVM 的垃圾收集。
口诀:(贾琏欲执事)。
具体实现如下:
1. 导入包
在程序中包含数据库编程所需的 JDBC 类。大多数情况下,使用 import java.sql.*
就足够了,如下所示。
//STEP 1. Import required packages
import java.sql.*;
2. 注册JDBC驱动程序
需要初始化驱动程序,这样就可以打开与数据库的通信。以下是代码片段实现这一目标。
//STEP 2: Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
3. 打开一个连接
使用 DriverManager.getConnection()
方法来创建一个 Connection
对象,它代表一个数据库的物理连接,如下所示。
//STEP 3: Open a connection
//Database credentials
static final String USER = "root";
static final String PASS = "pwd123456";
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL, USER, PASS);
4. 执行一个查询
需要使用一个类型为 Statement
或 PreparedStatement
的对象,并提交一个 SQL 语句到数据库执行查询。如下:
//STEP 4: Execute a query
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql;
sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);
如果要执行一个SQL语句:UPDATE
,INSERT
或 DELETE
语句,那么需要下面的代码片段:
//STEP 4: Execute a query
System.out.println("Creating statement...");
stmt = conn.createStatement();
String sql;
sql = "DELETE FROM Employees";
ResultSet rs = stmt.executeUpdate(sql);
5. 从结果集中提取数据
这一步中演示如何从数据库中获取查询结果的数据。可以使用适当的 ResultSet.getXXX()
方法来检索的数据结果如下。
//STEP 5: Extract data from result set
while(rs.next()){
//Retrieve by column name
int id= rs.getInt("id");
int age = rs.getInt("age");
String first = rs.getString("first");
String last = rs.getString("last");
//Display values
System.out.print("ID: " + id);
System.out.print(", Age: " + age);
System.out.print(", First: " + first);
System.out.println(", Last: " + last);
}
6. 清理环境资源
在使用 JDBC 与数据交互操作数据库中的数据后,应该明确地关闭所有的数据库资源以减少资源的浪费,对依赖于 JVM 的垃圾收集如下。
//STEP 6: Clean-up environment
rs.close();
stmt.close();
conn.close();
0x03 JDBC Statements, PreparedStatement 和 CallableStatement 语句
接口 | 推荐使用 |
---|---|
Statement | 用于对数据库进行通用访问,在运行时使用静态SQL语句时很有用。Statement 接口不能接受参数。 |
PreparedStatement | 当计划要多次使用SQL语句时使用。PreparedStatement 接口在运行时接受输入参数。 |
CallableStatement | 当想要访问数据库存储过程时使用。CallableStatement 接口也可以接受运行时输入参数。 |
处理 Statement 对象
创建 Statement 对象
在使用 Statement
对象执行 SQL 语句之前,需要使用 Connection
对象的 createStatement()
方法创建一个 Statement
对象。
在创建 Statement
对象后,可以使用它来执行一个SQL语句,它有三个执行方法可以执行。
boolean execute (String SQL)
:如果可以检索到ResultSet
对象,则返回一个布尔值true
; 否则返回false
。使用此方法执行 SQL DDL 语句或需要使用真正的动态 SQL,可使用于执行创建数据库,创建表的 SQL 语句等等。int executeUpdate (String SQL)
:返回受 SQL 语句执行影响的行数。使用此方法执行预期会影响多行的 SQL 语句,例如:INSERT
,UPDATE
或DELETE
语句。ResultSet executeQuery(String SQL)
:返回一个ResultSet
对象。 当您希望获得结果集时,请使用此方法,就像使用SELECT
语句一样。
关闭 Statement 对象
操作结束后,应关闭 Statement
对象。就像关闭一个 Connection
对象一样,以保存数据库资源一样,由于同样的原因,还应该关闭 Statement
对象。
一个简单的调用 close()
方法将执行该作业(工作)。 如果先关闭 Connection
对象,它也会关闭 Statement
对象。但是,应该始终显式关闭 Statement
对象,以确保正确的清理顺序。
Statement stmt = null;
try {
stmt = conn.createStatement( );
// TODO
}
catch (SQLException e) {
// TODO
}
finally {
stmt.close();
}
Statement 对象具体实现
在使用 Statement
对象执行 SQL 语句之前,需要使用 Connection
对象的 createStatement()
方法创建一个 Statement
对象,然后调用上述处理 Statement
类方法提交执行 SQL 。
Statement 操作 DDL
使用 Satemtnt 类创建数据库表。
@Test
public void testDDL() {
String sql = "CREATE TABLE student(id BIGINT AUTO_INCREMENT, name VARCHAR(20), age INT, PRIMARY KEY(id), intro VARCHAR(64))";
Connection connection = null; // 数据库连接对象
Statement statement = null; // 用于执行静态 SQL 语句并返回它所生成结果的对象
try {
Class.forName("com.mysql.jdbc.Driver"); // 加载注册驱动
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root"); // 创建 MySQL 连接对象
statement = connection.createStatement(); // 创建 Statement 对象,用于提交和接收 MySQL
int row = statement.executeUpdate(sql); // 提交 SQL 语句,并接收执行结果
System.out.println("数据库修改行数:" + row); // 执行创建表 SQL 影响行数为 0
} catch (Exception e) { // 释放资源
e.printStackTrace();
} finally {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Java7 新特性创建数据库表
@Test
public void testDDL_JAVA7() {
String sql = "CREATE TABLE student(id BIGINT AUTO_INCREMENT, name VARCHAR(20), age INT, PRIMARY KEY(id))";
try {
Class.forName("com.mysql.jdbc.Driver");
try (Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root"); Statement st = conn.createStatement();) {
st.executeUpdate(sql);
}
} catch (Exception e) {
e.printStackTrace();
}
}
Statement 操作 DML
@Test
public void testInsert() {
String sql = "INSERT INTO t_student (id, name, age, intro) VALUES (null, '王五', 30, '王五是个好孩子')";
Connection connection = null;
Statement statement = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root");
statement = connection.createStatement();
statement.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
Statement 操作 DQL
@Test
public void testQuery() {
String sql = "SELECT * FROM t_student";
Connection connection = null;
Statement statement = null;
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/db", "root", "root");
statement = connection.createStatement();
ResultSet query = statement.executeQuery(sql); // 接收查询返回的结果
while (query.next()) { // 遍历返回结果集
Long id = query.getLong("id"); // 根据列名获取结果
String name = query.getString("name");
Integer age = query.getInt("age");
String intro = query.getString("intro");
System.out.println(id + " " + name + " " + age + " " + intro);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
PreparedStatement 对象
PreparedStatement
接口扩展了 Statement
接口,它添加了比 Statement
对象更好一些优点的功能。此语句可以动态地提供 / 接受参数。(实例后面提供,将抽取重复代码,统一处理结果集和赋值)
创建 PreparedStatement 对象
JDBC 中的所有参数都由 ?
符号作为占位符,这被称为参数标记。在执行SQL语句之前,必须为每个参数(占位符)提供值。
setXXX()
方法将值绑定到参数,其中 XXX
表示要绑定到输入参数的值的 Java 数据类型。 如果忘记提供绑定值,则将会抛出一个 SQLException
。
每个参数标记是它其顺序位置引用。第一个标记表示位置 1,下一个位置 2 等等。该方法与 Java 数组索引不同(它不从 0 开始)。
所有 Statement
对象与数据库交互的方法 execute()
, executeQuery()
和 executeUpdate()
也可以用于 PreparedStatement
对象。但是,这些方法被修改为可以使用输入参数的 SQL 语句。
关闭PreparedStatement对象
就像关闭 Statement
对象一样,由于同样的原因 (节省数据库系统资源),也应该关闭 PreparedStatement
对象。
简单的调用 close()
方法将执行关闭。 如果先关闭 Connection
对象,它也会关闭 PreparedStatement
对象。 但是,应该始终显式关闭 PreparedStatement
对象,以确保以正确顺序清理资源。
PreparedStatement pstmt = null;
try {
String SQL = "Update Employees SET age = ? WHERE id = ?";
pstmt = conn.prepareStatement(SQL);
// TODO
}
catch (SQLException e) {
// TODO
}
finally {
pstmt.close();
}
CallableStatement 对象
创建 CallableStatement 对象
类似 Connection
对象创建 Statement
和 PreparedStatement
对象一样,它还可以使用同样的方式创建 CallableStatement
对象,该对象将用于执行对数据库存储过程的调用。
留坑。。。