在后台开发中,我们经常需要和数据库打交道,而在 C++ 开发中,MySQL Connector/C++ 只提供了基础操作接口,复杂的业务常常需要一系列复杂的调用过程,非常容易出错,那有什么方法可以避免呢?TarsCpp 中提供了数据库操作类 TC_Mysql,使我们能够方便地进行数据库操作,提高业务开发效率。本文将对 TC_Mysql 进行介绍分析。
• MySQL o 简介 o MySQL 常用 API o 存在的问题 • TC_Mysql o 数据库配置接口 TC_DBConf o 数据库操作类 TC_Mysql o 构造函数 o MySQL 操作函数 o 数据存储类 MysqlRecord 与 MysqlData • 数据库异常类 TC_Mysql_Exception简介
数据库是计算机应用系统中一种专门管理数据资源的系统,以数据为中心,解决了传统文件存储数据存在的数据冗余、移植性差等问题。在后台开发中,数据库操作具有非常重要的地位,不可避免地需要和数据库打交道。现在市面上的数据库软件多种多样,最常用的便是 MySQL。它是一款安全、跨平台、高效的关系型数据库系统,由瑞典的 MySQL AB 公司开发、发布并支持。由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,使得很多公司都采用 MySQL 数据库以降低成本。
数据库的基础操作包括增、删、改、查四种操作,对应的在 MySQL 中为 Insert, Delete, Update, Select 等,能够方便地实现对数据库中数据的管理。 MySQL 常用 API MySQL 支持了各种主流的程序设计语言,提供了丰富的数据库操作 API 函数。在 C++ 开发中,MySQL 官方提供了相关的数据库连接动态库 MySQL Connector/C++,接口定义在 `mysql.h` 中,包含 MySQL 基础操作 API,像常用的 `mysql_real_connect`, `mysql_real_query`, `mysql_store_result`, `mysql_fetch_row` 等。其中• `mysql_real_connect` 用于创建到 MySQL 数据库服务的连接;
• `mysql_real_query` 用于执行 MySQL 语句;
• `mysql_store_result` 用于将执行结果保存到本地;
• `mysql_fetch_row` 用于获取返回结果中的行。
这四个函数一般情况下能够满足大部分需求,它们在 `mysql.h` 中的声明如下 mysql_real_connect 函数有很多参数,涵盖了连接数据库所需要的基本信息如 host, user, password 等,成功创建连接会获得一个 MYSQL 对象,并将参数中的 MYSQL * 指针 mysql 指向该对象,供其它操作使用。 mysql_real_query 函数需要传入刚才提到的连接对象指针,SQL 字符串 q 和字符串长度 length,返回执行结果的行数。 mysql_store_result 函数传入 MYSQL 对象指针,返回执行结果,类型为 MYSQL_RES。 mysql_fetch_row 传入获取的结果,返回每行的数据。存在的问题
然而使用这些接口进行一次 MySQL 操作是非常麻烦的一件事,下面我们通过一个例子来看看如何通过这四个函数实现一次 MySQL 查询操作上述代码在 `main` 函数中,用最简单的方式实现了查询操作,没有包含任何错误和返回值判断的逻辑,但是看起来已经很复杂了。而实际业务场景中通常还需要对一些错误码还有返回结果进行判断,比如连接失败或断开,返回值为空等,总的来说,存在以下几个问题
• 需要自己构造 SQL 语句,容易出错; • 需要开发者自己添加错误码和异常的判断和处理逻辑; • 每次查询返回的结果需要调用 `mysql_fetch` 系列的多个函数才能完成结果读取。可见,开发者需要关注的细节太多,会很大程度上降低了开发效率。因此,把开发者无需关注的或重复的过程和细节隐藏起来,将 MySQL 操作 API 进一步封装,显得非常必要。自己封装的话未免太过于小题大做,而且难免有疏漏,对开发者自身能力要求也比较高。常见的方式是引入完备的第三方库,TarsCpp 的工具组件中就包含数据库操作类 `TC_Mysql`,能够完美解决这些问题。
TC_Mysql 是 TarsCpp 中提供的 MySQL 操作类,定义在文件 tc_mysql.h(文末附链接) 中,对 MySQL C++ 库中提供的 API 进一步地封装,屏蔽了很多与业务开发无关的细节,使用上更加方便简单。
文件 tc_mysql.h 中定义了三个类 TC_DBConf, TC_Mysql, TC_Mysql_Exception。其中 TC_Mysql 类中还定义两个子类。如图所示
其中异常类 `TC_Mysql_Exception` 和数据库配置接口 `TC_DBConf` 作为辅助类,主要在类 `TC_Mysql` 中使用。
`TC_Mysql` 类中的两个子类 `MysqlRecord` 和 `MysqlData`,作为数据存储类,类似于 MySQL C++ 库中的 `MYSQL_ROW` 和 `MYSQL_RES`,用于保存每次查询返回的结果。
下面我们就来对每个类进行分析。
数据库配置接口 TC_DBConf
在类 `TC_Mysql` 中,使用 `TC_DBConf` 类型的成员变量 `_dbConf` 来保存 MySQL 配置信息。`TC_DBConf` 用于保存数据库的连接配置如 host、user 等,提供了 `loadFromMap` 接口,能够从 `map` 类型变量中读取数据库配置。我们可以通过两种方式来加载配置
• 直接对成员变量赋值
• 使用 loadFromMap 从 map 类型参数读取配置
TC_DBConf 的定义也非常简单,具体如下
数据库操作类 TC_Mysql
构造函数
`TC_Mysql` 提供了三个版本的构造函数,实现了多种初始化方式。
1. 支持传入一个 `TC_DBConf` 对象来进行初始化,简化了参数,降低了出错的可能,也能提高代码可读性。
2. 提供默认构造函数,不传入参数,后续使用时再进行初始化。
3. 与 `mysql_real_connet` 参数相似,传入数据库配置信息来初始化。
不同的是这种方式在构造对象时即完成 MySQL 连接的初始化,而且可以直接使用 `string` 类型字符串。`mysql_real_connect` 需要先通过 `mysql_init` 创建对象后才能调用(可见 MySQL 常用 API 部分的示例),并且只能传入 C 风格的字符串。
下面这个例子展示了这三种构造方式MySQL 操作函数
TC_Mysql 中的包含了 Insert、Update、Replace、Delete、Query 等常用操作的函数,更加符合一般使用场景,相比 MySQL C++ 库都通过 mysql_real_query 来实现,使用上要简单得多。常用的几个操作函数声明如下 更多其他操作函数参见 [tc_mysql.h](文末附链接) • Insert、Update、Replace、Delete 从上述定义中可以看出,增、删、改相关的操作不需要自己构建 SQL 语句,传入相关参数就能够完成操作。 实际上,构造 SQL 语句的过程封装在函数中。 其中参数 `sTableName` 为表名,`mpColumns` 为需要插入、更新或替换的数据, `sCondition` 为条件。下面通过一个例子来看看如何使用这些操作函数 可以看到上述示例中存在一个 TC_Mysql::FT 类型,定义如下 它是 TC_Mysql 类中定义的枚举类型,用于定义字段的类型为字符串还是数字,判断在构建 SQL 语句时是否需要添加引号 '' 并转义。 例如上述例子中,最后实际执行的 Insert SQL 语句中,abcd 有引号,25 没有引号,如下 • Query 增、删、改都有了,那么查(Query)呢?就是前面定义中的 `queryRecord` 了。与 `mysql_real_query` 类似,参数传入 SQL 语句字符串。 不同的是,queryRecord 传入参数类型为 string,不需要额外传入字符串长度;并且 queryRecord 直接返回查询结果记录,不需要再调用 mysql_store_result 等函数来获取(实际上这个过程封装在函数内,参见 TC_Mysql 源码 )。 具体使用方式如下 返回数据的类型为 MysqlData,它是 TC_Mysql 的子类,用于保存数据,相当于 MySQL C++ 库中的 MYSQL_RES,我们会在下一部分详细介绍它。读取 MysqlData 类型数据的过程非常友好方便,能够直接以数组的方式遍历,并且读取字段值的类型为 string 而不是 char *,不需要额外获取字符串长度。这也使得代码变得更加简洁清晰。数据存储类 MysqlRecord 与 MysqlData
类 TC_Mysql 中包含了两个子类 MysqlRecord 和 MysqlData,用于保存查询返回的数据,方便数据的读取。MysqlRecord 相当于 map,用于保存一条记录;MysqlData 相当于 MysqlRecord 类型数组,用于保存多条记录。MysqlRecord
MysqlRecord 类用于记录一条 mysql 的记录,相当于 MYSQL_ROW,在 MysqlData 中被使用。MysqlRecord 类型的对象能够直接使用下标访问数据,例如 MysqlRecord 类的定义如下 可以看到 MysqlRecord 重载了 [] 运算符,实现了像 map 一样的下标访问数据的方式。MysqlRecord 还包含 map 类型引用的成员变量 _record,因此实际保存记录的数据类型为 map。MysqlData
保存一次查询的所有 mysql 数据记录,相当于 MYSQL_RES,每条记录即为一个 MysqlRecord 类型的数据。MysqlData 类型的数据能够通过下标访问每条记录,与数组相似,比如下面的例子MysqlData 重载了 [] 运算符,实现了与数组一样的方式遍历和读取记录,每条记录类型为 MysqlRecord。实际保存数据的成员变量为 _data,类型为 vector< map >,即通过 vector 容器来保存所有记录。定义如下
数据库异常类 TC_Mysql_Exception
在前面 MySQL 的介绍中可以看到,常规 MySQL 的操作没有进行异常判断和处理,需要自己实现相关的逻辑。 tc_mysql.h 中定义了异常类 TC_Mysql_Exception,用于在 MySQL 操作出现错误时抛出异常,作为类 TC_Mysql 的辅助类被使用。例如下面是 TC_Mysql 中函数 queryRecord 的部分实现,返回的指针 pstRst 为空时抛出一个错误: TC_Mysql 类中包含了异常处理逻辑,并将异常通过 TC_Mysql_Exception 抛出。方便用户在使用时,能够直接通过 try catch 来捕捉异常,比如 TC_Mysql 在 MySQL Connector C++ API 基础上进一步封装,对一些常用的流程例如 Insert、Update、Replace 等操作封装了独立的操作函数,使开发者无需自行拼接 SQL 语句,降低了出错的可能;同时屏蔽了错误处理等逻辑,开发者无需关注,仅通过 try catch 语句即可捕捉异常。另外,通过类 MysqlRecord, MysqlData 保存返回数据类型,极大的方便了数据的读取。这些都使得开发者能够更加方便地操作 MySQL,专注于业务代码的开发,进一步提高开发效率。 TARS可以在考虑到易用性和高性能的同时快速构建系统并自动生成代码,帮助开发人员和企业以微服务的方式快速构建自己稳定可靠的分布式应用,从而令开发人员只关注业务逻辑,提高运营效率。多语言、敏捷研发、高可用和高效运营的特性使 TARS 成为企业级产品。附文中所有链接:
tc_mysql.h:https://github.com/TarsCloud/TarsCpp/blob/master/util/include/util/tc_mysql.h
TC_Mysql 源码:https://github.com/TarsCloud/TarsCpp/blob/master/util/src/tc_mysql.cpp
TARS基金会是Linux基金会下的非营利性、微服务基金会,致力于建设一个强大而灵活的微服务生态系统。无论你在哪个行业,无论你使用什么技术栈,这里能助你快速实现你的创意。
点“在看”让TARS小姐姐变好看