企業級應用普遍採用「表現層 + 控制層 + 業務層 + **持久層 (DAO)** + 數據源」分層架構。本文闡述 DAO (Data Access Object) 模式核心思想、職責邊界、事務控制分層策略,並給出財務帳號管理的 DAO 接口設計規範。
| 層次 | 職責 | 事務控制 |
|---|---|---|
| 表現層 (Controller) | HTTP 請求接收、參數校驗、視圖渲染/JSON 返回 | 無 |
| 業務層 (Service) | 核心業務規則、流程編排、多 DAO 組合、**事務邊界管理** (@Transactional) | 統一控制 |
| 持久层 (DAO) | 單表/關聯表 CRUD、SQL 執行、實體映射、異常捕獲轉換為非檢查異常 | 不控制 (無 @Transactional) |
| 數據源 | 連接池 (HikariCP)、數據庫實例 | 無 |
public interface FinanceAccountDao {
// 1. 新增財務帳號
long insert(FinanceAccount account);
// 2. 修改帳號基本信息 (除狀態外)
int updateInfo(FinanceAccount account);
// 3. 修改帳號狀態 (啟用/停用/凍結)
int updateStatus(Long accountId, AccountStatus status);
// 4. 查詢所有帳號 (分頁)
List<FinanceAccount> selectPage(int pageNum, int pageSize);
// 5. 根據 ID 查詢帳號詳情
FinanceAccount selectById(Long accountId);
// 6. 根據條件動態查詢 (可選)
List<FinanceAccount> selectByCondition(FinanceAccountQuery query);
}
FinanceAccount 包含 id、account_no、account_name、balance、status、create_time、update_time 等字段,對應表列。SQLException,包裝為 DaoException (RuntimeException) 拋出,Service 層統一處理。Connection,**不在 DAO 中開啟/提交/關閉事務**。LIMIT ?, ? + COUNT(*) 統計總數;大偏移量優化採用「游標/ID 下標」模式。❌ 錯誤:在 DAO 方法內部 con.setAutoCommit(false) / commit()。導致:多 DAO 組合時無法保證原子性、DAO 無法複用、連接洩漏風險。
✅ 正確:Service 方法加 @Transactional (Spring) 或手動控制 Connection 事務,調用多個 DAO 方法:
@Transactional
public void transfer(Long fromId, Long toId, BigDecimal amount) {
FinanceAccount from = dao.selectById(fromId);
FinanceAccount to = dao.selectById(toId);
// 業務校驗...
from.setBalance(from.getBalance().subtract(amount));
to.setBalance(to.getBalance().add(amount));
dao.updateInfo(from); // 同一事務
dao.updateInfo(to); // 同一事務
}
DAO 是持久層的「門面」,職責單一:SQL 執行與對象映射。事務、緩存、鎖、幂等性等橫切關注點屬於 Service 或基礎設施層。良好的分層讓代碼可測、可維、可擴展。