本文系統梳理 JDBC 核心操作,不依賴 Hibernate 或 MyBatis 等 ORM 框架,直接使用原生 JDBC 講解數據庫訪問原理。掌握這些基礎,能更透徹地理解上層框架的運行機制。
Maven 項目需在 pom.xml 引入 MySQL 驅動:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.26</version>
</dependency>
Class.forName("com.mysql.jdbc.Driver"); // 加載驅動
String url = "jdbc:mysql://localhost:3306/blog";
String username = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, username, password);
適用於無參數的靜態 SQL,不支持預編譯,存在 SQL 注入風險,性能較低。
支持參數化查詢,SQL 預編譯一次可多次執行,防止 SQL 注入,性能優於 Statement。
專門用於調用數據庫存儲過程。
通過 next() 方法游標遍歷查詢結果,配合 getXxx() 方法獲取列數據。
| 方法 | 用途 | 返回值 |
|---|---|---|
| executeUpdate() | 增刪改、DDL 操作 | 受影響行數 |
| executeQuery() | 查詢操作 | ResultSet |
| execute() | 複雜操作(多結果集/多更新計數) | boolean |
public class JDBCHelper {
private Connection connection;
private PreparedStatement preparedStatement;
public JDBCHelper() {
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/blog";
connection = DriverManager.getConnection(url, "root", "123456");
} catch (Exception e) { e.printStackTrace(); }
}
/** 增刪改通用方法 */
public int update(String sql, Object... parameters) throws SQLException {
try {
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
preparedStatement.setObject(i + 1, parameters[i]);
}
return preparedStatement.executeUpdate();
} finally {
closeStatement();
}
}
/** 查詢通用方法 */
public ResultSet query(String sql, Object... parameters) throws SQLException {
try {
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < parameters.length; i++) {
preparedStatement.setObject(i + 1, parameters[i]);
}
return preparedStatement.executeQuery();
} finally {
closeStatement();
}
}
private void closeStatement() {
if (preparedStatement != null) {
try { preparedStatement.close(); } catch (SQLException e) {}
preparedStatement = null;
}
}
}
| 隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
|---|---|---|---|
| READ_UNCOMMITTED | 可能 | 可能 | 可能 |
| READ_COMMITTED | 避免 | 可能 | 可能 |
| REPEATABLE_READ | 避免 | 避免 | 可能 |
| SERIALIZABLE | 避免 | 避免 | 避免 |
現象解釋:
public void addUserAndRole(UserBean user, UserRoleBean role) throws SQLException {
connection.setAutoCommit(false); // 關閉自動提交
connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
String userSql = "INSERT INTO b_user(name,password,level,des,tel) VALUES(?,?,?,?,?)";
String roleSql = "INSERT INTO b_user_role(userId,roleId) VALUES(?,?)";
try {
// 插入用戶
preparedStatement = connection.prepareStatement(userSql);
preparedStatement.setString(1, user.getName());
preparedStatement.setString(2, user.getPassword());
preparedStatement.setInt(3, user.getLevel());
preparedStatement.setString(4, user.getDes());
preparedStatement.setString(5, user.getTel());
preparedStatement.executeUpdate();
// 模擬業務異常
if (role.getRoleId() == 0) {
throw new Exception("角色ID無效");
}
// 插入用戶角色關係
preparedStatement = connection.prepareStatement(roleSql);
preparedStatement.setInt(1, role.getUserId());
preparedStatement.setInt(2, role.getRoleId());
preparedStatement.executeUpdate();
connection.commit(); // 提交事務
System.out.println("執行成功");
} catch (Exception e) {
connection.rollback(); // 回滾事務
System.out.println("執行失敗: " + e.getMessage());
}
}
頻繁創建/銷毀連接開銷極大,生產環境必須使用連接池(如 C3P0、Druid、HikariCP)。詳細原理參考:連接池深度解析