首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> MySQL 資料庫 >> MySQL 綜合教學

Web 項目分層架構實戰:基於 DBHelper 模式的用戶增刪改查完整實現

     編輯:MySQL 綜合教學

Web 項目分層架構實戰:基於 DBHelper 模式的用戶增刪改查完整實現

本文記錄一個經典 Java Web 專案(Manage)從零構建的過程:五層封裝、DBHelper 通用數據訪問、實體映射、Servlet 控制器、JSP 視圖、反射工具類、調試策略與心得體會。

專案結構


src/
├── dao/           # 數據訪問層
│   ├── UserDao.java
│   └── TestDao.java
├── dbhelper/      # 通用數據庫操作封裝
│   └── DBHelper.java
├── entity/        # 實體類
│   └── User.java
├── servlet/       # 控制層
│   └── UserServlet.java
├── tools/         # 工具類
│   └── ReflectUtil.java  # 反射自動映射 ResultSet → Entity
└── web/
    ├── user.jsp      # 列表/新增/編輯頁
    ├── show.jsp      # 詳情頁
    └── update.jsp    # 修改頁

核心組件實現要點

1. DBHelper:JDBC 模板化


public class DBHelper {
  private static final String URL = "jdbc:mysql://localhost:3306/manage?useSSL=false";
  private static final String USER = "root";
  private static final String PASS = "123456";
  
  static { Class.forName("com.mysql.jdbc.Driver"); }
  
  public static Connection getConn() throws SQLException {
    return DriverManager.getConnection(URL, USER, PASS);
  }
  
  // 通用增刪改
  public static int executeUpdate(String sql, Object... params) {
    try (Connection c = getConn(); PreparedStatement ps = c.prepareStatement(sql)) {
      setParams(ps, params);
      return ps.executeUpdate();
    }
  }
  
  // 通用查詢 + 反射映射
  public static  List executeQuery(String sql, Class clazz, Object... params) {
    try (Connection c = getConn(); PreparedStatement ps = c.prepareStatement(sql)) {
      setParams(ps, params);
      try (ResultSet rs = ps.executeQuery()) {
        return ReflectUtil.mapResultSet(rs, clazz);
      }
    }
  }
  
  private static void setParams(PreparedStatement ps, Object[] params) throws SQLException {
    for (int i = 0; i < params.length; i++) ps.setObject(i+1, params[i]);
  }
}

2. ReflectUtil:ResultSet 自動映射實體


public class ReflectUtil {
  public static  List mapResultSet(ResultSet rs, Class clazz) throws Exception {
    List list = new ArrayList<>();
    ResultSetMetaData meta = rs.getMetaData();
    int cols = meta.getColumnCount();
    while (rs.next()) {
      T obj = clazz.newInstance();
      for (int i = 1; i <= cols; i++) {
        String colName = meta.getColumnLabel(i);
        Object val = rs.getObject(i);
        // 駝峰轉換:user_name → setUserName
        String methodName = "set" + toCamelCase(colName);
        Method m = clazz.getMethod(methodName, val.getClass());
        m.invoke(obj, val);
      }
      list.add(obj);
    }
    return list;
  }
  // toCamelCase 實現略
}

3. UserDao:業務 SQL 封裝


public class UserDao {
  public List findAll() {
    return DBHelper.executeQuery("SELECT * FROM t_user", User.class);
  }
  public User findById(int id) {
    List list = DBHelper.executeQuery("SELECT * FROM t_user WHERE id=?", User.class, id);
    return list.isEmpty() ? null : list.get(0);
  }
  public int insert(User u) {
    return DBHelper.executeUpdate("INSERT INTO t_user(name,age,email) VALUES(?,?,?)", u.getName(), u.getAge(), u.getEmail());
  }
  public int update(User u) {
    return DBHelper.executeUpdate("UPDATE t_user SET name=?,age=?,email=? WHERE id=?", u.getName(), u.getAge(), u.getEmail(), u.getId());
  }
  public int delete(int id) {
    return DBHelper.executeUpdate("DELETE FROM t_user WHERE id=?", id);
  }
}

4. UserServlet:請求分發


@WebServlet("/user")
public class UserServlet extends HttpServlet {
  private UserDao dao = new UserDao();
  
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    String action = req.getParameter("action");
    if ("list".equals(action)) {
      req.setAttribute("users", dao.findAll());
      req.getRequestDispatcher("/user.jsp").forward(req, resp);
    } else if ("edit".equals(action)) {
      req.setAttribute("user", dao.findById(Integer.parseInt(req.getParameter("id"))));
      req.getRequestDispatcher("/update.jsp").forward(req, resp);
    } else if ("delete".equals(action)) {
      dao.delete(Integer.parseInt(req.getParameter("id")));
      resp.sendRedirect("user?action=list");
    } else {
      resp.sendRedirect("user?action=list");
    }
  }
  
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    req.setCharacterEncoding("UTF-8");
    String action = req.getParameter("action");
    User u = new User();
    u.setName(req.getParameter("name"));
    u.setAge(Integer.parseInt(req.getParameter("age")));
    u.setEmail(req.getParameter("email"));
    if ("add".equals(action)) {
      dao.insert(u);
    } else if ("update".equals(action)) {
      u.setId(Integer.parseInt(req.getParameter("id")));
      dao.update(u);
    }
    resp.sendRedirect("user?action=list");
  }
}

調試策略:分層驗證,縮小故障域

  1. DAO 層先行: 寫完 UserDao 立即在 TestDao.main() 單元測試 CRUD,確認 SQL 與映射無誤。
  2. Servlet 空殼驗證: 新建 Servlet 只打印 action 參數,部署訪問確認 URL 映射正確。
  3. JSP 靜態測試: 先寫死數據在 JSP 顯示,確認 EL 表達式、JSTL 標籤語法無誤。
  4. 端到端串聯: 逐層替換靜態數據為真實調用,每替換一層驗證一次。
  5. 異常定位: 瀏覽器 500 頁面 → 查看 Tomcat catalina.out 堆疊 → 定位到具體行號 → 修復 → 熱部署驗證。

心得與最佳實踐

  • 小步快跑: 每個方法寫完即測,不要積累百行代碼再調試。
  • 反射映射雙刃劍: 省去手工 rs.getString() 但引入性能開銷與類型匹配風險,生產環境建議緩存 Method 對象或改用 MyBatis。
  • 配置外部化: 數據庫連接參數必須放 db.properties,DBHelper 讀取配置文件而非硬編碼。
  • 編碼統一: request.setCharacterEncoding("UTF-8")、JSP pageEncoding="UTF-8"、MySQL characterEncoding=UTF-8、Tomcat URIEncoding="UTF-8" 四位一體。
  • 事務邊界: 當前 DBHelper 單條 SQL 自動提交,多表操作需引入 Connection.setAutoCommit(false) 手動事務管理。

這個「手工構建 ORM」的練習,讓人深刻理解框架背後的原理:連接池、PreparedStatement、ResultSet 映射、異常轉換、事務邊界。建議後續引入 MyBatis + Spring,將樣板代碼交給框架,專注業務邏輯創新。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved