JDBC 是 Java 訪問關係型數據庫的統一 API。本文以 MySQL 為例,完整演示驅動加載、建立連接、實體類映射、DAO 層封裝 (增刪改查)、Servlet 控制器調用、JSP 頁面交互、字符集過濾器、項目目錄結構,並給出生產環境必備的連接池 (Druid/HikariCP) 遷移指南。
CREATE DATABASE LandDB;
USE LandDB;
CREATE TABLE T_BL_CANTON_CODE (
CTN_CODE INT PRIMARY KEY,
CTN_NAME VARCHAR(60) NOT NULL,
GOV_NAME VARCHAR(60) NOT NULL,
LAND_DP_NAME VARCHAR(60) NOT NULL,
PARENT_CODE INT NOT NULL
);
INSERT INTO T_BL_CANTON_CODE VALUES (12000, '長沙市', '長沙市人民政府', '長沙市國土資源廳', 'HN');
INSERT INTO T_BL_CANTON_CODE VALUES (11000, '北京市', '北京市人民政府', '北京市國土資源廳', 'BN');
驅動包:mysql-connector-java-5.1.x.jar 放入 WEB-INF/lib 或 Maven 依賴。
public class Canton {
private int ctn_code;
private String ctn_name, gov_name, land_dp_name, parent_code;
// Getter/Setter 省略
}
public class BuildConn {
private static final String URL = "jdbc:mysql://localhost:3306/landdb";
private static final String USER = "user2";
private static final String PASS = "123456";
public Connection openConn() throws SQLException {
try { Class.forName("com.mysql.jdbc.Driver"); }
catch (ClassNotFoundException e) { throw new SQLException("驅動加載失敗", e); }
return DriverManager.getConnection(URL, USER, PASS);
}
}
public class CantonDaoImp {
private BuildConn buildConn = new BuildConn();
// 增
public boolean addCanton(Canton c) {
String sql = "INSERT INTO T_BL_CANTON_CODE VALUES (?,?,?,?,?)";
try (Connection con = buildConn.openConn();
PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, c.getCtn_code());
ps.setString(2, c.getCtn_name());
ps.setString(3, c.getGov_name());
ps.setString(4, c.getLand_dp_name());
ps.setString(5, c.getParent_code());
return ps.executeUpdate() > 0;
} catch (SQLException e) { e.printStackTrace(); return false; }
}
// 刪 (按主鍵)
public int delCanton(int ctnCode) {
String sql = "DELETE FROM T_BL_CANTON_CODE WHERE CTN_CODE = ?";
try (Connection con = buildConn.openConn();
PreparedStatement ps = con.prepareStatement(sql)) {
ps.setInt(1, ctnCode);
return ps.executeUpdate();
} catch (SQLException e) { e.printStackTrace(); return 0; }
}
// 查 (全部)
public List<Canton> listCanton() {
String sql = "SELECT * FROM T_BL_CANTON_CODE";
List<Canton> list = new ArrayList<>();
try (Connection con = buildConn.openConn();
PreparedStatement ps = con.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
Canton c = new Canton();
c.setCtn_code(rs.getInt("CTN_CODE"));
c.setCtn_name(rs.getString("CTN_NAME"));
c.setGov_name(rs.getString("GOV_NAME"));
c.setLand_dp_name(rs.getString("LAND_DP_NAME"));
c.setParent_code(rs.getString("PARENT_CODE"));
list.add(c);
}
} catch (SQLException e) { e.printStackTrace(); }
return list;
}
// 改... 類似
}
@WebFilter("/*")
public class CharsetFilter implements Filter {
private String encoding = "UTF-8";
@Override public void init(FilterConfig fc) { encoding = fc.getInitParameter("encoding"); }
@Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
req.setCharacterEncoding(encoding);
res.setContentType("text/html;charset=" + encoding);
chain.doFilter(req, res);
}
@Override public void destroy() {}
}
web.xml 配置 (若不使用 @WebFilter):
<filter><filter-name>CharsetFilter</filter-name><filter-class>com.example.filter.CharsetFilter</filter-class>
<init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param>
</filter>
<filter-mapping><filter-name>CharsetFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
dao.addCanton() → 轉發列表頁。ctn_code → dao.delCanton() → 轉發列表頁。dao.listCanton() → request.setAttribute("list", list) → 轉發 listCanton.jsp。addCanton.jsp 表單 action="CantonAddServlet",輸入框 name="ctn_code" 等與 Servlet 參數對應。
listCanton.jsp 使用 <% List<Canton> list = (List)request.getAttribute("list"); %> 取數據,<%=ct.getCtn_name()%> 輸出,刪除鏈接 href="CantonDelServlet?ctn_code=<%=ct.getCtn_code()%>"。

| 問題 | 原因 | 解決方案 |
|---|---|---|
| 頻繁創建/關閉 Connection | 每次 DAO 調用 new BuildConn().openConn() | **引入連接池**:Druid / HikariCP,配置 DataSource,DAO 注入 DataSource 獲取連接。 |
| SQL 注入風險 | 拼接 SQL 字符串 | **全面使用 PreparedStatement** 佔位符 ?,參數綁定。 |
| 資源洩漏 | 異常時 Connection/Statement/ResultSet 未關閉 | **try-with-resources (Java 7+)** 自動關閉,或 finally 手動關閉。 |
| 事務控制缺失 | 多條 SQL 無原子性保證 | con.setAutoCommit(false) + commit()/rollback(),或 Spring @Transactional。 |
| 硬編碼配置 | URL/用戶/密碼寫死代碼 | **外部化配置**:properties/yaml + 環境變量,構建時注入。 |
// Maven 依賴
<dependency><groupId>com.zaxxer</groupId><artifactId>HikariCP</artifactId><version>5.0.1</version></dependency>
// Spring 配置 DataSource Bean
@Bean public DataSource dataSource() {
HikariConfig cfg = new HikariConfig();
cfg.setJdbcUrl("jdbc:mysql://localhost:3306/landdb");
cfg.setUsername("user2"); cfg.setPassword("123456");
cfg.setMaximumPoolSize(20); cfg.setMinimumIdle(5);
return new HikariDataSource(cfg);
}
// DAO 改造
@Repository public class CantonDaoImp {
@Autowired private DataSource dataSource;
public Connection getConnection() throws SQLException { return dataSource.getConnection(); }
// 其餘邏輯不變,自動受益於連接池
}
JDBC 雖屬底層 API,但理解其驅動加載、連接管理、預編譯語句、結果集遍歷、資源釋放、事務邊界等核心機制,是掌握 MyBatis、JPA、Spring Data 等上層框架原理、排查數據庫性能問題、設計高可用數據訪問層的基石。實際項目中**務必使用成熟連接池**,並配合事務管理框架,切勿手動管理裸連接。