浅谈基于Qt反射机制的对象关系映射框架

2020-10-09 11:13童辰蒋超蒋俊铭
科学与信息化 2020年26期

童辰 蒋超 蒋俊铭

摘 要 C++语言不具备高级语言如Java的反射机制,无法在程序运行时获取类的结构信息,在开发数据库交互程序时不具备Hibernate、Mybatis等对象关系映射能力。Qt是基于C++語言的工具包,提供QProperty宏获取类结构信息,本文基于该特性,开发对象关系映射框架,实现数据库数据到类对象的双向转换,支持数据库的增、删、改、查操作,支持主流的Oracle和Mysql数据库,支持缓存操作,提高访问效率。同时开发配套工具自动生成数据类代码。

关键词 对象关系映射;Qt;Oracle;Mysql

引言

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的技术。简单来说,ORM是通过使用类成员变量和数据库之间映射的元数据,将程序中的对象自动持久化到关系数据库中。而C++作为主流且历史悠久的开发语言,不具备在程序运行时获取类的结构信息的能力。C++的数据类型如何适配Oracle和Mysql的数据类型。数据库操作均需要使用Sql语句,程序函数如何转换为Sql语句。对于频繁访问的程序对象如何设计缓存避免访问数据库提高效率。类文件通常代码比较多,如何加快代码开发速度,这些问题都需要研究讨论。

1运行时类结构信息获取

Qt的类对象在C++的基础上封装了类数据结构信息并支持运行时获取接口。首先需要对其实现原理进行了解[1]。通常C++的编译过程为预处理->编译->链接->运行,Qt编译的过程中,有一个moc的过程,即为moc->预处理->编译->链接->运行。在moc过程中,需要识别一些特殊的宏Q_OBJECT、Q_PROPERTY、Q_INVOKABLE,然后根据头文件生成moc实现文件,实现文件中包含了类数据结构信息并支持运行时获取接口,获取接口包括metaObject(),property(),className(),method(),constructor()等。

2程序类型和数据库类型的转换

不同数据库的数据类型定义是有差异的,Oracle和Mysql在数据类型定义上差异较大,如何屏蔽数据库类型的差异性?可以使用QVariant通用数据类型与数据库类型进行第一次映射,QVariant类的函数屏蔽了类型操作细节,与QSqldatabase类配合,通过加载不同的数据库插件屏蔽了数据库类型差异[2]。

C++常用的数据类型有int、double等,Qt也定义了QString、QDatetime等更为方便实用的类对象,对于int、double、QString、QDatetime等常用类型,QVariant能自动根据数据库类型进行转换,但是一些性能或空间限制下,将数据库时间类型设置为int,则QVariant无法自动转换。本框架使用Q_CLASSINFO()宏进行映射,比如上述例子,标注类成员为int类型,对应的数据库类型为时间类型,在生成数据库sql语句时根据此映射进行转换。

3类成员变量生成数据库Sql语句

数据库支持Sql进行增删改查,限制数据库数据可以映射到程序类对象,自然对数据库的增删改查也要提升到对程序类对象,需要对增删改查四个动作分别封装处理函数。类对象与业务相关所以字段均不一致,而处理函数参数只能是一种类型。有两种解决方案:①使用模板参数作为函数入参,缺点是代码实现必须编写在头文件中,不利用功能更新升级,调用时需要注明参数类型,代码冗长。②抽象公共基类,所有类均继承该类,利用多态特性,将基类作为函数入参,这样分离了接口和实现,扩展性好。综合比较选择方案2。

对数据库数据的操作包括增删改查四种,基本语法基本一致,不同数据差异主要在字段不一致,只要保证类成员变量名和数据库表每一个字段名对应,即可实现数据对象到Sql语句的转换,包括如下4种情况。增加数据,Insert into 表名字段名 values(字段值)。删除数据,Delete from 表名 where 主键=值。修改数据,update 表名字段名 values(字段值)。查询,Select 字段名 from 表名 where 条件 order 条件。从上述可见,不同操作差异的地方就是表名、字段名、主键、条件,在类定义中只要定义了前三者即可,条件的可变性较大,可在处理函数扩展参数,作为入参输入。

4程序缓存设计

程序设计中经常出现核心数据频繁使用,如果每次均从数据库获取则延时大影响程序的运行效率,前面对数据库的操作已经封装了增删改查四种函数,则可以在函数实现中缓存数据。当增加数据到数据库成功同时增加缓存,删除数据库成功时删除缓存,修改数据库成功时修改缓存,查询数据库时同步增加缓存,如此就实现程序使用的每一个数据在缓存中均具备且和数据库保持状态一致。当数据量大时,要快速检索数据,使用Map结构进行存储,分两层进行检索,先按照类型再按照每一条数据的主键值进行检索,从而将检索效率提高到O(nlgn)时间复杂度级别。

5类代码文件生成工具

数据类代码因为包含成员变量的读写函数、元数据配置定义、其他配置定义、字段值打印等,代码量较大,若手写代码开发效率低。通过分析多个数据类代码文件,其差异的地方在于成员变量名、类名、表配置名、主键名等,而这些信息均可以从对应的数据库表拿到,因此所有的信息来源是数据库表,配合数据类代码模板即可实现完整的数据类代码。生成工具提供数据库表名的输入框,从数据库表基础信息中提取字段名、主键等信息,替换代码模板中的占位符,即可生成数据类代码。

6结束语

本文研究了Qt运行时类结构信息获取方法,解决数据库类型和程序类型映射问题,提出类成员变量生成Sql语句的方案,进一步优化程序缓存设计,研究类代码文件生成工具,从而实现C++语言下的对象关系映射框架,极大提高了数据库交互程序的开发效率和稳定性。

参考文献

[1] Mark S,白建平.Qt高级编程[M].北京:电子工业出版社,2011:171.

[2] 王维波,栗宝鹃,候春望.Qt 5.9 C++ 开发指南[M].北京:人民邮电出版社,2018:213.