所謂的模型,就是返回給瀏覽器的真正有意義信息的抽象集合,但在實際編程中一般會混雜一些業務邏輯代碼,所以並不總是那麼純粹。
模型的狀態一般會保存在數據庫或者其他外部系統中,但是從控制器只關心它的狀態(用於生成視圖的具體信息),而不關心它的底層實現。
直接使用SQL
語句對數據庫進行操作,有時會顯得十分煩雜,使得代碼晦澀難懂,特別是當數據表的結構十分複雜的時候。爲了儘量消除這部分阻力,可以使用TreeFrog
的O/R映射
系統(SqlObject
),同時對於web
程序來說,增刪查改基本上是必需的基本功能,而且這些SQL
語句有相當的規律性,這些就爲ORM
提供了充分的必要性和可行性。
總的來說,實現ORM
模型有一下的優點:
- 代碼結構更加清晰直觀;
- 更好地隱藏和屏蔽控制器端毫不關心的內在數據信息;
- 解除一個模型對應一個數據表的限制,增加模型類設計的自由度
1.模型層的API
通過生成器創建的模型類,其一般內容包括各個屬性的gettet
和setter
,我們可以通過create
和get
等靜態方法來獲得模型類的實例對象。我們來看下之前的日誌文章管理系統的部分代碼:
static Blog create(const QString &title, const QString &body);
static Blog create(const QVariantMap &values);
static Blog get(int id); //獲取指定ID的模型對象
static Blog get(int id, int lockRevision);
static QList<Blog> getAll(); //獲取所有的模型對象
如果執行create()
方法,在獲取對象實例的同時還會將它保存到數據庫中。
首先我們先來看看TAbstractModel
類的定義方法:
virtual bool create(); //創建
virtual bool save(); // 創建或修改
virtual bool update(); // 修改更新
virtual bool remove(); // 刪除
virtual bool isNull() const; // 判斷是否存在於數據庫中
virtual bool isNew() const; // 是否仍未更新到數據庫中
virtual bool isSaved() const; //是否已經更新到數據庫中
void setProperties(const QVariantMap &properties);
其中,save()
方法比較特殊,如果目標對象不在數據庫中,那麼就會在內部調用create()
方法,否則將會調用update()
方法。不過不確定是否需要重新創建一個新的模型對象,就可以使用save()
方法。
2.模型類的注意事項
一般而言,類的獨立性越高可重用性也越強,因此我們在設計模型層時,也要儘量地減少它與其他模塊的耦合性和依賴性。
通常每個模型都會有對應的一個數據表,而各個模型之間的關係就反映在它們之間的某些關聯屬性上(外鍵)。下面是一些編程中常用到的API
:
texport
方法(在前面的控制器已經能提到過)可以把對應的變量導出到視圖層,但是該變量必須具備一下條件:
- 公開的默認構造函數;
- 公開的複製構造函數;
- 公開的析構函數;
- 被進行
Q_DECLARE_METATYPE
宏定義;
通過生成器創建的模型層類就滿足上述的條件,因爲它們都繼承於TAbstractModel
這個類,可以很方便地訪問和操作數據庫。(同時也需要注意的是,如果某個模型類不需要訪問數據庫,那麼可以不必繼承TAbstractModel
。
3.如何自定義模型類
正如之前說過的,通過生成器創建的模型的命名規則十分簡單而僵硬,就是表名去掉下劃線然後把首字母大寫。我們可以通過在命令的最後加上一個參數作爲該模型的自定義名稱:
$ tspawn model blog_entries MyBlogEntry ← 生成自定義命名的模型
模型並不一定需要和某個數據表相關聯,如果只是爲了往視圖層傳輸數據信息,也就是作爲一個數據信息的聚合載體。可以不必使用生成器,直接寫模型類的代碼,像下面這樣去定義我們的模型類。
class T_MODEL_EXPORT Post
{
public:
//已經聲明好了默認構造函數、拷貝構造函數、析構函數
//這部分的代碼可以自由編寫
};
Q_DECLARE_METATYPE(Post)//爲了實現往視圖層傳輸單體數據
Q_DECLARE_METATYPE(QList<Post>) //爲了實現往視圖層傳輸列表數據