C++连接数据库有很多种方法,ODBC,ADO等等。我这里就采用了ADO的方法。

既然都涉及到数据库了,就直接做个带界面的吧。先打开VS2010,新建一个“MFC应用程序”,在里面记得选择“基于对话框”,因为这个比较简单,我就用这个举例了,其他的自己研究一下吧,然后一直下一步就可以了。

下面正式开始,先把对话框上的静态文本控件删了,没有什么用。再添加一个“List Control”控件,现在应该是这个样子的。

然后右键单击这个List Control,选择属性。在属性里找到View,改成Report,如图:

然后调整一下控件的大小吧。

现在应该是这个样子的。

好了,界面的设计就到这吧,毕竟我不是来讲界面设计的。

然后是数据库,我们需要一个测试用的数据库,我随便设计了一个,用的是SqlServer2005,其他的类似,执行SQL语句:

Code:
  1. use master
  2. create database db_test
  3. use db_test
  4. create table student(
  5. stu_id int identity(1,1) primary key,
  6. stu_num varchar(8) not null default '00000000',
  7. stu_name varchar(20) not null,
  8. stu_class varchar(50) not null
  9. )
  10. insert into student values( '20101611', '测试1', '计算机')
  11. insert into student values( '20101612', '测试2', '数学')
  12. insert into student values( '20101613', '测试3', '计算机')
  13. insert into student values( '20101614', '测试4', '中文')

上面的SQL语句的结果是创建了一个名称为db_test的数据库,并且建立了一个表,名称为student,里面有四列,第一列id为自增。后面添加了四个测试数据。

下面是程序代码部分:

添加一个类,我叫做CDataBaseADO,在DataBaseADO.h文件中的#pragma once后加上一句

Code:
  1. #import "C:/Program Files/common files/system/ado/msado15.dll" no_namespace rename("EOF","adoEOF")

因为ADO是COM组件的一个,这句是引入一个库文件,否则的话,下面的都不能使用。

然后在类里添加几个变量:

Code:
  1. private:
  2. // _ConnectionPtr通常被用来创建一个数据连接或执行一条不返回任何结果的SQL语句,如一个存储过程。
  3. _ConnectionPtr m_pConnection;

数据类型 _ConnectionPtr实际上就是由类模板_com_ptr_t而得到的一个具体的实例类,其定义可以到msado15.tlh、comdef.h 和comip.h这三个文件中找到。在msado15.tlh中有:

Code:
  1. _COM_SMARTPTR_TYPEDEF(_Collection, __uuidof(_Collection));

经宏扩展后就得到了_ConnectionPtr类。_ConnectionPtr类封装了Connection对象的Idispatch接口指针,及一 些必要的操作。我们就是通过这个指针来操纵Connection对象。类似地,后面用到的_CommandPtr和_RecordsetPtr类型也是这 样得到的,它们分别表示命令对象指针和记录集对象的指针。

然后在构造函数和析构函数里写上如下代码:

Code:
  1. CDataBaseADO::CDataBaseADO(void)
  2. {
  3. ::CoInitialize(NULL); //初始化OLE/COM库环境
  4. m_pConnection=NULL;
  5. }
  6. CDataBaseADO::~CDataBaseADO(void)
  7. {
  8. if(m_pConnection)
  9. m_pConnection->Close();
  10. m_pConnection=NULL;
  11. ::CoUninitialize();     //释放程序占用的COM 资源
  12. }

然后我们来写打开数据库连接和关闭。数据库只有打开了才能使用。

Code:
  1. // 打开数据库连接
  2. bool CDataBaseADO::Open(_bstr_t strConnection)
  3. {
  4. if( FAILED( m_pConnection.CreateInstance(__uuidof(Connection)) ) )   //初始化Connection指针
  5. return false;
  6. try{
  7. m_pConnection->Open(strConnection, "", "", 0);
  8. }catch(_com_error e)
  9. {
  10. AfxMessageBox(e.Description());
  11. return false;
  12. }
  13. return true;
  14. }
  15. // 关闭数据库连接
  16. void CDataBaseADO::Close(void)
  17. {
  18. if(m_pConnection)
  19. m_pConnection->Close();
  20. m_pConnection=NULL;
  21. }

这些都很简单了,我注释里面也写得很清楚了。在_ConnectionPtr使用前都需要初始化,然后打开,用完之后关闭。记住Open()和Close()一定要成对出现,一次打开就要有一次关闭,而且不能多了,也不能少了。

下面就是重头戏了,select操作的实现。这个就是执行了一条select语句后,返回一个记录集,然后我们把记录集处理一下,放到一个容器里,而不是返回记录集指针,这样以后我们在用这个类的时候,就可以不用在每个里面都去写那句#import了,类相对更独立一些。

