65.9K
CodeProject 正在变化。 阅读更多。
Home

SQL 数据库访问

starIconstarIconstarIconstarIconstarIcon

5.00/5 (9投票s)

2019年3月18日

MIT

2分钟阅读

viewsIcon

11061

SQL 数据库访问

当我最初打算写一篇关于从 C++ 访问数据库的文章时,我原本打算写关于 MySQL Connector/C++。 但出于某种原因,我对此并不满意。 睡了一晚后,我意识到我之所以不感兴趣,是因为它

  1. 仅限于单一数据库后端,并且
  2. API 过于底层。

我希望写一个支持多个数据库后端,并尽可能抽象连接细节的库。 理想情况下,我希望有一个库能让你更接近 SQL 语法,而不是处理 C++ 机制。 因此,在寻找酷炫且可移植的 C++ 库的过程中,我决定继续寻找……

然后,我发现了 SOCI – The C++ Database Access Library🙂 它具备了我所需要的一切:多后端支持(DB2、Firebird、MySQL、ODBC、Oracle、PostgresSQL 和 SQLite3),以及非常自然的 SQL 查询方式,这归功于运算符重载和模板魔法。 甚至他们的 第一个示例 也故意没有添加注释,因为它的可读性和理解性都很高。

除了非常自然的 SQL 后端交互方式之外,我最喜欢的是它允许你以非侵入性的方式(无需更改现有代码)将自定义数据结构存储到数据库表并从中检索。

所以我使用 brew 包管理器 在我的 Mac 上安装了 MySQL 服务器,并开始编写代码。 在 30 分钟内,我创建了一个可以连接到我的数据库服务器、创建数据库和表、从我的自定义 Person 数据结构中插入行到表、统计行数、检索具有给定 ID 的表条目,并最终清理自身的示例。

唯一需要解释的代码是 struct type_conversion<Person> 对象。 这是 SOCI 将自定义数据结构转换为数据库以及从数据库转换为自定义数据结构的机制,需要 2 个方法:from_base 将一组行值转换为结构,而 to_base 则反之。 其余部分不言自明! 以下是如何开始使用

#include <iostream>
#include <string>
#include <exception>
#include <soci/soci.h>
#include <soci/mysql/soci-mysql.h>

using namespace std;
using namespace soci;

struct Person
{
	int ID;
	string FirstName;
	string LastName;
	int DOB;
	string EMail;
};

namespace soci
{
	template<>
	struct type_conversion<Person>
	{
		typedef values base_type;

		static void from_base(const values& v, indicator, Person& p)
		{
			p.ID = v.get<int>("ID");
			p.FirstName = v.get<string>("FirstName");
			p.LastName = v.get<string>("LastName");
			p.DOB = v.get<int>("DOB");
			p.EMail = v.get<string>("EMail");
		}

		static void to_base(const Person& p, values& v, indicator& ind)
		{
			v.set("ID", p.ID);
			v.set("FirstName", p.FirstName);
			v.set("LastName", p.LastName);
			v.set("DOB", p.DOB);
			v.set("EMail", p.EMail);
			ind = i_ok;
		}
	};
}

int main()
{
	try
	{
		session sql(mysql, "host=localhost user=root password=''");

		sql << "CREATE DATABASE blog";
		sql << "USE blog";
		sql << "CREATE TABLE people (ID INT, FirstName TEXT, LastName TEXT, DOB INT, EMail TEXT)";

		Person him{1, "Martin", "Vorbrodt", 19800830, "martin@vorbrodt.blog"};
		Person her{2, "Dorota", "Vorbrodt", 19800127, "dorota@vorbrodt.blog"};
		sql << "INSERT INTO people (ID, FirstName, LastName, DOB, EMail) 
                       VALUES (:ID, :FirstName, :LastName, :DOB, :EMail)", use(him);
		sql << "INSERT INTO people (ID, FirstName, LastName, DOB, EMail) 
                       VALUES (:ID, :FirstName, :LastName, :DOB, :EMail)", use(her);

		int count{};
		sql << "SELECT COUNT(*) FROM people", into(count);
		cout << "Table 'people' has " << count << " row(s)" << endl;

		Person pOut{};
		sql << "SELECT * FROM people WHERE ID = 1", into(pOut);
		cout << pOut.FirstName << ", " << pOut.LastName << ", " << pOut.DOB << ", " 
                                       << pOut.EMail << endl;

		sql << "DROP TABLE people";
		sql << "DROP DATABASE blog";
	}
	catch (exception& e)
	{
		cerr << e.what() << endl;
	}
}

程序输出

Table ‘people’ has 2 row(s)
Martin, Vorbrodt, 19800830, martin@vorbrodt.blog
© . All rights reserved.