Java JDBC(Java Database Connectivity)是一个用于执行SQL语句的Java API,它能够与各种关系数据库进行交互。JDBC的核心流程主要包括以下几个步骤:
- 加载驱动程序
- 建立连接
- 创建和执行SQL语句
- 处理结果集
- 关闭资源
以下是这些步骤在JDBC中的实现细节及其源码分析。
1. 加载驱动程序
在使用JDBC时,首先需要加载数据库驱动程序。这通常通过Class.forName()方法来完成,该方法会注册相应的驱动程序。
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
2. 建立连接
通过DriverManager.getConnection()方法建立数据库连接。
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "password";
Connection conn = null;
try {
conn = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
e.printStackTrace();
}
DriverManager 源码分析
DriverManager类负责管理一组JDBC驱动程序。当调用getConnection()时,它会遍历已注册的驱动程序列表,找到合适的驱动并返回连接。
public class DriverManager {
private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException {
if (driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver));
} else {
throw new NullPointerException();
}
}
public static Connection getConnection(String url, String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return getConnection(url, info);
}
public static Connection getConnection(String url, java.util.Properties info) throws SQLException {
for (DriverInfo aDriver : registeredDrivers) {
try {
Connection result = aDriver.driver.connect(url, info);
if (result != null) {
return result;
}
} catch (SQLException e) {
// 忽略并尝试下一个驱动
}
}
throw new SQLException("No suitable driver found for " + url);
}
}
3. 创建和执行SQL语句
通过Connection对象创建Statement或PreparedStatement对象,并通过它们执行SQL查询或更新。
Statement stmt = null;
ResultSet rs = null;
try {
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM mytable");
while (rs.next()) {
System.out.println(rs.getString("column1"));
}
} catch (SQLException e) {
e.printStackTrace();
}
Statement 源码分析
Statement接口用于执行静态SQL语句并返回其生成的结果。
public interface Statement extends Wrapper, AutoCloseable {
ResultSet executeQuery(String sql) throws SQLException;
int executeUpdate(String sql) throws SQLException;
void close() throws SQLException;
}
public class StatementImpl implements Statement {
private ConnectionImpl connection;
public StatementImpl(ConnectionImpl connection) {
this.connection = connection;
}
public ResultSet executeQuery(String sql) throws SQLException {
// 执行查询并返回结果集
return new ResultSetImpl();
}
public int executeUpdate(String sql) throws SQLException {
// 执行更新并返回受影响的行数
return 1;
}
public void close() throws SQLException {
// 关闭语句
}
}
4. 处理结果集
通过ResultSet对象获取查询结果。
try {
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.println("ID: " + id + ", Name: " + name);
}
} catch (SQLException e) {
e.printStackTrace();
}
ResultSet 源码分析
ResultSet接口表示SQL查询的结果集。
ResultSet 接口
public interface ResultSet extends Wrapper, AutoCloseable {
boolean next() throws SQLException;
String getString(int columnIndex) throws SQLException;
String getString(String columnLabel) throws SQLException;
int getInt(int columnIndex) throws SQLException;
int getInt(String columnLabel) throws SQLException;
void close() throws SQLException;
// ... 还有许多其他方法,例如 getDouble, getDate 等
}
简单的 ResultSet 实现类
为了简单起见,这个实现类是非常简化的,仅供理解流程之用。
public class ResultSetImpl implements ResultSet {
private List<Map<String, Object>> data;
private int cursor;
public ResultSetImpl(List<Map<String, Object>> data) {
this.data = data;
this.cursor = -1; // 初始化指针在第一条记录之前
}
@Override
public boolean next() throws SQLException {
cursor++;
return cursor < data.size();
}
@Override
public String getString(int columnIndex) throws SQLException {
Map<String, Object> row = data.get(cursor);
return (String) row.values().toArray()[columnIndex - 1];
}
@Override
public String getString(String columnLabel) throws SQLException {
Map<String, Object> row = data.get(cursor);
return (String) row.get(columnLabel);
}
@Override
public int getInt(int columnIndex) throws SQLException {
Map<String, Object> row = data.get(cursor);
return (int) row.values().toArray()[columnIndex - 1];
}
@Override
public int getInt(String columnLabel) throws SQLException {
Map<String, Object> row = data.get(cursor);
return (int) row.get(columnLabel);
}
@Override
public void close() throws SQLException {
// 关闭结果集
}
}
5. 关闭资源
在完成数据库操作后,应该关闭所有打开的资源,例如ResultSet、Statement和Connection。
finally {
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
完整的 JDBC 示例流程
结合以上所有步骤,以下是一个完整的JDBC示例:
import java.sql.*;
public class JDBCDemo {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1. 加载驱动程序
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 建立连接
String url = "jdbc:mysql://localhost:3306/mydatabase";
String user = "root";
String password = "password";
conn = DriverManager.getConnection(url, user, password);
// 3. 创建语句对象并执行查询
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM mytable");
// 4. 处理结果集
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.println("ID: " + id + ", Name: " + name);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
// 5. 关闭资源
try {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
这个示例展示了如何按照JDBC的核心流程进行数据库操作,从加载驱动程序、建立连接,到创建和执行SQL语句以及处理结果集,最后关闭所有资源。实际应用中,常常会使用更高级的机制来管理这些资源,例如使用连接池或框架(如Spring)的JDBC模板。