看看代码先:

Code:
  1. // 查询
  2. //strSql:查询语句
  3. //strName:要返回的记录集内的列名
  4. vector<vector<_variant_t>> CDataBaseADO::Select(BSTR strSql, vector<_variant_t> strName)
  5. {
  6. _RecordsetPtr pRecordset;     //定义数据集对象
  7. vector<vector<_variant_t>> vRecord;    //这是C++0x新标准,不是VS2010或者不支持新标准的,
  8. //要写成vector< vector<_variant_t> >,因为>>会被认为是右移操作符
  9. if( FAILED( pRecordset.CreateInstance(__uuidof(Recordset)) ) )             //初始化Recordset指针
  10. return vector<vector<_variant_t>>();
  11. try{
  12. pRecordset->Open(strSql, (IDispatch*)m_pConnection, adOpenDynamic, adLockOptimistic, adCmdText);//adOpenDynamic:动态 adLockOptimistic乐观封锁法 adCmdText:文本查询语句
  13. pRecordset->MoveFirst();
  14. while(!pRecordset->adoEOF)//遍历所有记录
  15. {
  16. //取记录字段值
  17. vector<_variant_t> vTheValue; //VARIANT数据类型的泛型
  18. for(int i=0; i<strName.size(); ++i)
  19. {
  20. vTheValue.push_back( pRecordset->GetCollect( strName.at(i) ) );//得到字段的值,并添加到容器的最后
  21. }
  22. vRecord.push_back(vTheValue);
  23. pRecordset->MoveNext();       //移动到下一条记录
  24. }
  25. pRecordset->Close();       //关闭连接
  26. pRecordset = NULL;
  27. }catch( _com_error e)
  28. {
  29. AfxMessageBox(e.Description());
  30. }
  31. return vRecord;
  32. }

到这里有好多东西要说明的。一个一个来说。

_variant_t   记录有数据的类型和数据的值,它封闭了VARIANT数据类型,VARIANT是一个结构体类型,具体的定义可以上网搜一下,也可以看MSDN,我就不介绍了。简单说,它可以是任何类型,在它里面的vt属性表示了它的类型。具体怎么使用,在后面会说到,现在还没有涉及到转化问题。

Vector是一个容器,一个能够存放任意类型的动态数组,能够增加和压缩数据。你可以随时增加或减少里面的数据。因为我们从数据库中获取的数据数量是未知的(在你运行完程序之前)所以要用动态数组来存储。它的定义方法为vector<类型> 变量名,而我们的函数的返回类型呢,是容器的容器,相当于二维数组吧,因为记录集就是那样的,有行有列。里面的是每列的内容,外面的容器存储的是行的内容,每行里面都有需要的列数。

还有一点,关于C++0x的标准问题。因为VS2010是支持C++0x的,所以我直接写成了vector<vector<_variant_t>>这样的,>>不会被认为成右移运算符,但是不支持新标准的编译器里这样写就是错误的,因为>>会被认为成右移运算符,所以要在中间加空格,成为vector< vector<_variant_t> >这样的。关于C++0x也不是我们的重点,大家注意一下自己的编译器,看看支持新标准不。

我们将_RecordsetPtr类型的数据集对象定义为局部变量,然后初始化,然后打开。Open()函数的原型如下:

HRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t & ActiveConnection,enum CursorTypeEnum CursorType, enum LockTypeEnum LockType, long Options )

参数说明:

①Source是数据查询字符串

②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象)

③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:

enum CursorTypeEnum

{

adOpenUnspecified = -1,///不作特别指定

adOpenForwardOnly = 0,///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用

adOpenKeyset = 1,///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。

adOpenDynamic = 2,///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。

adOpenStatic = 3///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你的记录集来说是不可见的。

};

④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:

enum LockTypeEnum

{

adLockUnspecified = -1,///未指定

adLockReadOnly = 1,///只读记录集

adLockPessimistic = 2,悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制

adLockOptimistic = 3,乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做数据的更新、插入、删除等动作

adLockBatchOptimistic = 4,乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。

};

⑤option可以取以下值

adCmdText:表明CommandText是文本命令

adCmdTable:表明CommandText是一个表名

adCmdProc:表明CommandText是一个存储过程

adCmdUnknown:未知

在我们执行了查询操作后,要先将记录集移动到第一个,然后遍历,把所有的结果都放到容器中。遍历结束后,关闭数据集,并且返回。

好吧,到现在为止,数据集已经获取到了,数据也能成功返回了,但是在程序里是显示不出来的,因为我们还没做显示部分。不过这些不是我们的主要内容,附带着讲一下吧。

