面向對象分析設計中,實體物件需持久化存儲。檔案存儲難以表達物件關聯、效能不足;物件導向資料庫(OODB)尚不成熟。關係型資料庫(RDBMS)技術成熟、普及率高,但與 OO 語言存在「阻抗失配」。為降低耦合、提升開發效率,需在應用系統與資料庫間建立物件存儲模組。
資料表對應實體物件,欄位對應屬性。各表結構不同(欄位數、型別、約束),難以統一類別表示;但共同擁有表名、CRUD 操作。
建立抽象基類 CEntity:
Read()、Write()、Delete() 等,參數為資料庫連接。參考 GoF 命令模式:
Execute()、Undo() 介面。CEntity 派生類)的服務,如 SaveCommand、LoadCommand、DeleteCommand。CStorage 管理資料庫連接,執行命令佇列,提供 SaveEntity()、ReadEntity() 等統一服務。優勢:命令與演算法分離、調用者與具體命令解耦、命令可序列化/記錄/撤銷、新增命令無需修改既有代碼。
class CEntity {
public:
CEntity(const CString& name);
virtual ~CEntity();
virtual BOOL Read(CAdoRecordSet* rs) = 0;
virtual BOOL Write(CAdoRecordSet* rs) = 0;
virtual BOOL Delete(CAdoRecordSet* rs) = 0;
protected:
CString m_strEntityName; // 對應資料表名
};
參數 T 必須繼承 CEntity。負責依 SQL 查詢生成實體集合:
template <class T>
class CEntitySet {
public:
CEntitySet(const CString& name);
BOOL Execute(); // 執行查詢,填充 m_vecEnt
T* operator[](int idx) const;
int GetSize() const;
void SetSQLString(const CString& sql);
void SetConn(CAdoConnection* conn);
private:
CString m_strEntityName, m_strSQL;
CAdoConnection* m_pConn;
std::vector<T*> m_vecEnt;
};
Execute() 實作:開啟 Recordset → 逐筆 new T → 呼叫 Read() 映射欄位 → 加入向量。
SaveEntity(CEntity*)、ReadEntity(CEntity*)、DeleteEntity(CEntity*)。CEntity* 多態呼叫實體的私有存取介面,確保封裝性。// 初始化
m_Storage.SetConnString("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Book.mdb");
m_Storage.Initialize();
// 借書:關聯視圖查詢 → 實體存儲
CBorrowBook borrowbook; borrowbook.SetBookNumber("10001");
m_Storage.ReadEntity(&borrowbook);
CBorrow borrow;
borrow.SetPersonName(borrowbook.GetPersonName());
borrow.SetBorrowDate(borrowbook.GetBorrowDate());
m_Storage.SaveEntity(&borrow);
// 查詢價格 > 90 的圖書
CEntitySet<CBook> books("book");
books.SetConn(m_Storage.GetConn());
books.SetSQLString("price>90");
books.Execute();
for (int i=0; i<books.GetSize(); ++i) {
CBook* p = books[i];
// 顯示至 ListCtrl...
}
private,僅 CStorage/CEntitySet 透過基類指標多態調用,對應用層隱藏。本文展示以命令模式為核心、結合模板技術與多態機制的物件持久化框架。該設計實現了實體物件與關係表的透明映射,支援動態查詢集合生成,且擴充新實體無需修改框架核心,有效解決了 OO 應用與 RDBMS 的整合難題。