<think>我们使用 try-catch-finally 结构重写 CategoryServlet.java,确保资源正确释放,异常得到妥善处理。
假设 CategoryServlet 包含基本的增删改查操作(add, update, delete, list等)。
我们将为每个数据库操作(使用Connection、PreparedStatement等)添加 try-catch-finally 块来管理资源和异常。
同时,我们将使用辅助方法进行参数验证和错误处理。
注意:以下代码是一个示例,假设已经存在一个DBUtil类用于获取数据库连接和关闭资源。
我们将按照以下步骤:
1. 在操作数据库的方法中,获取数据库连接和语句对象。
2. 在try块中执行数据库操作。
3. 在catch块中捕获特定异常,并重新抛出(或处理)。
4. 在finally块中关闭资源(PreparedStatement和Connection),注意关闭顺序(后打开的先关闭)。
同时,我们将为参数解析和验证添加辅助方法,并在操作中调用这些方法。
我们将包括以下操作:
- list: 列出所有分类
- add: 添加分类
- update: 更新分类
- delete: 删除分类
由于问题要求使用try-catch-finally结构,我们将为每个操作(如add, update, delete, list)单独编写方法,并在这些方法中使用try-catch-finally。
注意:为了简洁,我们只展示关键部分,省略部分细节(如分页等)。</think>### 使用 try-catch-finally 结构重写 CategoryServlet.java
以下是使用严格的 try-catch-finally 结构重写的 CategoryServlet,包含完整的资源管理和异常处理机制:
```java
package com.servlet;
import com.dao.CategoryDAO;
import com.dao.DBUtil;
import com.model.Category;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class CategoryServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String action = request.getParameter("action");
// 验证action参数
if (action == null || action.trim().isEmpty()) {
handleError(request, response, "操作类型不能为空");
return;
}
try {
switch (action) {
case "list":
listCategories(request, response);
break;
case "add":
addCategory(request, response);
break;
case "update":
updateCategory(request, response);
break;
case "delete":
deleteCategory(request, response);
break;
case "edit":
editCategory(request, response);
break;
default:
handleError(request, response, "无效的操作类型: " + action);
}
} catch (ServletException e) {
handleError(request, response, "操作失败: " + e.getMessage(), e);
} catch (SQLException e) {
handleError(request, response, "数据库错误: " + e.getMessage(), e);
} catch (Exception e) {
handleError(request, response, "系统错误: " + e.getMessage(), e);
}
}
private void listCategories(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, SQLException {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
// 获取数据库连接
conn = DBUtil.getConnection();
// 准备SQL查询语句
String sql = "SELECT id, name, description FROM categories";
stmt = conn.prepareStatement(sql);
// 执行查询
rs = stmt.executeQuery();
// 处理结果集
List<Category> categories = new ArrayList<>();
while (rs.next()) {
Category category = new Category();
category.setId(rs.getInt("id"));
category.setName(rs.getString("name"));
category.setDescription(rs.getString("description"));
categories.add(category);
}
// 设置属性并转发
request.setAttribute("categories", categories);
request.getRequestDispatcher("categoryList.jsp").forward(request, response);
} catch (SQLException e) {
throw new SQLException("查询分类列表失败: " + e.getMessage(), e);
} finally {
// 确保资源关闭(即使为空)
closeResources(conn, stmt, rs);
}
}
private void addCategory(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, SQLException {
Connection conn = null;
PreparedStatement stmt = null;
try {
// 获取数据库连接
conn = DBUtil.getConnection();
// 验证参数
String name = validateRequired(request, "name", "分类名称");
String description = request.getParameter("description");
// 准备SQL插入语句
String sql = "INSERT INTO categories (name, description) VALUES (?, ?)";
stmt = conn.prepareStatement(sql);
// 设置参数
stmt.setString(1, name);
stmt.setString(2, description != null ? description : "");
// 执行插入操作
int rowsAffected = stmt.executeUpdate();
if (rowsAffected > 0) {
// 成功操作
response.sendRedirect("category?action=list");
} else {
throw new SQLException("添加分类失败: 没有行被影响");
}
} catch (SQLException e) {
throw new SQLException("数据库操作失败: " + e.getMessage(), e);
} finally {
closeResources(conn, stmt, null);
}
}
private void updateCategory(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, SQLException {
Connection conn = null;
PreparedStatement stmt = null;
try {
// 获取数据库连接
conn = DBUtil.getConnection();
// 验证参数
int id = parseInt(request, "id", "分类ID");
String name = validateRequired(request, "name", "分类名称");
String description = request.getParameter("description");
// 准备SQL更新语句
String sql = "UPDATE categories SET name = ?, description = ? WHERE id = ?";
stmt = conn.prepareStatement(sql);
// 设置参数
stmt.setString(1, name);
stmt.setString(2, description != null ? description : "");
stmt.setInt(3, id);
// 执行更新操作
int rowsAffected = stmt.executeUpdate();
if (rowsAffected > 0) {
// 成功操作
response.sendRedirect("category?action=list");
} else {
throw new SQLException("更新分类失败: 没有行被影响或分类不存在");
}
} catch (SQLException e) {
throw new SQLException("数据库操作失败: " + e.getMessage(), e);
} finally {
closeResources(conn, stmt, null);
}
}
private void deleteCategory(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, SQLException {
Connection conn = null;
PreparedStatement stmt = null;
try {
// 获取数据库连接
conn = DBUtil.getConnection();
// 验证参数
int id = parseInt(request, "id", "分类ID");
// 检查是否有关联图书
if (hasAssociatedBooks(conn, id)) {
throw new ServletException("无法删除分类: 存在关联的图书");
}
// 准备SQL删除语句
String sql = "DELETE FROM categories WHERE id = ?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, id);
// 执行删除操作
int rowsAffected = stmt.executeUpdate();
if (rowsAffected > 0) {
// 成功操作
response.sendRedirect("category?action=list");
} else {
throw new SQLException("删除分类失败: 分类不存在");
}
} catch (SQLException e) {
throw new SQLException("数据库操作失败: " + e.getMessage(), e);
} finally {
closeResources(conn, stmt, null);
}
}
private void editCategory(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException, SQLException {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
// 获取数据库连接
conn = DBUtil.getConnection();
// 验证参数
int id = parseInt(request, "id", "分类ID");
// 准备SQL查询语句
String sql = "SELECT id, name, description FROM categories WHERE id = ?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, id);
// 执行查询
rs = stmt.executeQuery();
if (rs.next()) {
Category category = new Category();
category.setId(rs.getInt("id"));
category.setName(rs.getString("name"));
category.setDescription(rs.getString("description"));
// 设置属性并转发
request.setAttribute("category", category);
request.getRequestDispatcher("editCategory.jsp").forward(request, response);
} else {
throw new ServletException("分类不存在: ID=" + id);
}
} catch (SQLException e) {
throw new SQLException("查询分类详情失败: " + e.getMessage(), e);
} finally {
closeResources(conn, stmt, rs);
}
}
// === 辅助方法 ===
// 检查分类是否有关联图书
private boolean hasAssociatedBooks(Connection conn, int categoryId) throws SQLException {
PreparedStatement stmt = null;
ResultSet rs = null;
try {
String sql = "SELECT COUNT(*) FROM books WHERE category_id = ?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, categoryId);
rs = stmt.executeQuery();
if (rs.next()) {
return rs.getInt(1) > 0;
}
return false;
} finally {
// 关闭内部资源
if (rs != null) {
try { rs.close(); } catch (SQLException e) { /* 忽略 */ }
}
if (stmt != null) {
try { stmt.close(); } catch (SQLException e) { /* 忽略 */ }
}
}
}
// 资源关闭方法
private void closeResources(Connection conn, Statement stmt, ResultSet rs) {
try {
if (rs != null) rs.close();
} catch (SQLException e) {
System.err.println("关闭ResultSet失败: " + e.getMessage());
}
try {
if (stmt != null) stmt.close();
} catch (SQLException e) {
System.err.println("关闭Statement失败: " + e.getMessage());
}
try {
if (conn != null) conn.close();
} catch (SQLException e) {
System.err.println("关闭Connection失败: " + e.getMessage());
}
}
// 验证必需参数
private String validateRequired(HttpServletRequest request, String paramName, String fieldName)
throws ServletException {
String value = request.getParameter(paramName);
if (value == null || value.trim().isEmpty()) {
throw new ServletException(fieldName + "不能为空");
}
return value.trim();
}
// 解析整数参数
private int parseInt(HttpServletRequest request, String paramName, String fieldName)
throws ServletException {
String value = validateRequired(request, paramName, fieldName);
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
throw new ServletException(fieldName + "必须是有效的整数: " + value);
}
}
// 错误处理方法
private void handleError(HttpServletRequest request, HttpServletResponse response, String message)
throws ServletException, IOException {
handleError(request, response, message, null);
}
private void handleError(HttpServletRequest request, HttpServletResponse response, String message, Throwable cause)
throws ServletException, IOException {
// 记录错误日志
if (cause != null) {
cause.printStackTrace();
}
// 设置错误属性
request.setAttribute("error", message);
if (cause != null) {
request.setAttribute("errorDetails", cause.getMessage());
}
// 转发到错误页面
request.getRequestDispatcher("error.jsp").forward(request, response);
}
}
```
### 关键改进说明
#### 1. 统一的 try-catch-finally 结构
```java
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null; // 仅查询操作需要
try {
// 1. 获取数据库连接
conn = DBUtil.getConnection();
// 2. 准备SQL语句
stmt = conn.prepareStatement(sql);
// 3. 设置参数和执行操作
// ...
} catch (SQLException e) {
// 4. 处理特定异常
throw new SQLException("操作失败: " + e.getMessage(), e);
} finally {
// 5. 确保资源关闭
closeResources(conn, stmt, rs);
}
```
#### 2. 资源关闭方法
```java
private void closeResources(Connection conn, Statement stmt, ResultSet rs) {
try {
if (rs != null) rs.close();
} catch (SQLException e) {
// 记录错误但不中断流程
}
try {
if (stmt != null) stmt.close();
} catch (SQLException e) {
// 记录错误
}
try {
if (conn != null) conn.close();
} catch (SQLException e) {
// 记录错误
}
}
```
#### 3. 业务逻辑增强
1. **删除保护**:
```java
private void deleteCategory(...) {
// 检查关联图书
if (hasAssociatedBooks(conn, id)) {
throw new ServletException("无法删除分类: 存在关联的图书");
}
// ...
}
```
2. **关联检查**:
```java
private boolean hasAssociatedBooks(Connection conn, int categoryId) {
// 查询关联图书数量
String sql = "SELECT COUNT(*) FROM books WHERE category_id = ?";
// ...
return rs.getInt(1) > 0;
}
```
3. **编辑操作**:
```java
private void editCategory(...) {
// 查询分类详情
String sql = "SELECT id, name, description FROM categories WHERE id = ?";
// ...
request.setAttribute("category", category);
request.getRequestDispatcher("editCategory.jsp").forward(...);
}
```
### 操作详解
#### 1. 分类列表 (listCategories)
```java
private void listCategories(...) {
try {
conn = DBUtil.getConnection();
stmt = conn.prepareStatement("SELECT id, name, description FROM categories");
rs = stmt.executeQuery();
List<Category> categories = new ArrayList<>();
while (rs.next()) {
Category category = new Category();
category.setId(rs.getInt("id"));
category.setName(rs.getString("name"));
category.setDescription(rs.getString("description"));
categories.add(category);
}
request.setAttribute("categories", categories);
request.getRequestDispatcher("categoryList.jsp").forward(...);
} finally {
closeResources(conn, stmt, rs);
}
}
```
#### 2. 添加分类 (addCategory)
```java
private void addCategory(...) {
try {
conn = DBUtil.getConnection();
String name = validateRequired(request, "name", "分类名称");
String description = request.getParameter("description");
stmt = conn.prepareStatement("INSERT INTO categories (name, description) VALUES (?, ?)");
stmt.setString(1, name);
stmt.setString(2, description != null ? description : "");
int rowsAffected = stmt.executeUpdate();
if (rowsAffected > 0) {
response.sendRedirect("category?action=list");
} else {
throw new SQLException("添加失败");
}
} finally {
closeResources(conn, stmt, null);
}
}
```
#### 3. 更新分类 (updateCategory)
```java
private void updateCategory(...) {
try {
conn = DBUtil.getConnection();
int id = parseInt(request, "id", "分类ID");
String name = validateRequired(request, "name", "分类名称");
String description = request.getParameter("description");
stmt = conn.prepareStatement("UPDATE categories SET name = ?, description = ? WHERE id = ?");
stmt.setString(1, name);
stmt.setString(2, description != null ? description : "");
stmt.setInt(3, id);
int rowsAffected = stmt.executeUpdate();
if (rowsAffected > 0) {
response.sendRedirect("category?action=list");
} else {
throw new SQLException("更新失败");
}
} finally {
closeResources(conn, stmt, null);
}
}
```
#### 4. 删除分类 (deleteCategory)
```java
private void deleteCategory(...) {
try {
conn = DBUtil.getConnection();
int id = parseInt(request, "id", "分类ID");
// 检查关联
if (hasAssociatedBooks(conn, id)) {
throw new ServletException("存在关联图书");
}
stmt = conn.prepareStatement("DELETE FROM categories WHERE id = ?");
stmt.setInt(1, id);
int rowsAffected = stmt.executeUpdate();
if (rowsAffected > 0) {
response.sendRedirect("category?action=list");
} else {
throw new SQLException("删除失败");
}
} finally {
closeResources(conn, stmt, null);
}
}
```
#### 5. 编辑分类 (editCategory)
```java
private void editCategory(...) {
try {
conn = DBUtil.getConnection();
int id = parseInt(request, "id", "分类ID");
stmt = conn.prepareStatement("SELECT id, name, description FROM categories WHERE id = ?");
stmt.setInt(1, id);
rs = stmt.executeQuery();
if (rs.next()) {
Category category = new Category();
category.setId(rs.getInt("id"));
category.setName(rs.getString("name"));
category.setDescription(rs.getString("description"));
request.setAttribute("category", category);
request.getRequestDispatcher("editCategory.jsp").forward(...);
} else {
throw new ServletException("分类不存在");
}
} finally {
closeResources(conn, stmt, rs);
}
}
```
### 最佳实践增强
#### 1. 事务管理
```java
private void updateWithTransaction(...) {
Connection conn = null;
try {
conn = DBUtil.getConnection();
conn.setAutoCommit(false); // 开始事务
// 执行多个操作
updateCategoryName(...);
updateCategoryDescription(...);
conn.commit(); // 提交事务
} catch (SQLException e) {
try {
if (conn != null) conn.rollback(); // 回滚事务
} catch (SQLException ex) {
// 处理回滚异常
}
throw e;
} finally {
closeResources(conn, null, null);
}
}
```
#### 2. 连接池优化
```java
// 在DBUtil中使用连接池
public class DBUtil {
private static DataSource dataSource;
static {
try {
Context ctx = new InitialContext();
dataSource = (DataSource) ctx.lookup("java:/comp/env/jdbc/libraryDB");
} catch (NamingException e) {
throw new RuntimeException("连接池初始化失败", e);
}
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
}
```
#### 3. 日志记录
```java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CategoryServlet extends HttpServlet {
private static final Logger logger = LoggerFactory.getLogger(CategoryServlet.class);
private void handleError(...) {
logger.error("操作失败: {}", message, cause);
// ...
}
private void closeResources(...) {
try {
if (rs != null) rs.close();
} catch (SQLException e) {
logger.warn("关闭ResultSet失败", e);
}
// ...
}
}
```
#### 4. 输入验证
```java
// 增强验证:名称唯一性检查
private void validateCategoryName(Connection conn, String name, Integer excludeId)
throws SQLException, ServletException {
PreparedStatement stmt = null;
ResultSet rs = null;
try {
String sql = "SELECT COUNT(*) FROM categories WHERE name = ?";
if (excludeId != null) {
sql += " AND id != ?";
}
stmt = conn.prepareStatement(sql);
stmt.setString(1, name);
if (excludeId != null) {
stmt.setInt(2, excludeId);
}
rs = stmt.executeQuery();
if (rs.next() && rs.getInt(1) > 0) {
throw new ServletException("分类名称已存在");
}
} finally {
if (rs != null) try { rs.close(); } catch (SQLException e) { /* 忽略 */ }
if (stmt != null) try { stmt.close(); } catch (SQLException e) { /* 忽略 */ }
}
}
// 在addCategory和updateCategory中调用
validateCategoryName(conn, name, id); // update时传入当前ID
```
### 结构优势总结
1. **资源安全**:
- 统一资源关闭方法确保连接、语句和结果集总是被释放
- finally块保证资源关闭即使在异常情况下也能执行
2. **错误处理**:
- 分层异常处理(外层分类处理,内层具体处理)
- 保留原始异常信息(使用异常链)
- 统一错误页面跳转机制
3. **业务完整性**:
- 删除操作前的关联检查防止数据不一致
- 名称唯一性验证保证数据质量
- 事务支持确保多个操作的原子性
4. **代码复用**:
- 辅助方法减少重复代码(参数验证、资源关闭等)
- 统一的操作模式提高可维护性
- 可扩展的结构便于添加新功能
这种结构确保了分类管理功能的健壮性和可维护性,同时提供了良好的用户体验和数据完整性保护。