在资源视图里,打开对话框,然后在ListControl上点右键,添加变量,如图:

在新对话框里,输入名称,我的叫m_ListCtrl,如图:

当然VC6的要在Class Wizard里设置了。注意的是控件ID是否正确。一般的说,不用设置了,类别选成Control。

然后我们就要将这个ListControl设置成四列,并且设置一些小格式。见代码:

Code:
  1. CRect rc;
  2. m_ListCtrl.GetWindowRect(&rc);  //获取控件大小
  3. //设置了四列,大小是一样的
  4. m_ListCtrl.InsertColumn(0, _T("序号"), LVCFMT_CENTER, rc.Size().cx/4, 0);
  5. m_ListCtrl.InsertColumn(1, _T("学号"), LVCFMT_CENTER, rc.Size().cx/4, 1);
  6. m_ListCtrl.InsertColumn(2, _T("姓名"), LVCFMT_CENTER, rc.Size().cx/4, 2);
  7. m_ListCtrl.InsertColumn(3, _T("班级"), LVCFMT_CENTER, rc.Size().cx/4, 3);
  8. //LVS_EX_GRIDLINES是希望显示网格;LVS_EX_FULLROWSELECT是希望被选中时整行反色显示;LVS_EX_HEADERDRAGDROP是让其支持点击表头排序;LVS_EX_TWOCLICKACTIVATE是希望有鼠标在未被选中的行上移动的时候有一些效果
  9. m_ListCtrl.SetExtendedStyle(m_ListCtrl.GetExtendedStyle() | LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP /*| LVS_EX_TWOCLICKACTIVATE*/);

把这些代码放在BOOL CMFCDataBaseDlg::OnInitDialog()函数的return前,运行就有效果了。

然后,双击一下对话框里的确定按钮,我就不再添加新按钮了。进入到函数里,把里面的OnOK()注释了,添加如下代码:

Code:
  1. void CMFCDataBaseDlg::OnBnClickedOk()
  2. {
  3. // TODO: 在此添加控件通知处理程序代码
  4. //CDialogEx::OnOK();
  5. if(!m_DataBase.Open("Provider=SQLOLEDB.1;Password=aaa;Persist Security Info=True;User ID=sa;Initial Catalog=db_test;Data Source=YJN-PC//SQLEXPRESS2005"))
  6. return;
  7. vector<_variant_t> vName;    //设置要返回的列名
  8. vName.push_back("stu_id");
  9. vName.push_back("stu_num");
  10. vName.push_back("stu_name");
  11. vName.push_back("stu_class");
  12. //查询结果
  13. vector<vector<_variant_t>> vResult(m_DataBase.Select(::SysAllocString(L"select * from student"),  vName));
  14. m_ListCtrl.DeleteAllItems();   //删除所有的项目
  15. //通过循环添加所有的内容
  16. for(int i=0; i<vResult.size(); ++i)
  17. {
  18. m_ListCtrl.InsertItem(i, VariantToCString( vResult.at(i).at(0) ) );           //插入一行,每行的第一列是序号
  19. m_ListCtrl.SetItemText(i, 1, VariantToCString(vResult.at(i).at(1)) );      //设置该行的后面列的内容
  20. m_ListCtrl.SetItemText(i, 2, VariantToCString(vResult.at(i).at(2)) );
  21. m_ListCtrl.SetItemText(i, 3, VariantToCString(vResult.at(i).at(3)) );
  22. }
  23. m_DataBase.Close();      //记得要关闭连接
  24. }

看注释应该就明白了吧,嗯,连接字符串的查找方式,有一个简单的方法,随便新建一个文本文档,就是txt的,然后改扩展名,改成udl的,双击打开。在提供程序里选择合适的程序,我选的是“Microsoft OLE DB Provider for SQL Server”,因为我连接的是SQL2005,然后点下一步,根据提示选择,把允许保存密码钩上,选好数据库,测试一下,如果成功了,就OK了。最后点确定。然后把这个文件改回.txt的,用记事本打开,里面就有连接字符串。看看我的是这样的

Code:
  1. [oledb]
  2. ; Everything after this line is an OLE DB initstring
  3. Provider=SQLOLEDB.1;Password=aaa;Persist Security Info=True;User ID=sa;Initial Catalog=db_test;Data Source=YJN-PC/SQLEXPRESS2005

第三行就是连接字符串。这里还有一个地方要注意,就是/的问题,在C++里//才表示一个/,转义字符嘛。一定要注意啊。

还有一个转换函数:

