JDBC (Java Database Connectivity) 是 Java 訪問關係型資料庫的標準 API。本文系統梳理 **JDBC 兩層/三層架構、核心介面體系、標準程式設計六步驟、PreparedStatement 防注入 CRUD 範本、事務控制、連接池 (HikariCP) 整合**,提供可直接落地生產的分層代碼結構。
| 架構 | 特點 | 適用場景 |
|---|---|---|
| 兩層 (Client/Server) | Java App 直連 DB,邏輯與資料存取混雜 | 桌面應用、簡單工具、原型驗證 |
| 三層 (Presentation/Business/Data) | 中間層封裝業務邏輯,Controller-Service-DAO 分層 | 企業級 Web 系統、微服務、標準分層架構 |
現代開發**強烈建議採用三層架構**,並配合 Spring/JPA/MyBatis 等 ORM 框架;理解原生 JDBC 有助於排查框架底層問題。
Driver:資料庫廠商實現,註冊至 DriverManager。Connection:資料庫會話,管理事務、建立 Statement。Statement:靜態 SQL 執行。PreparedStatement:預編譯 SQL,**防 SQL 注入、效能優化(執行計畫緩存)**,主力選擇。CallableStatement:呼叫預存程序。ResultSet:結果集游標,支援順向/可捲動/可更新。DataSource:連接池核心介面,替代 DriverManager。// 1. 載入驅動 (JDBC 4.0+ 可省略,SPI 自動發現)
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 建立連接
Connection conn = DriverManager.getConnection(URL, USER, PASS);
// 3. 建立 Statement
PreparedStatement ps = conn.prepareStatement(SQL);
// 4. 綁定參數
ps.setString(1, "value");
ps.setInt(2, 100);
// 5. 執行
ResultSet rs = ps.executeQuery(); // 查詢
int rows = ps.executeUpdate(); // 增刪改
// 6. 處理結果 / 釋放資源
try (conn; ps; rs) { ... } // Java 7+ try-with-resources 自動關閉
public class Goddess {
private Integer id, age, sex, isDel;
private String userName, email, mobile, createUser, updateUser;
private Date birthday, createDate, updateDate;
// Getter/Setter/Builder 省略
}
public class DbUtil {
private static final String URL = "jdbc:mysql://localhost:3306/imooc?useSSL=false&serverTimezone=UTC";
private static final String USER = "root";
private static final String PASS = "123456";
static { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (Exception e) { throw new ExceptionInInitializerError(e); } }
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASS);
}
}
public interface GoddessDao {
void insert(Goddess g);
void update(Goddess g);
void deleteById(int id);
Goddess getById(int id);
List<Goddess> listAll();
}
public class GoddessDaoImpl implements GoddessDao {
private static final String INSERT_SQL = "INSERT INTO imooc_goddess(user_name,sex,age,birthday,email,mobile,create_user,create_date,update_user,update_date,is_del) VALUES(?,?,?,?,?,?,?,NOW(),?,NOW(),?)";
private static final String UPDATE_SQL = "UPDATE imooc_goddess SET user_name=?,sex=?,age=?,birthday=?,email=?,mobile=?,update_user=?,update_date=NOW(),is_del=? WHERE id=?";
private static final String DELETE_SQL = "DELETE FROM imooc_goddess WHERE id=?";
private static final String GET_SQL = "SELECT * FROM imooc_goddess WHERE id=?";
private static final String LIST_SQL = "SELECT * FROM imooc_goddess WHERE is_del=0";
@Override
public void insert(Goddess g) {
executeUpdate(INSERT_SQL, g.getUserName(), g.getSex(), g.getAge(), g.getBirthday(),
g.getEmail(), g.getMobile(), g.getCreateUser(), g.getUpdateUser(), g.getIsDel());
}
@Override
public void update(Goddess g) {
executeUpdate(UPDATE_SQL, g.getUserName(), g.getSex(), g.getAge(), g.getBirthday(),
g.getEmail(), g.getMobile(), g.getUpdateUser(), g.getIsDel(), g.getId());
}
@Override
public void deleteById(int id) {
executeUpdate(DELETE_SQL, id);
}
@Override
public Goddess getById(int id) {
return querySingle(GET_SQL, this::mapRow, id);
}
@Override
public List<Goddess> listAll() {
return queryList(LIST_SQL, this::mapRow);
}
// 通用執行模板
private void executeUpdate(String sql, Object... params) {
try (Connection conn = DbUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
bindParams(ps, params);
ps.executeUpdate();
} catch (SQLException e) { throw new RuntimeException(e); }
}
private <T> T querySingle(String sql, RowMapper<T> mapper, Object... params) {
try (Connection conn = DbUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
bindParams(ps, params);
try (ResultSet rs = ps.executeQuery()) {
return rs.next() ? mapper.mapRow(rs) : null;
}
} catch (SQLException e) { throw new RuntimeException(e); }
}
private <T> List<T> queryList(String sql, RowMapper<T> mapper, Object... params) {
List<T> list = new ArrayList<>();
try (Connection conn = DbUtil.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
bindParams(ps, params);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) list.add(mapper.mapRow(rs));
}
} catch (SQLException e) { throw new RuntimeException(e); }
return list;
}
private void bindParams(PreparedStatement ps, Object[] params) throws SQLException {
for (int i = 0; i < params.length; i++) ps.setObject(i + 1, params[i]);
}
private Goddess mapRow(ResultSet rs) throws SQLException {
Goddess g = new Goddess();
g.setId(rs.getInt("id"));
g.setUserName(rs.getString("user_name"));
g.setSex(rs.getInt("sex"));
g.setAge(rs.getInt("age"));
g.setBirthday(rs.getDate("birthday"));
g.setEmail(rs.getString("email"));
g.setMobile(rs.getString("mobile"));
g.setCreateUser(rs.getString("create_user"));
g.setCreateDate(rs.getTimestamp("create_date"));
g.setUpdateUser(rs.getString("update_user"));
g.setUpdateDate(rs.getTimestamp("update_date"));
g.setIsDel(rs.getInt("is_del"));
return g;
}
@FunctionalInterface
interface RowMapper<T> { T mapRow(ResultSet rs) throws SQLException; }
}
public void transfer(int fromId, int toId, int amount) {
Connection conn = null;
try {
conn = DbUtil.getConnection();
conn.setAutoCommit(false); // 開啟事務
// 扣減
executeUpdate(conn, "UPDATE account SET balance=balance-? WHERE id=?", amount, fromId);
// 增加
executeUpdate(conn, "UPDATE account SET balance=balance+? WHERE id=?", amount, toId);
conn.commit();
} catch (Exception e) {
if (conn != null) try { conn.rollback(); } catch (SQLException ignored) {}
throw new RuntimeException(e);
} finally {
if (conn != null) try { conn.setAutoCommit(true); conn.close(); } catch (SQLException ignored) {}
}
}
private void executeUpdate(Connection conn, String sql, Object... params) throws SQLException {
try (PreparedStatement ps = conn.prepareStatement(sql)) {
bindParams(ps, params);
ps.executeUpdate();
}
}
// Maven 依賴
// com.zaxxer:HikariCP:5.0.1
public class DataSourceUtil {
private static HikariDataSource ds;
static {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/imooc?useSSL=false&serverTimezone=UTC");
config.setUsername("root");
config.setPassword("123456");
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
config.setConnectionTimeout(30000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
config.setPoolName("GoddessPool");
ds = new HikariDataSource(config);
}
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
public static void close() { if (ds != null) ds.close(); }
}
將 DbUtil.getConnection() 改為委派 DataSourceUtil.getConnection() 即可無縫切換至連接池。
DriverManager 直連生產庫。掌握原生 JDBC 分層實戰,是理解 Spring JDBC Template、MyBatis、JPA 等框架底層原理的基石。