使用ADO封装类的数据库程序开发实例

  源代码运行效果图如下:
  
 


  用过ADO的人都知道, 调用ADO要处理很多"麻烦"的事情,如异常处理等,要写很多try - catch块. 有点不甚其烦。我干脆把常用的函数都封装起来,免去老是要写try - catch块的麻烦。做起来虽然没有什么技术含量,但也比较烦琐,所以只完成了一部分,且由于时间及个人水平有限,没有对封装的东西作全面测试,并必定有很多错误,但想到对某些朋友可能有用。所以先"捐"出来了。^-^.
  
  在介绍这两个类之前,让我们先来了解一下ADO,本文假设你已有一定的编程能力:
  
   一、了解ADO的结构体系
  
  ADO(ActiveX Data Object, Active 数据对象)是Microsoft提供的一种面向对象,与语言无关的数据访问应用编程接口。据大部分资料介绍,它有如下主要特点:
  
  一:易于使用。
  
  二:可以访问多种数据源。
  
  三:访问速度快,效率高:
  
  四:方便Web应用。
  
  五:技术编程接口丰富。
  
  六:低内存支出和占用磁盘空间较少.
  
  正是看到ADO这么多优点,使我对用ADO开发数据库产生了兴趣.ADO用起来也如前面所说的一样,确实不难。总的来说,ADO模型包括了下列对象:连接(Connection)、命令 (Command)、记录集 (Recordset)、字段 (Field)、参数 (Parameter)、错误 (Error)、属性 (Property)、集合 、事件.它们之间的关系如下图:
  
  


  (2)对于访问一个数据库来说,我们一般先建立一个ADO连接.
  
  (3)ADO连接可以直接执行SQL语句来操纵数据库,但如果我们要对数据在应用程序和数据源之间进行存取的话,就需要用到记录集对象。一个ADO连接可以有多个ADO连接,但一个ADO连接一般只能对应一个且必须对应一个ADO连接.
  
  (4)另外如果你可进行更高级别的访问的话,还可能要用到命令对象。例如要调用存储过程等。
  
  (5)一个记录集包含有一个字段集,一个字段集则包含有多个字段对象。
  
  (6)同样一个命令对象也包含一个参数集,一个参数集则包含有多个参数对象。
  
  (7)连接对象也有一个错误集并包含有多个错误对象。
  
  这就是ADO各对象之间大致的关系.
  
   二、了解ADO连接
  
  在使用数据库之前,要先建立连接.一般先用CreateInstance方法创建ADO连接对象,然后就可以用Open方法连接到数据库。它的原型是 Open(BSTR ConnectionString, BSTR UserID, BSTR Password, long Options);其中UserID和Password如果在ConnectionString已经指明了用户名和密码,一般就可以不必管它们.Options指的是是以同步方式(adConnectUnspecified)还是以异步方式(adAsyncConnect)进行连接,默认为同步.这个函数的关键这处在于ConnectionString参数,它决定了我们将以什么方式连接到什么数据源,例如:
  如果是Access数据库,它的格式则一般为: "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=db.mdb";
  如果是SQL Server: "Provider=SQLOLEDB.1;Data Source=sqlservername;Initial Catalog=master;UserID=sa; PWD=password";
  具体的内容视你的环境而定.要连接到其他数据库,请参考相关的资料.
  例如:
  _ConnectionPtr pConnection;
  LPCSTR strConnect = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb";
  //创建 Connection 对象---------------------------
  HRESULT hr = pConnection.CreateInstance("ADODB.Connection");
  //设置连接时间-----------------------------------
  pConnection->put_ConnectionTimeout(long(5));
  if (SUCCEEDED(hr))
  {
  // 连接数据库---------------------------------------------
  pConnection->Open(strConnect, "", "", adConnectUnspecified))
  }
  
   三、了解ADO记录集
  
  创建了ADO连接,我们就可以通过ADO记录集来访问数据库了.同样,在使用记录集之前要先创建对象.然后调用Open方法打开记录集.它不但可以执行普通的SQL语句,还可以调用存储过程等等:
  Open(VARIANT Source, VARIANT ActiveConnection, CursorTypeEnum CursorType,LockTypeEnum LockType, LONG Options).
  其中ActiveConnection参数为一个有效的 Connection 对象名,就是我们在上面所说过的ADO连接对象.
  
  CursorType参数指的是记录集光标类型,在官方的资料中是这样说明它的取值类型的:
  
  1.adOpenForwardOnly 仅向前游标,默认值。除了只能在记录中向前滚动外,与静态游标相同。当只需要在记录集中单向移动时,使用它可提高性能。
  
  2.adOpenKeyset 键集游标。尽管从您的记录集不能访问其他用户删除的记录,但除无法查看其他用户添加的记录外,键集游标与动态游标相似。仍然可以看见其他用户更改的数据。
  
  3.adOpenDynamic 动态游标。可以看见其他用户所作的添加、更改和删除。允许在记录集中进行所有类型的移动,但不包括提供者不支持的书签操作。
  
  4.adOpenStatic 静态游标。可以用来查找数据或生成报告的记录集合的静态副本。另外,对其他用户所作的添加、更改或删除不可见。
  我们现在可不用管这么多,就用默认的adOpenStatic类型吧.
  
  LockType参数,用于指示在什么时候锁定记录:
  
  AdLockReadOnly (默认值)只读 - 不能改变数据。
  
  AdLockPessimistic 保守式锁定(逐个) - 提供者完成确保成功编辑记录所需的工作,通常通过在编辑时立即锁定数据源的记录。
  
  AdLockOptimistic 开放式锁定(逐个) - 提供者使用开放式锁定,只在调用Update 方法时才锁定记录。
  
  AdLockBatchOptimistic 开放式批更新-用于批更新模式(与立即更新模式相对)。
  
  Options参数指的是操作类型:
  adCmdText 指示strSQL为命令文本, 即普通的SQL语句.
  
  adCmdTable 指示ADO生成SQL查询以便从在strSQL中命名的表中返回所有行.
  
  adCmdTableDirect 指示所作的更改在strSQL中命名的表中返回所有行.
  
  adCmdStoredProc 指示strSQL为存储过程.
  
  adCmdUnknown 指示strSQL参数中的命令类型为未知
  adCmdFile 指示应从在strSQL中命名的文件中恢复保留(保存的)Recordset.
  
  adAsyncExecute 指示应异步执行strSQL.
  
  adAsyncFetch 指示在提取 Initial Fetch Size 属性中指定的初始数量后,应该异步提取所有剩余的行.如果所需的行尚未提取,主要的线程将被堵塞直到行重新可用.
  
  adAsyncFetchNonBlocking 指示主要线程在提取期间从未堵塞. 如果所请求的行尚未提取,当前行自动移到文件末尾.
  
  唉又是一大串,如果你只是要执行SQL语句,就把它设为adCmdText吧. 这样Source就是你要执行的SQL语句了.
  
  例如:
  
  LPCSTR strSQL = "select * from vckbasetable";
  _RecordsetPtr pRecordset;
  pRecordset.CreateInstance("ADODB.Recordset");
  pRecordset->Open(_bstr_t(strSQL),
  _variant_t((IDispatch*)pConnection, true),
  adOpenStatic,
  AdLockOptimistic ,
  adCmdText);
  
  ADO中读取记录集中指定字段的值一般有两种方法:
  
  第一种:FieldsPtr pFields;
  pRecordset->get_Fields(&pFields);
  pFields->Item[L"COLUMN_NAME"]->Value;
  //或pFields->Item[long(index)]->Value;
  //其中index为整型或长整型.GetFields()函数返回的是记录集对象的字段集合对象的指针.
  
  第二种:pRecordset->get_Collect("COLUMN_NAME");
  //或pRecordset->get_Collect(long(index));
  
  它们都将返回一个_variant_t类型的值,推荐使用后一种方法.
  
  例如:int ncol = rset.GetFieldsCount();
  while (!rset.IsEOF())
  {
  for (int i = 0; i < ncol; i++)
  {
  rset.GetValueString(value, (long)(i));
  }
  rset.MoveNext();
  }
  
   四、了解ADO字段.
  
  一个记录集通常包含多个字段,通过访问记录,我们可以得到很多有用的信息,如字段名,字段的数据类型,定义的宽度,实际占有的宽度等等:
  
  一般用记录集的get_Fields方法取得字段集合对象:FieldsPtr pFields;
  Recordset->get_Fields(&pFields);
  
  然后可以获得相应的字段对象:long lIndex = 0;
  FieldPtr pf = pFields->GetItem(_variant_t(lIndex));
  //或: FieldPtr pf = pFields->GetItem("COLUMN_NAME");
  
  字段对象有很多有用的属性,这些可以参考我的源代码或其他相关资料.如:get_ActualSize(long *pl) //实际宽度
  get_Attributes(long *pl) //属性
  get_DefinedSize(long *pl) //定义宽度(以字节为单位,如整型为4,长整型为8...)
  get_Name(BSTR *pbstr) //字段名
  get_Type(DataTypeEnum *pDataType) //数据类型
  get_Value(VARIANT *pvar) // 字段的值
  
  有了以上对ADO的基本了解后, 接下来让我们在此基础上进行具体的编程。
  
  五、开始编写ADO应用程序.
  
  使用ADO之前,我们另外还需要添加下面的语句,如此把ADO的库引入到工程中. #import "c:/program files/common files/system/ado/msado15.dll"
  no_namespace rename("EOF","adoEOF")
  根据机器安装时候的设置不同具体的路径可能不一样。
  另外编译的时候会出现如下的警告信息:
  msado15.tlh(405) : warning C4146: unary minus operator applied to unsigned type, result still unsigned
  MSDN建议我们不要理会。如果你实在不想看到的话可以在stdafx.h中加入一行下面的代码:
  #pragma warning(disable:4146)
  这样这个警告信息就不会再出现了。
  
  ADO使用了COM,所以在使用ADO之前,必须对COM进行了初始化,否则无法使用.你可以使用AfxOleInit()来初始化,但只能初始一次,你不能多次调用此函数,建议你在应用程序的APP类的InitInstance方法中进行初始化.
  上面这些一般相关的资料都有详细说明,因此我就不细说了,下面我们来看看如何我封装的两个类。
  
  六、ADO封装类:CAdoConnection 和 CAdoRecordSet
  
  首先当然是要连接到数据源了,连接到数据源的函数是_CAdoConnection的Connect方法,它封装了ADO连接对象的CreateInstance和Open方法:
  我们来看看我是如何封装的:
  
  BOOL CAdoConnection::Connect(LPCTSTR strConnect, long lOptions)
  {
  m_strConnect = strConnect;
  try
  {
  ///创建 Connection 对象---------------------------
  HRESULT hr = m_pConnection.CreateInstance("ADODB.Connection");
  if (SUCCEEDED(hr))
  {
  // 连接数据库---------------------------------------------
  if (SUCCEEDED(m_pConnection->Open(strConnect, "", "", lOptions)))
  {
  return TRUE;
  }
  }
  }
  catch (_com_error e)
  {
  TRACE(_T(":( 连接数据库发生错误: %s/n"), e.ErrorMessage());
  return FALSE;
  }
  catch (...)
  {
  TRACE(_T(":( 连接数据库时发生未知错误:"));
  }
  return FALSE;
  }
  
  使用之前先定义一个CAdoConnection 类对象如m_adoConnection,例如:CString strSrcName = "E://Access//datebase.mdb";//假设在e盘有这样一个access的数据库文件
  CString strConnect = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + strSrcName;
  m_adoConnection.Connect(LPCSTR(strConnect));
  
  这样就连接到数据源了.
  接着就是要打开记录集了,下面我对它的open方法的封装:使用进你可以忽略后面的三个参数,直接把一个SQL语句传给它.
  
  
  HRESULT CAdoRecordSet::Open(LPCTSTR strSQL,
  long lOption,
  CursorTypeEnum CursorType,
  LockTypeEnum LockType)
  {
  try
  {
  if (m_pConnection == NULL)
  {
  return -1;
  }
  else if (m_pRecordset == NULL)
  {
  m_pRecordset.CreateInstance("ADODB.Recordset");
  }
  
  m_pRecordset->Open(_bstr_t(strSQL),
  _variant_t((IDispatch*)m_pConnection->GetConnection(), true),
  CursorType, LockType, lOption);
  if (m_pRecordset == NULL)
  {
  return -1;
  }
  return (m_pRecordset->adoEOF) ? 0 : 1;
  }
  catch (_com_error e)
  {
  TRACE(_T(":( 打开记录集发生错误: %s/n"), e.ErrorMessage());
  return -1;
  }
  }
  例如我们可以这样来用:CAdoRecordSet rset;
  rset.SetAdoConnection(&(GetDocument()->m_adoConnection));//记得要先指定相应的连接对象,否则会出错.
  m_strSQL = "select * from city";//要执行的SQL语句.
  rset.Open(m_strSQL);
  
  七.写一个数据查询工具(我对实现过程只稍作了介绍,具体内容请参考源代码):
  
  7.1 连接数据库
  
  首先要编写连接到数据库的代码,为此,我写了一个对话框类(CLogoDig)用来选择不同的数据源,
  然后在视图类(这个视图类是从CFromView类派生的)添加 CAdoConnection 类成员变量,并添加如下连接函数 void CAccessView::OnFileConnect()
  {
  CLogoDig dlg;
  if (dlg.DoModal() == IDOK)
  {
  if (dlg.m_nSrcType == 0)
  {
  CString strConnect = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + dlg.m_strSrcName;
  GetDocument()->m_adoConnection.Disconnect();
  if (!GetDocument()->m_adoConnection.Connect(LPCSTR(strConnect)))
  {
  AfxMessageBox("连接数据库失败!");
  return;
  }
  }
  else if (dlg.m_nSrcType == 1)
  {
  CString strConnect = "Provider=SQLOLEDB.1;Data Source=" + dlg.m_strSrcName +
  ";Initial Catalog=" + dlg.m_strDbName +
  ";User ID=" + dlg.m_strUserName + "; PWD=" + dlg.m_strPassWord;
  GetDocument()->m_adoConnection.Disconnect();
  if (!GetDocument()->m_adoConnection.Connect(LPCSTR(strConnect)))
  {
  AfxMessageBox("连接数据库失败!");
  return;
  }
  }
  ((CMainFrame*)GetParentFrame())->m_wndLeftBar.InitTree();
  }
  }
  
  7.2 CCoolControlBar类和CMSFlexGrid类
  
  在这个数据查询工具中我们使用了另外两个类:CCoolControlBar类和CMSFlexGrid类。.前者是我写的一个可以动态改变大小的控制条类,后者是系统自带一个网格控件,这个控件功能较少,但对于只用来显示一下查询结果已经够用了。
  在编辑CFromView类对话框资源视图中,在正在编辑的对话框资源上点右键,选择插入ActiveX控件,然后选择"Microsoft FlexGrid control"控件。然后在类向导中在"Member Variables"页中为它添加变量,类向导会提示要引入向个头文件,确认,这时你的类视图中就会增加好几个类。
  先介绍一个要用到的几个函数:
  SetCols 设置总共拥有的列数
  SetFixedCols 设置固定的列数,即背景为灰色的那种
  SetRows 设置总共拥有的行数
  SetCol 设置当前列
  SetRow 设置当前行
  SetText 设置当前格的文本。位置由上面两个函数决定。
  SetColWidth 设置列宽
  
  7.3 数据的显示与处理
  
  另外我们还需要两个编辑控件,用来输入SQL语句和显示错误信息.并在OnSize消息处理函数中动态设置它们的位置: void CAccessView::OnSize(UINT nType, int cx, int cy)
  {
  CFormView::OnSize(nType, cx, cy);
  
  if (m_wndGrid.GetSafeHwnd() != NULL) //控件是否已经创建
  {
  m_editError.MoveWindow(0, 10, cx, cy - 50);
  m_wndGrid.MoveWindow(0, 0, cx, cy - 40);
  m_editSQL.MoveWindow(0, cy - 40, cx, 40);
  }
  }
  
  添加一条菜单项并添加相应响应函数,用于开始执行输入的SQL语句:void CAccessView::OnRun()
  {
  UpdateData();
  UpdateGrid();
  }
  
  根据查询情况显示查询结果:void CAccessView::UpdateGrid()
  {
  //连接对象是否打开------------------------------------------
  if (!GetDocument()->m_adoConnection.IsOpen())
  {
  AfxMessageBox("数据库没有打开或已经关闭!");
  return;
  }
  //先隐藏这两个控件------------------------------------------
  m_wndGrid.ShowWindow(SW_HIDE);
  m_editError.ShowWindow(SW_HIDE);
  
  CAdoRecordSet rset;
  rset.SetAdoConnection(&(GetDocument()->m_adoConnection));
  if (rset.Open(m_strSQL, adCmdText) != 1)
  {
  //查询出错,取得出错信息并显示在编辑控件里面------------------
  m_strError = GetDocument()->m_adoConnection.GetLastError();
  UpdateData(FALSE);
  m_editError.ShowWindow(SW_SHOW);
  return;
  }
  //下面我用到的有些函数在类中可能没有封装,所以还是使用了try块,以防万一,:(
  try
  {
  //取得记录集的字段数和行数----------------------------------
  int nrow = rset.GetRecordCount();
  int ncol = rset.GetFields()->Count;
  //设置网格控件的列数和行数----------------------------------
  m_wndGrid.SetCols(ncol);
  m_wndGrid.SetRows(nrow + 1); // 多留一行以显示字段名
  m_wndGrid.SetFixedCols(0);
  
  CString value;
  //填充字段名-----------------------------------------------
  m_wndGrid.SetRow(0);
  for (int i = 0; i < ncol; i++)
  {
  m_wndGrid.SetCol(i);
  m_wndGrid.SetText(LPCSTR(rset.GetFieldName(i)));
  //设置当前列的大致宽度-------------------------------
  int nwidth = rset.GetFieldDefineSize(i) * 200;
  nwidth = nwidth > 2000 ? 2000 : nwidth;
  m_wndGrid.SetColWidth(i, nwidth);
  }
  
  //
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值