Code:
  1. // 转换字符串
  2. CString CMFCDataBaseDlg::VariantToCString(_variant_t var)
  3. {
  4. CString str; //转换以后的字符串
  5. switch(var.vt)
  6. {
  7. case VT_BSTR:         //var is BSTR type
  8. str=var.bstrVal;
  9. break;
  10. case VT_I2:           //var is short int type
  11. str.Format(L"%d",(int)var.iVal);
  12. break;
  13. case VT_I4:          //var is long int type
  14. str.Format(L"%d",var.lVal);
  15. break;
  16. case VT_R4:         //var is float type
  17. str.Format(L"%10.6f",(double)var.fltVal);
  18. break;
  19. case VT_R8:         //var is double type
  20. str.Format(L"%10.6f",var.dblVal);
  21. break;
  22. case VT_CY:        //var is CY type
  23. str=COleCurrency(var).Format();
  24. break;
  25. case VT_DATE:     //var is DATE type
  26. str=COleDateTime(var).Format();
  27. break;
  28. case VT_BOOL:     //var is  VARIANT_BOOL
  29. str= (var.boolVal==0) ?L"FALSE": L"TRUE";
  30. break;
  31. default:
  32. str.Format(L"Unk type %d/n",var.vt);
  33. TRACE(L"Unknown type %d/n",var.vt);
  34. }
  35. return str;
  36. }

这个函数实现了将_variant_t类型的转换为CString类型。现在来说说吧,看看函数内容大约也明白了吧,根据_variant_t里的vt属性可以知道里面的内容是什么类型的,然后对应的显示就OK了。

再来看看添加,修改,删除的实现。

这个非常简单,都在一个函数里就可以实现了。看看代码吧:

Code:
  1. // 执行SQL语句,并返回影响的行数
  2. //IsText:是否是文本命令,是:文本命令,否:存储过程
  3. int CDataBaseADO::ExcuteSQL(_bstr_t CommandText, bool IsText)
  4. {
  5. _variant_t RecordsAffected;   //记录影响的行数
  6. try{
  7. if(IsText)
  8. m_pConnection->Execute(CommandText, &RecordsAffected, adCmdText);
  9. else
  10. m_pConnection->Execute(CommandText, &RecordsAffected, adCmdStoredProc);
  11. }catch(_com_error e)
  12. {
  13. return -1;
  14. }
  15. return RecordsAffected.intVal;
  16. }

这是运用了_ConnectionPtr里的Execute方法,具体如下:

_RecordsetPtr Connection15::Execute ( _bstr_t CommandText, VARIANT * RecordsAffected, long Options )

  其中CommandText是命令字串,通常是SQL命令。

  

  参数RecordsAffected是操作完成后所影响的行数,

  

  参数Options表示CommandText中内容的类型,Options可以取如下值之一:

  adCmdText:表明CommandText是文本命令

  adCmdTable:表明CommandText是一个表名

  adCmdProc:表明CommandText是一个存储过程

  adCmdUnknown:未知

我这个函数只能执行SQL语句和存储过程,不过一般也就够用了。具体想要怎么改数据,自己写SQL语句吧,去拼接字符串,然后执行,当然,如果出错了,就返回-1,执行失败了就是0喽。到这就结束了。以后有机会封装个更好的类,这个类可以拿出来用,功能虽然简单,也够用了。测试一下?应该的。

给对话框加个按钮先,添加单击事件,在里面写上如下代码:

Code:
  1. void CMFCDataBaseDlg::OnBnClickedButton1()
  2. {
  3. // TODO: 在此添加控件通知处理程序代码
  4. if(!m_DataBase.Open("Provider=SQLOLEDB.1;Password=aaa;Persist Security Info=True;User ID=sa;Initial Catalog=db_test;Data Source=YJN-PC//SQLEXPRESS2005"))
  5. return;
  6. int r = m_DataBase.ExcuteSQL("insert into student values( '20101616', '测试6', '中文')");
  7. CString str;
  8. str.Format(L"%d", r);
  9. MessageBox( str );
  10. m_DataBase.Close();      //记得要关闭连接
  11. }

运行一下,弹出个1吧,那就对了。当然了像Open()这样的,你可以写到别的地方,不用每次都打开,关闭。这些自己发挥吧,我只是抛砖引玉罢了。

代码下载:http://download.csdn.net/source/2978261

