2012年5月20更新:
1. 连Oracle没有这个问题,修改了标题
2.可以使用VC6生成这些东西,不过和VS2008生成的有一些不同。
==============
这两天做数据库实验,VS2008连接MySQL数据库的时候,或者添加类的时候,每次勾选“绑定所有列”都会导致VS2008崩溃,上网一番搜索也没找到什么合适的解决方式。找到的一些文章都是建议手工绑定。
虽然我用的表不超过十个,每个表里的属性也不超过十个,不过总觉得手工去建立一个一个CRecordSet的派生类,再自己在*.h添加变量,在*.cpp添加诸如“RFX_Text(pFX, _T("location"), m_location);”这样的句子实在是太麻烦了……
于是自己写了一个小小的代码生成器来完成这项工作。其实思路很简单,就是读取*.sql文件里的create table部分,参照VS2008 Class Wizard自动生成对应的*.h和*.cpp,生成对应的*.h和*.cpp文件。分别为table对应的CRecordSet的派生类。之后把这些文件都拷贝到VS2008工程的文件夹里,添加到项目中就可以了。
生成器是按照自己写*.sql的习惯来写的,主要是方便自己使用,所以对sql文件有做了几个小限制:
1. 使用生成代码器前把create table替换成createtable
2. table名字不包括“XXXXXXXX”,因为这个作为占位符放在sample.h和sample.cpp中供复制文件时替换
3. 目前只支持varchar和int类型,因为我暂时只需要这两个,不过扩展起来很方便
4. 习惯性写法“create table tablename( ...”中tablename后加一个括号,然后换行,所以代码生成器中直接去掉括号得到tablename
要手工改一下*.cpp里初始化部分的n_Fields……
如果发现新添加的这些类没有基类型,手工用Class Wizard添加一个CRecordSet的派生类再删掉就可以了。
代码草草加了一些注释,贴上来吧。
/*
CRecordSet派生类产生器
团子 2012年4月20日
sql文件要求:
处理前先把"create table"换成createtable
table名后换行
暂时只处理varchar和int类型,别的有需要再加
假设table不会叫"XXXXXXXX"
*/
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
const string output_dir="res\\"; //输出文件夹
const string sqlfilename="lab2_gen.sql"; //*.sql文件
const string suffix="set"; //类名=C+tablename+suffix
string tablename; //当前处理的table的名字+suffix
void copytofile(ofstream &dst, const char* filename)
{
ifstream src(filename);
if (!src.is_open())
{
printf("%s 未找到!\n", filename);
return;
}
int pos;
string tmp;
while (getline(src, tmp))
{
while ((pos=tmp.find("XXXXXXXX", 0))!=string::npos)
{
tmp.replace(pos, 8, tablename.c_str());
}
dst<<tmp<<endl;
}
src.close();
}
int main()
{
string str[2];
ofstream res_h, res_cpp;
freopen(sqlfilename.c_str(), "r", stdin);
int now=0, pre=1;
str[pre]="";
while (cin>>str[now])
{
const char* strpre=str[pre].c_str();
const char* strnow=str[now].c_str();
if (str[pre].find("createtable", 0)==0)
{
str[now].replace(str[now].find("(", 0),1,""); //如果习惯性写法中table名之后连着括号
tablename=str[now]+"set";
string hname=output_dir+tablename+".h";
string cppname=output_dir+tablename+".cpp";
printf("build %s %s\n", hname.c_str(), cppname.c_str());
res_h.open(hname.c_str(), ios::out);
res_cpp.open(cppname.c_str(), ios::out);
if (!res_h.is_open() || !res_cpp.is_open())
{
printf("输出文件打开失败!\n");
return 0;
}
copytofile(res_h, "sample1.h");
copytofile(res_cpp, "sample1.cpp");
}
if (str[now].find("varchar", 0)==0)
{
//变量名以m_开头
res_h<<"\tCString m_"<<str[pre]<<";"<<endl;
res_cpp<<"\tRFX_Text(pFX, _T(\""<<str[pre]<<"\"), m_"<<str[pre]<<");"<<endl;
}
else if (str[now].find("int", 0)==0)
{
//变量名以m_开头
res_h<<"\tint m_"<<str[pre]<<";"<<endl;
res_cpp<<"\tRFX_Int(pFX, _T(\""<<str[pre]<<"\"), m_"<<str[pre]<<");"<<endl;
}
//else if ... 如果要添加对更多类型的支持,这里可以相应扩展
if (str[now].find(");", 0)==0)
{
copytofile(res_h, "sample2.h");
copytofile(res_cpp, "sample2.cpp");
res_h.close();
res_cpp.close();
}
now=1-now;
pre=1-pre;
}
return 0;
}
sample.h和sample.cpp是VS2008 Class Wizard自动生成的CRecordSet的派生类对应的文件修改而来的。拆成两部分,中间部分正好是用来添加变量或者是RFX_Text之类的。
sample1.h
// XXXXXXXX.h : CXXXXXXXX 的声明
#pragma once
// 由团子的code_gen生成~
class CXXXXXXXX : public CRecordset
{
public:
CXXXXXXXX(CDatabase* pDatabase = NULL);
DECLARE_DYNAMIC(CXXXXXXXX);
// 字段/参数数据
sample1.cpp
// XXXXXXXX.h : CXXXXXXXX 类的实现
// CXXXXXXXX 实现
// 由团子的code_gen生成~
#include "stdafx.h"
#include "XXXXXXXX.h"
IMPLEMENT_DYNAMIC(CXXXXXXXX, CRecordset)
CXXXXXXXX::CXXXXXXXX(CDatabase* pdb)
: CRecordset(pdb)
{
m_nFields = 0;
m_nDefaultType = dynaset;
}
CString CXXXXXXXX::GetDefaultConnect()
{
return _T("换掉我"); //换成自己的
}
CString CXXXXXXXX::GetDefaultSQL()
{
return _T("");
}
void CXXXXXXXX::DoFieldExchange(CFieldExchange* pFX)
{
pFX->SetFieldType(CFieldExchange::outputColumn);
sample2.h
// 以下字符串类型(如果存在)反映数据库字段(ANSI 数据类型的 CStringA 和 Unicode
// 数据类型的 CStringW)的实际数据类型。
// 这是为防止 ODBC 驱动程序执行可能
// 不必要的转换。如果希望,可以将这些成员更改为
// CString 类型,ODBC 驱动程序将执行所有必要的转换。
// (注意: 必须使用 3.5 版或更高版本的 ODBC 驱动程序
// 以同时支持 Unicode 和这些转换)。
// 重写
// 向导生成的虚函数重写
public:
virtual CString GetDefaultConnect(); // 默认连接字符串
virtual CString GetDefaultSQL(); // 记录集的默认 SQL
virtual void DoFieldExchange(CFieldExchange* pFX); // RFX 支持
// 实现
#ifdef _DEBUG
virtual void AssertValid() const;
virtual void Dump(CDumpContext& dc) const;
#endif
};
sample2.cpp
}
/
// CXXXXXXXX Õï¶Ï
#ifdef _DEBUG
void CXXXXXXXX::AssertValid() const
{
CRecordset::AssertValid();
}
void CXXXXXXXX::Dump(CDumpContext& dc) const
{
CRecordset::Dump(dc);
}
#endif //_DEBUG