[其它] 一个简单数据访问接口的设计

dayn9 2007-10-24
我设计一个数据库访问接口,目标是简单易用,STL兼容,类型安全,效率不太差,尽量非侵入。
已实现大部分功能,但我长年困在C/C++世界,视野和经验都很有限,几个地方感觉设计的不太好。
烦请各位老大有空帮看看,指点一二。

我只建立了两个类,数据库类和记录集类。下面是简单示例。

	//一个POD类型,要在内存中操作并保存到数据库
	struct Student {
		int		num;
		string	name; 
		int		age;
		
		bool operator==(const Student& s) const {
			return num == s.num 
				&& name == s.name 
				&& age == s.age;
		}
	};

	class3f(Student, num, name, age);		//注册Student类型的meta信息

	int main()
	{
		Sqlite db;
		db.open("test.db");
		
		//建表
		TypeInfo info = typeof(Student());	//获取Student的meta信息
		Table tab(info);					//根据meta信息创建表结构信息
		db.drop(tab);
		db.create(tab);

		//向表中填充数据
		Student kids[] = {
			{11,	"jiang",	11,},
			{12,	"kang",		9,},
			{13,	"cao",		12,},
			{14,	"ma",		7,},
			{15,	"tu",		13,},
		};

		ActiveSet<Student> as1(kids, kids + 5);		//数组复制到记录集
		db.update(as1);								//记录集提交到数据库表

		//读取数据到记录集,增删改,之后提交
		ActiveSet<Student> as2;
		db.fill(as2, "age > 10");

		Student s1 = {19,	"ji", 13};
		as2.push_back(s1);

		as2[3].remove();

		Student s2 = as[2];
		s2.age += 2;
		as2[2] = s2;

		db.update(as2);				//只提交有变化的数据

		//用stl容器操作数据
		vector<Student>	v;
		list<Student> l;
		map<int64, Student> m;		//key是表中的主键,有主键数据才能提交更新

		db.fill(v);
		db.fill(l);
		db.fill(m);
		db.update(m);				//要提交全部数据,无论变化与否
	}
dayn9 2007-10-24
刚才贴的代码,格式惨不忍睹,请教google才学会贴代码,惭愧...
jimmy_c 2007-10-30
不错啊。有什么问题吗?
dayn9 2007-10-31
最头痛的一个问题是string的映射。

C++中string是不限长度的,而数据库中的字符串是要设定长度的。而如果string映射成长度无限的text类型,则会损失一部分检索能力和效率。
一个办法是制定字符串的长度。下面是元信息提取的宏,我不知道如何实现指定name的长度而不至于太丑陋。


class3f(Student, num, name, age);       //注册Student类型的meta信息  


jimmy_c 2007-10-31
class3f不应该是模板方法么?
定义个
class StringType
{
private:
  string _sectionName;
  int _sectionLength;
public:
  StringType(string sectionName, int sectionLength);
};
class TextType
{
private:
  string _sectionName;
public:
  StringType(string sectionName);
};

做参数。
然后模板特化。
写成:

class3f(Student, num, StringType(name, 256), age);

没有太仔细考虑,不知这样可行不?
jimmy_c 2007-10-31
另一个想法,和上面的问题无关:class3f能不能写成变参的呀?
dayn9 2007-11-01
class3f是个宏,这是很无奈的选择,也是我觉得实现不好的地方。

这个宏不光要取出类型信息,还要取出类型和成员的字符串名称,这是模板做不到的。类型名称要映射成数据库表名,成员名称要映射成数据库的字段名称。

宏不能可变参数,连重载都不能。所以才这样丑陋:
class3f,表示 a class of 3 fields
同样还有class4f ... class10f,够丑陋吧,真的很无奈。
要在C++中实现简单的反射,有更好的办法么?

class3f(Student, num, StringType(name, 256), age);


你的这个提议对我很有启发,正在想法实现。
jimmy_c 2007-11-01
记得boost也有大量类似的代码。

丑陋什么的我的美感神经比较粗,到没觉得什么。不过我是觉得经常碰到table有几十个字段的,这种定义方法,效率就低了。还很可能不够用。所以觉得应该用变参函数好一些。

模板函数而言,如果中间定义一组字段类型类,就像StringType一样,应该也可以处理。不过对于数据库处理而言,其实简单的OO,给它们定义一个基类,应该也足够了,不应该有性能上的问题。

我的想法最终调用大概像这样:

classnf<Student>(NullType(),
								IntType(&Student::num, "num"), 
								StringType(&Student::name, "name", 256), 
								IntType(&Student::age, "age")),
								NullType());


两个NullType标记参数表的开始结尾。

如果高兴,可以把IntType(...), StringType(...)定义成宏。
dayn9 2007-11-05
你提到的也是很现实的问题。
我也曾考虑过,如果表定义实在处理不好,干脆采用RoR中的做法,但如果通过数据定义生成类定义,是不是对OO程序的一种明显的侵入?因为我们的程序和RoR不同,毕竟不是数据主导的。

我还是比较倾向于你的这个提议:
class3f(Student, num, StringType(name, 256), age);
jimmy_c 2007-11-05
dayn9 写道
但如果通过数据定义生成类定义,是不是对OO程序的一种明显的侵入?因为我们的程序和RoR不同,毕竟不是数据主导的。

这句话是什么意思?为什么通过数据定义生成类定义就不是OO了呢?

其实template本身就不是OO的范畴,我提到的变参函数完全也不属于OO。但是还是不能理解你的话的意思。
Global site tag (gtag.js) - Google Analytics