[原创]详细的C++连接数据库相关推荐

  1. mac系统M1pro芯片安装VMware Fusion虚拟机win11操作系统(原创详细版)

    VMware22年11月份推出Fusion 13,这是Fusion虚拟软件的最新更新.它允许Mac用户操作虚拟机来运行非macOS操作系统,如Windows 11. 有了Fusion 13,英特尔和苹 ...

  2. [原创] 详细的教你PS手绘美女

    [转自]http://68ps.5d6d.com/thread-18621-1-2.html 本帖最后由 孤独之 于 2010-1-20 19:02 编辑 由于工作的原因好久没来给大家发作品了啊!现在 ...

  3. 如何建立一个网站,可用互联网访问?(原创详细教程)

    总体需要准备的东西: web服务器/虚拟主机. 域名. 网页源码. FTP上传下载工具. 数据库管理软件.(若搭建静态网站则不需要) 注:在此过程中,重点需要将域名解析到服务器,服务器与域名绑定. 详 ...

  4. centos7 下 的lamp 的安装原创详细教程

    时间 : 2017-08-03           目标: 基于CENTOS7 安装 LNMP,liunx的安装不做讲解,主要是 NGINX PHP7 MYSQL 的编译安装 第一节    nginx ...

  5. JDBC如何连接mysql数据库附详细步骤

    JDBC连接数据库在学习中是很重要的一个环节,今天给大家详细说明JDBC连接数据库需要的步骤 1.加载驱动 驱动包的下载地址 https://dev.mysql.com/downloads/conne ...

  6. 渗透测试入门24之渗透测试参考书、课程、工具、认证

    白帽子渗透测试入门资源:参考书.课程.工具.认证文章目录 前言 名词解析 Pwk课程与OSCP证书 CTF 工具 参考书 相关文献推荐 资源打包前言 初入渗透测试领域,过程中遇到不少错综复杂的知识,也 ...

  7. pycharm创建django项目及开发初准备

    文章目录 前言 一.使用pycharm创建django项目 二.初步设置 对文件进行简单说明 设置 app的介绍和创建及设置 app的创建 第一种方式 第二种方式(本质上还是命令,但是更加智能) ap ...

  8. 命令行 run .exe 串口脚定义

    5.系统美化核心文件列表. 这个根据自己的需要替换..  下面是我总结的文件列表. EXE文件列表 NetSetup.exe-网络安装向导 Regedit.exe-注册表 Explorer.exe-资 ...

  9. 数据库管理员的工作任务

    Ⅰ数据库管理员的工作任务 一般情况下,作为一个DBA,应该做好下面12项任务 安装和配置 容量规划 应用架构设计 管理数据库对象 存储空间管理 安全管理 备份和恢复 性能监视和调优 作业调度 网络管理 ...

最新文章

  1. 零起点学算法17——比较2个数大小
  2. UVa 167(八皇后)、POJ2258 The Settlers of Catan——记两个简单回溯搜索
  3. revit如何根据坐标进行画线_护肤干货丨抗衰老的护肤品有哪些?如何根据成分进行挑选?(1)...
  4. 协作的力量——2021-2022年度《大数据系统基础》成果展示
  5. 解决xtraFinder在EI下不能使用问题
  6. 【MFC】定义XP风格的工具栏
  7. Android 关于ListView中按钮监听的优化问题(方法二)
  8. MySQL MGR与Galera性能测试
  9. 装饰工程预结算教程电子书_东北分公司举行工程预结算实战技能培训
  10. The MySQL server is running with the --skip-grant-tables option so it cannot execute this statement
  11. MaxCompute实战之数据存储
  12. matlab 矩阵逻辑与,MATLAB自学笔记(七):数组运算与矩阵操作
  13. 软件测试用例朋友圈发表功能,微信发朋友圈测试用例
  14. 2.4G信道跳频-LFSR-C代码实现
  15. 正确插入目录并且自由更新
  16. 低代码掀起“数字革命”,引领制造业数字化转型
  17. python绘制直方图
  18. 2021美业趋势:包装刺激消费、家用美容仪受追捧、内外兼修的美容时代
  19. excel离散度图表怎么算_excel离散数据表格-Excel 离散程度分析图表如何做
  20. python实现购物车总结,Python实现的购物车功能示例

热门文章

  1. 魔兽世界私服trinitycore2的架构(5)世界对象
  2. 【安卓 R 源码】 bindService 源码分析
  3. (转)jquery.validate插件的使用
  4. CMS用通用图片轮换flash幻灯片播放器:Bcastr3和Bcastr4
  5. word 给设定样式添加快捷键
  6. 电脑软件 个人觉得值得收藏
  7. android html图片点击事件,TextView加载html图片并点击放大
  8. Ubuntu18.04 安装opencv 3.2.0 ,opencv3.4.15
  9. linux下搭建HTTP代理服务器
  10. 【数据分析实战】基于python对Airbnb房源进行数据分析