文章目录
- 介绍
- 背景
- 发展
- 目的和设计
- 演进
- 结论
- JDBC的主要组件
- JDBC使用流程
- JDBC的事务管理
- JDBC驱动类型
- 结论
- insert和batch insert
- 示例:
- update和batch update
- 事务的重要性
- delete和batch delete
- 单条记录删除(Delete)
- 批量删除多条记录(Batch Delete)
- 查询
- 参考资料
- 官方文档和教程:
- 官方API规范:
介绍
JDBC(Java Database Connectivity)的由来可以追溯到1990年代中期,当时Java语言正在蓬勃发展之中。在那个时期,Java以其“一次编写,到处运行”的理念迅速赢得了开发者的青睞。然而,为了进一步扩展Java语言在企业级应用中的可用性,需要一个标准化的接口,让Java应用能够与数据库进行交互,这就是JDBC诞生的背景。
背景
在JDBC出现之前,数据库互操作性常常依赖于数据库厂商特定的API,或者是采用诸如ODBC(Open Database Connectivity)这样的数据库互联接口。这些方法或多或少存在一定的限制,特别是对于Java这样旨在跨平台运行的语言来说。例如,使用ODBC需要通过JNI(Java Native Interface)来进行连接,这降低了Java程序的可移植性。
发展
为了解决这个问题,Java的母公司Sun Microsystems(太阳微系统)发布了JDBC API。JDBC 1.0在1997年随JDK 1.1发布,标志着Java程序可以直接,以一种跨平台的、与数据库厂商无关的方式来访问数据库。从那时起,JDBC API成为了Java SE(Standard Edition)的一部分。
目的和设计
JDBC API的主要目的是提供一个统一的、数据库无关的方法来访问多种关系型数据库。这意味着Java开发者可以用几乎相同的代码基础来与不同的数据库进行交互,仅需更换相应数据库的JDBC驱动即可完成对不同数据库的访问,大大提高了代码的可重用性和开发效率。
JDBC API通过“驱动管理器”和“数据库URL”简化了数据库连接过程。它提供了一套接口(如Connection、Statement、ResultSet等),开发者可以使用这些接口与数据库进行交互,而这些接口均由具体数据库的JDBC驱动实现。
演进
随着时间的推移,JDBC API经历了多个版本的更新,每个版本都增加了新的特性和性能改进。例如,JDBC 2.0(包含在JDK 1.2中)增加了批处理、滚动结果集等特性,JDBC 3.0提高了连接池管理,而JDBC 4.0引入了自动发现驱动的能力,简化了驱动的加载过程。
结论
总的来说,JDBC是Java技术栈中关键的一环,它有效地弥合了Java应用与数据库之间的鸿沟,使得Java成为了一个理想的选择,用于开发企业级数据库驱动的应用。JDBC以其强大的数据库操作能力、跨平台的特性、以及出色的扩展性,继续在当今的软件开发领域发挥着重要作用。
JDBC的主要组件
-
DriverManager:这个类管理一组JDBC驱动程序的列表。当一个应用程序尝试连接数据库时,DriverManager会负责寻找合适的驱动来建立连接。
-
Connection:表示应用程序和数据库之间的连接(或会话)。通过Connection对象,可以管理事务(比如提交或回滚事务)。
-
Statement:用于在数据库上执行静态SQL语句并返回它所生成结果的对象。
-
PreparedStatement:继承自Statement,它表示预编译的SQL语句。这种语句的性能更好,并且可以防止SQL注入攻击。
-
CallableStatement:继承自PreparedStatement,用于执行数据库存储过程。
-
ResultSet:表示SQL语句执行结果的数据集。它允许以迭代方式读取数据。
JDBC使用流程
- 加载JDBC驱动:加载连接数据库所需要的驱动,可以通过调用Class.forName()实现。
Class.forName("com.mysql.jdbc.Driver");
- 建立连接:使用DriverManager.getConnection()方法通过数据库的URL、用户名和密码来建立连接。
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/数据库名", "用户名", "密码");
- 创建Statement:创建一个Statement或PreparedStatement对象,用于执行SQL语句。
Statement stmt = conn.createStatement();
- 执行SQL语句:执行SQL查询或更新语句。
ResultSet rs = stmt.executeQuery("SELECT * FROM 表名");
- 处理结果:处理ResultSet中的数据(仅针对查询操作)。
while (rs.next()) {
System.out.println(rs.getString("列名"));
}
- 清理环境:关闭ResultSet、Statement和Connection对象。
rs.close();
stmt.close();
conn.close();
JDBC的事务管理
JDBC允许开发者通过Connection对象控制事务。默认情况下,JDBC的事务是自动提交的,这意味着每条SQL语句被执行后会立即提交。如果需要手动控制事务,可以关闭自动提交模式,然后显式地提交或回滚事务:
// 关闭自动提交
conn.setAutoCommit(false);
// 执行SQL语句
// ...
// 提交事务
conn.commit();
// 或回滚事务
// conn.rollback();
JDBC驱动类型
JDBC驱动主要分为四种类型:
- 类型1:JDBC-ODBC桥驱动(不再推荐使用)
- 类型2:本地API部分Java驱动
- 类型3:网络协议全Java驱动
- 类型4:数据库协议全Java驱动(直接连接数据库,无需其他服务器或桥接程序,通常推荐使用类型4驱动)
结论
JDBC为Java应用提供了一种与数据库交互的强大机制,通过将Java代码与数据库交互细节解耦,使数据库访问更加标准化和简化。了解和掌握JDBC对于任何从事Java开发,并需要与关系型数据库交互的开发者来说,都是非常重要的。
insert和batch insert
- 插入单条记录(Insert)
插入单条记录通常使用 PreparedStatement。预编译的 SQL 语句提高了性能,并且有助于防止SQL注入攻击。
示例:
假设我们有一个数据库名为 mydatabase,表名为 students,表结构包含两列:id(INT 类型)和 name(VARCHAR 类型)。
以下是插入单条记录到 students 表的 JDBC 代码示例:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class JdbcInsertDemo {
public static void main(String[] args) {
String jdbcURL = "jdbc:mysql://localhost:3306/mydatabase";
String username = "user";
String password = "password";
try {
Connection connection = DriverManager.getConnection(jdbcURL, username, password);
// 插入SQL语句
String sql = "INSERT INTO students (id, name) VALUES (?, ?)";
// 创建PreparedStatement
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1, 1); // 设置第一个问号(id)的值为 1
statement.setString(2, "John Doe"); // 设置第二个问号(name)的值为 John Doe
int rowsInserted = statement.executeUpdate();
// 检查插入是否成功
if (rowsInserted > 0) {
System.out.println("A new student was inserted successfully!");
}
// 关闭连接
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 批量插入多条记录(Batch Insert)
当需要插入多条记录时,使用批处理方式可以大大提高性能,减少对数据库的操作次数。
示例:
我们使用相同的数据库和表结构执行批量插入操作。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class JdbcBatchInsertDemo {
public static void main(String[] args) {
String jdbcURL = "jdbc:mysql://localhost:3306/mydatabase";
String username = "user";
String password = "password";
try {
Connection connection = DriverManager.getConnection(jdbcURL, username, password);
// 关闭自动提交
connection.setAutoCommit(false);
// 插入SQL语句
String sql = "INSERT INTO students (id, name) VALUES (?, ?)";
// 创建PreparedStatement
PreparedStatement statement = connection.prepareStatement(sql);
// 添加多批次操作到PreparedStatement
for (int i = 1; i <= 5; i++) {
statement.setInt(1, i);
statement.setString(2, "Student " + i);
statement.addBatch(); // 将这个设置的参数加入到批处理中
}
// 执行批处理操作
int[] rowsInserted = statement.executeBatch();
// 提交事务
connection.commit();
System.out.println("Inserted rows: " + rowsInserted.length);
// 关闭连接
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
在这个例子中,我们用 PreparedStatement.addBatch() 方法添加了多个插入操作到批处理任务中,最后用 executeBatch() 方法执行这些操作。使用 connection.setAutoCommit(false) 和 connection.commit() 来手动管理事务,保证所有插入操作要么全部成功,要么全部失败,从而保持数据的一致性。
update和batch update
- 更新单条记录(Update)
更新操作通常使用 PreparedStatement 来实现,以提高性能并防止SQL注入攻击。
示例:
假设我们有一个数据库名为 mydatabase,表名为 students,要更新这个表中的记录。
String jdbcURL = "jdbc:mysql://localhost:3306/mydatabase";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(jdbcURL, username, password);
// 更新SQL语句
String sql = "UPDATE students SET name = ? WHERE id = ?";
// 创建PreparedStatement
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, "John Doe Updated"); // 将名字更新为 'John Doe Updated'
statement.setInt(2, 1); // 更新id为1的记录
int rowsUpdated = statement.executeUpdate();
// 检查更新是否成功
if (rowsUpdated > 0) {
System.out.println("An existing student was updated successfully!");
}
// 关闭连接
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
- 批量更新多条记录(Batch Update)
当需要更新多条记录时,使用批处理(Batch Processing)方式可以大大提高效率,减少对数据库的操作次数。
示例:
仍然使用相同的数据库和表结构来执行批量更新操作。
String jdbcURL = "jdbc:mysql://localhost:3306/mydatabase";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(jdbcURL, username, password);
// 关闭自动提交
connection.setAutoCommit(false);
// 更新SQL语句
String sql = "UPDATE students SET name = ? WHERE id = ?";
// 创建PreparedStatement
PreparedStatement statement = connection.prepareStatement(sql);
// 添加多个批次操作到PreparedStatement
for (int i = 1; i <= 5; i++) {
statement.setString(1, "Student " + i + " Updated");
statement.setInt(2, i); // 更新id为i的记录
statement.addBatch(); // 将这个设置的参数加入到批处理中
}
// 执行批处理操作
int[] rowsUpdated = statement.executeBatch();
// 提交事务
connection.commit();
System.out.println("Updated rows count: " + rowsUpdated.length);
// 关闭连接
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
在这个例子中,我们通过使用 addBatch() 方法向 PreparedStatement 添加了多个更新操作。然后,使用 executeBatch() 方法执行这些操作。使用 connection.setAutoCommit(false) 来关闭自动提交,并手动调用 connection.commit() 提交事务,从而确保这些更新操作要么全部成功,要么在遇到错误时全部撤回,以保持数据的一致性。
事务的重要性
在批量更新中,手动管理事务非常重要,因为它可以保障数据的完整性和一致性。如果在处理过程中发生错误,它允许我们回滚到事务开始之前的状态,撤销所有的更新操作。通过调用 connection.rollback() (在捕获异常时)实现。
delete和batch delete
单条记录删除(Delete)
删除操作通常使用 PreparedStatement 来实现,以提高性能并防止 SQL 注入攻击。
示例:
假设我们有一个数据库名为 mydatabase,表名为 students,并想删除该表中的某些记录。
String jdbcURL = "jdbc:mysql://localhost:3306/mydatabase";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(jdbcURL, username, password);
// DELETE SQL语句
String sql = "DELETE FROM students WHERE id = ?";
// 创建PreparedStatement
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1, 1); // 删除id为1的记录
int rowsDeleted = statement.executeUpdate();
// 检查删除是否成功
if (rowsDeleted > 0) {
System.out.println("A student was deleted successfully!");
}
// 关闭连接
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
这段代码将删除 students 表中 id 等于 1 的记录。
批量删除多条记录(Batch Delete)
与批量插入或更新相似,可以使用批处理来删除多条记录,从而减少数据库操作的次数和提高性能。
示例:
使用相同的数据库和表结构来执行批量删除操作。
String jdbcURL = "jdbc:mysql://localhost:3306/mydatabase";
String username = "root";
String password = "password";
try {
Connection connection = DriverManager.getConnection(jdbcURL, username, password);
// 关闭自动提交
connection.setAutoCommit(false);
// DELETE SQL语句
String sql = "DELETE FROM students WHERE id = ?";
// 创建PreparedStatement
PreparedStatement statement = connection.prepareStatement(sql);
// 添加多个批次操作到PreparedStatement
for (int i = 1; i <= 5; i++) {
statement.setInt(1, i); // 删除id为i的记录
statement.addBatch(); // 将这个设置的参数加入到批处理中
}
// 执行批量删除操作
int[] rowsDeleted = statement.executeBatch();
// 提交事务
connection.commit();
System.out.println("Deleted rows count: " + rowsDeleted.length);
// 关闭连接
statement.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
在这个例子中,我们通过使用 addBatch() 方法向 PreparedStatement 中添加了多个删除操作。然后,使用 executeBatch() 方法执行这些操作。使用 connection.setAutoCommit(false) 来关闭自动提交,并手动调用 connection.commit() 提交事务。与更新操作类似,这样做可以确保这些删除操作要么全部成功,要么在遇到错误时全部撤回,以保持数据的一致性。
在处理可能遇到的任何 SQLException 时,应该对事务进行回滚,使用 connection.rollback()。这会撤销自上一个提交或回滚以来执行的所有更改。在生产环境中,你通常也需要添加适当的错误处理,并且始终确保资源在使用后被适当地关闭,最好是在 finally 块或使用 try-with-resources 语句。
查询
在 JDBC 中,执行查询操作通常涉及到以下步骤:编写 SQL 查询语句、执行查询、处理 ResultSet 对象并提取数据,以及最后清理资源。
下面是一个使用 JDBC 执行查询操作并处理结果集的完整示例。
示例:
假设我们有一个数据库名为 mydatabase,表名为 employees,表结构包含以下列:id(INT 类型)、name(VARCHAR 类型)和 department(VARCHAR 类型)。
要查询 employees 表中的数据,可以使用以下 JDBC 代码:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class JdbcSelectDemo {
public static void main(String[] args) {
String jdbcURL = "jdbc:mysql://localhost:3306/mydatabase";
String username = "root";
String password = "password";
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
// 建立数据库连接
connection = DriverManager.getConnection(jdbcURL, username, password);
// 准备 SQL 查询语句
String sql = "SELECT id, name, department FROM employees";
// 创建 PreparedStatement 对象
statement = connection.prepareStatement(sql);
// 执行查询语句并获取结果集
resultSet = statement.executeQuery();
// 遍历结果集并获取数据
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String department = resultSet.getString("department");
// 打印出从数据库中检索的数据
System.out.println("ID: " + id + ", Name: " + name + ", Department: " + department);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 关闭 ResultSet
if (resultSet != null) {
resultSet.close();
}
// 关闭 PreparedStatement
if (statement != null) {
statement.close();
}
// 关闭 Connection
if (connection != null) {
connection.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
在这个例子中,我们建立了与数据库的连接,使用了一个 SELECT 语句来查询 employees 表。然后使用 PreparedStatement.executeQuery 方法执行了查询,并返回了一个 ResultSet 对象。
通过 while 循环和 ResultSet.next 方法遍历结果集,我们可以逐行读取数据。通过 ResultSet 的 getInt、getString 等方法根据列的数据类型来获取每列的值。
最后,我们在 finally 块中关闭了 ResultSet、PreparedStatement 和 Connection。这样做是很重要的,因为它可以释放数据库和 JDBC 资源,避免内存泄漏和其他潜在问题。一个更清洁的替代方案是使用 Java 7 引入的 try-with-resources 语法,这将自动关闭在 try 语句中打开的资源。
JDBC 的查询操作是 数据库交互的核心,不仅可以检索整个表格或者特定列,还可以根据需要进行条件查询、联合查询或其他复杂的数据库操作。
参考资料
官方文档和教程:
- Oracle官方JDBC文档:Oracle提供了完整的JDBC参考文档,其中包含了API的全部细节。
- Oracle的JDBC教程:一个涵盖JDBC基础概念到高级特性的综合性教程。
这些资源对理解 JDBC 的不同方面以及如何与数据库交互非常有帮助。您可以在 Oracle 的官方网站或Java的官方文档中找到这些资源。
官方API规范:
- 访问Java SE API规范可以查看关于 JDBC 接口和类的官方说明。