本範例示範在 JSP 頁面中使用 **JDBC-ODBC Bridge** (舊技術,JDK 8 已移除) 連線 SQL Server,執行查詢並顯示結果。現代專案請改用 **Type 4 純 Java 驅動** (Microsoft JDBC Driver for SQL Server) 搭配連線池。本文保留原始碼結構並加入最佳實務註解。
<%@ page contentType="text/html;charset=UTF-8" %>
<%@ page import="java.sql.*" %>
<%@ page import="javax.naming.*" %>
<%
// 建議:從 JNDI 取得 DataSource (連線池),避免硬編碼連線資訊
// Context ctx = new InitialContext();
// DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/mssql");
// Connection conn = ds.getConnection();
// 以下為教學示範用硬編碼方式 (僅限開發測試)
String url = "jdbc:sqlserver://localhost:1433;databaseName=mydb;encrypt=true;trustServerCertificate=true";
String user = "sa";
String password = "your_strong_password";
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// JDBC 4.0+ 自動載入驅動,無需 Class.forName()
// Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
conn = DriverManager.getConnection(url, user, password);
stmt = conn.createStatement();
String query = "SELECT Comname FROM Information"; // 僅查詢需要的欄位
rs = stmt.executeQuery(query);
// 取得中繼資料顯示欄位名稱
ResultSetMetaData meta = rs.getMetaData();
int colCount = meta.getColumnCount();
%>
<table border="1" cellpadding="5" cellspacing="0">
<thead>
<tr>
<% for (int i = 1; i <= colCount; i++) { %>
<th><%= meta.getColumnLabel(i) %></th>
<% } %>
</tr>
</thead>
<tbody>
<% while (rs.next()) { %>
<tr>
<% for (int i = 1; i <= colCount; i++) { %>
<td><%= rs.getString(i) %></td>
<% } %>
</tr>
<% } %>
</tbody>
</table>
<%
} catch (SQLException e) {
out.println("<p style='color:red'>資料庫錯誤: " + e.getMessage() + "</p>");
e.printStackTrace(); // 開發環境印堆疊,生產環境改用 Logger
} finally {
// 逆序關閉資源
try { if (rs != null) rs.close(); } catch (SQLException ignored) {}
try { if (stmt != null) stmt.close(); } catch (SQLException ignored) {}
try { if (conn != null) conn.close(); } catch (SQLException ignored) {}
}
%>
| 原寫法問題 | 改進建議 |
|---|---|
JDBC-ODBC Bridge (sun.jdbc.odbc.JdbcOdbcDriver) | **改用 Microsoft JDBC Driver** (Type 4):com.microsoft.sqlserver.jdbc.SQLServerDriverMaven: com.microsoft.sqlserver:mssql-jdbc:12.4.2.jre11 |
| 硬編碼連線字串、帳密 | **JNDI DataSource + 連線池** (HikariCP/Tomcat DBCP),設定於 context.xml 或 server.xml |
SELECT * | **明確列出欄位** (SELECT Comname, Comtypename...),減少網路傳輸、避免結構變更影響 |
字元編碼 gb2312 | **統一 UTF-8**:頁面、資料庫、JDBC 連線字串 characterEncoding=UTF-8 |
| 資源關閉無 finally | **try-with-resources (JDK 7+)** 或 finally 區塊確保關閉 |
| 例外直接印在頁面 | **統一錯誤頁面** (web.xml <error-page>),生產環境記錄 Logger 不暴露堆疊 |
| Scriptlet 混雜 HTML | **MVC 架構**:Servlet 處理邏輯 → JSP 僅顯示 → 或採用 Thymeleaf/FreeMarker/React 等模板/前端框架 |
// 1. Servlet / Controller
@WebServlet("/companies")
public class CompanyServlet extends HttpServlet {
@Resource(name = "jdbc/mssql") DataSource ds;
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
try (Connection conn = ds.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT Comname FROM Information");
ResultSet rs = ps.executeQuery()) {
List companies = new ArrayList<>();
while (rs.next()) companies.add(rs.getString("Comname"));
req.setAttribute("companies", companies);
req.getRequestDispatcher("/WEB-INF/views/companies.jsp").forward(req, resp);
} catch (SQLException e) {
throw new ServletException("查詢失敗", e);
}
}
}
// 2. JSP View (使用 JSTL)
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core" %>
<table>
<c:forEach var="name" items="${companies}">
<tr><td><c:out value="${name}"/></td></tr>
</c:forEach>
</table>
// 3. context.xml 連線池設定
<Resource name="jdbc/mssql"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
url="jdbc:sqlserver://localhost:1433;databaseName=mydb;encrypt=true;trustServerCertificate=true"
username="app_user"
password="pool_password"
maxTotal="20"
maxIdle="10"
maxWaitMillis="5000"/>