規範 J2EE 架構中,各層維護專屬數據模型,嚴禁跨層傳播:
| 層級 | 數據模型 | 來源/職責 | 邊界 |
|---|---|---|---|
| Web 視圖層 | Form Bean (ActionForm) | HTML Form 提交數據,1:1 對應頁面字段 | Action 截止,不得進入 Service |
| 業務邏輯層 | VO (Value Object) / DTO | 業務邏輯交互數據,承載業務規則、驗證、計算 | Service 內部,不得滲透到 View |
| 持久化層 | PO (Persistent Object) | 數據庫表映射,包含關聯集合、外鍵、版本號等 ORM 元數據 | DAO 內部,不得外洩 |
UserFormName 值對象 + 權限集合 + 組織集合 + 貼子集合 → UserPOUserForm + Action 轉換邏輯,UserPO、數據庫零變動LazyInitializationExceptionHibernate PO 支持脫管狀態、延遲加載、增脏檢查,理論上可作為 VO 在 Service 層流轉。但強烈建議保持邏輯分離:
UserVO 精簡屬性 (id、name、email、status),Service 返回 VOBeanUtils.copyProperties(userPO, userVO) 或 MapStruct 自動映射SELECT new UserVO(u.id, u.name) FROM User u),避免加載完整 PO例外:極簡 CRUD 內部系統,可暫時合併,但需在架構文檔標註技術債,重構時拆分。
// 1. JSP 提交 Form
// 2. Struts 自動填充 UserForm (firstName, middleName, lastName, email, password)
// 3. Action.register()
public ActionForward register(ActionMapping mapping, ActionForm form) {
UserForm uf = (UserForm) form;
// 4. Form → VO 轉換 (Action 責任)
UserVO vo = new UserVO();
vo.setFullName(uf.getFirstName() + " " + uf.getMiddleName() + " " + uf.getLastName());
vo.setEmail(uf.getEmail());
vo.setRawPassword(uf.getPassword());
// 5. 調用業務服務
userService.register(vo); // Service 內部 VO → PO → DAO.save(po)
// 6. 返回結果
return mapping.findForward("success");
}
// 1. JSP 提交查詢 Form (fullName)
// 2. Action.query()
public ActionForward query(ActionMapping mapping, ActionForm form) {
QueryForm qf = (QueryForm) form;
// 3. Form → 參數對象
Name name = new Name(qf.getFirstName(), qf.getMiddleName(), qf.getLastName());
// 4. 業務查詢返回 VO (不含敏感/集合字段)
UserVO vo = userService.findByName(name); // Service: PO → VO 投影
// 5. VO → Form Bean (用於頁面回顯)
UserForm uf = new UserForm();
BeanUtils.copyProperties(vo, uf);
request.setAttribute("userForm", uf);
return mapping.findForward("detail");
}
Form Bean 面向頁面,VO 面向業務,PO 面向存儲。 三層模型各司其職,在 Action (Web→Service) 與 Service/DAO (Service↔Persistence) 邊界進行顯式轉換。這看似增加樣板代碼,實則是為系統長期可維護性、安全性、擴展性支付的必要保費。Hibernate 的強大特性不應成為打破分層邊界的借口,紀律性架構才能應對業務複雜度的指數級增長。