所谓的模型,就是返回给浏览器的真正有意义信息的抽象集合,但在实际编程中一般会混杂一些业务逻辑代码,所以并不总是那么纯粹。
模型的状态一般会保存在数据库或者其他外部系统中,但是从控制器只关心它的状态(用于生成视图的具体信息),而不关心它的底层实现。
直接使用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>) //为了实现往视图层传输列表数据