使用ADO封装类的数据库程序开发实例(下)
作者:成真

《使用ADO封装类的数据库程序开发实例(上)》 中详细介绍了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);
}

//读取记录集-----------------------------------------------
int n = 1;
while (!rset.IsEOF())
{
m_wndGrid.SetRow(n);
n++;
for (int i = 0; i < ncol; i++)
{
m_wndGrid.SetCol(i);
rset.GetValueString(value, (long)(i));
m_wndGrid.SetText(LPCTSTR(value));
}
rset.MoveNext();
}
m_wndGrid.ShowWindow(SW_SHOW);
}
catch (_com_error)
{
return;
}
}
记录集用完后,可以及时用Close()将它关闭,以释放相应的资源. CAdoRecordSet类折构函数,会自动关闭记录.

7.4 其它功能(获取数据库信息)


我们还可以加一点另外的功能:在右边的树控件中显示数据库所拥有的表单和它们各自所拥有所有字段.这段主要是用来演示如何获得数据库的一些信息.
主要实现函数如下:
void CLeftBar::InitTree()
{
CAdoRecordSet rset;
_bstr_t Value;
CString strTablename = "";
HTREEITEM item = TVI_ROOT;
try
{
if (GetDocument()->m_adoConnection.GetConnection()->State != adStateOpen) return;
m_ctrlTree.DeleteAllItems();
//取得数据库字段信息---------------------------------------------
rset = GetDocument()->m_adoConnection.OpenSchema (adSchemaColumns);
while (!rset.IsEOF())
{
CString strValue;
//取得表名-----------------------------------------------
rset.GetValueString(strValue, "TABLE_NAME");
if (strValue != strTablename)
{
strTablename = strValue;
item = m_ctrlTree.InsertItem((LPCTSTR)strTablename, 1, 1);
}
//取得字段名--------------------------------------------
Value = rset.GetFields()->Item[L"COLUMN_NAME"]->Value;
m_ctrlTree.InsertItem((LPCTSTR)Value, 2, 2, item);

rset.MoveNext();
}
}
catch(_com_error e)
{

}
}
当然,你一样可以查询看数据库还拥有哪些视图,索引等信息. 这里还用到了CCoolControlBar类,但如何使用这里就不细说了,毕竟现在主要谈的是ADO,如果你对它有什么兴趣可以真接找我.:)

一个小查询器就差不多完成了。
八、其他参考信息(有很多我在源代码里面已有详细注释,就不一一重复了)
_Connection、_Recordset、_Field参考

(1)_Connection [方法] [属性] [事件]
(2)_Recordset [方法] [属性] [杂项] [事件]
(3)_Field [方法] [属性]

(1)._Connection (ADO for VC++ 语法)

方法
BeginTrans(long *TransactionLevel)
CommitTrans(void)
RollbackTrans(void)

Cancel(void)

Close(void)

Execute(BSTR CommandText, VARIANT *RecordsAffected, long Options,
_ADORecordset **ppiRset)

Open(BSTR ConnectionString, BSTR UserID, BSTR Password, long Options)

OpenSchema(SchemaEnum Schema, VARIANT Restrictions, VARIANT SchemaID,
_ADORecordset **pprset)

属性
get_Attributes(long *plAttr)
put_Attributes(long lAttr)

get_CommandTimeout(LONG *plTimeout)
put_CommandTimeout(LONG lTimeout)

get_ConnectionString(BSTR *pbstr)
put_ConnectionString(BSTR bstr)

get_ConnectionTimeout(LONG *plTimeout)
put_ConnectionTimeout(LONG lTimeout)

get_CursorLocation(CursorLocationEnum *plCursorLoc)
put_CursorLocation(CursorLocationEnum lCursorLoc)

get_DefaultDatabase(BSTR *pbstr)
put_DefaultDatabase(BSTR bstr)

get_IsolationLevel(IsolationLevelEnum *Level)
put_IsolationLevel(IsolationLevelEnum Level)

get_Mode(ConnectModeEnum *plMode)
put_Mode(ConnectModeEnum lMode)

get_Provider(BSTR *pbstr)
put_Provider(BSTR Provider)

get_State(LONG *plObjState)

get_Version(BSTR *pbstr)

get_Errors(ADOErrors **ppvObject)

事件
BeginTransComplete(LONG TransactionLevel, ADOError *pError,
EventStatusEnum *adStatus, _ADOConnection *pConnection)

CommitTransComplete(ADOError *pError, EventStatusEnum *adStatus,
_ADOConnection *pConnection)

ConnectComplete(ADOError *pError, EventStatusEnum *adStatus,
_ADOConnection *pConnection)

Disconnect(EventStatusEnum *adStatus, _ADOConnection *pConnection)

ExecuteComplete(LONG RecordsAffected, ADOError *pError,
EventStatusEnum *adStatus, _ADOCommand *pCommand,
_ADORecordset *pRecordset, _ADOConnection *pConnection)

InfoMessage(ADOError *pError, EventStatusEnum *adStatus,
_ADOConnection *pConnection)

RollbackTransComplete(ADOError *pError, EventStatusEnum *adStatus,
_ADOConnection *pConnection)

WillConnect(BSTR *ConnectionString, BSTR *UserID, BSTR *Password,
long *Options, EventStatusEnum *adStatus,
_ADOConnection *pConnection)

WillExecute(BSTR *Source, CursorTypeEnum *CursorType,
LockTypeEnum *LockType, long *Options,
EventStatusEnum *adStatus, _ADOCommand *pCommand,
_ADORecordset *pRecordset, _ADOConnection *pConnection)

(2)._Recordset (ADO for VC++ 语法)

方法
AddNew(VARIANT FieldList, VARIANT Values)

Cancel(void)

CancelBatch(AffectEnum AffectRecords)

CancelUpdate(void)

Clone(LockTypeEnum LockType, _ADORecordset **ppvObject)

Close(void)

CompareBookmarks(VARIANT Bookmark1, VARIANT Bookmark2,
CompareEnum *pCompare)

Delete(AffectEnum AffectRecords)

Find(BSTR Criteria, LONG SkipRecords, SearchDirectionEnum SearchDirection,
VARIANT Start)

GetRows(long Rows, VARIANT Start, VARIANT Fields, VARIANT *pvar)

GetString(StringFormatEnum StringFormat, long NumRows, BSTR ColumnDelimeter,
BSTR RowDelimeter, BSTR NullExpr, BSTR *pRetString)

Move(long NumRecords, VARIANT Start)

MoveFirst(void)

MoveLast(void)

MoveNext(void)

MovePrevious(void)

NextRecordset(VARIANT *RecordsAffected, _ADORecordset **ppiRs)

Open(VARIANT Source, VARIANT ActiveConnection, CursorTypeEnum CursorType,
LockTypeEnum LockType, LONG Options)

Requery(LONG Options)

Resync(AffectEnum AffectRecords, ResyncEnum ResyncValues)

Save(BSTR FileName, PersistFormatEnum PersistFormat)

Supports(CursorOptionEnum CursorOptions, VARIANT_BOOL *pb)

Update(VARIANT Fields, VARIANT Values)

UpdateBatch(AffectEnum AffectRecords)

属性
get_AbsolutePage(PositionEnum *pl)
put_AbsolutePage(PositionEnum Page)

get_AbsolutePosition(PositionEnum *pl)
put_AbsolutePosition(PositionEnum Position)

get_ActiveCommand(IDispatch **ppCmd)

get_ActiveConnection(VARIANT *pvar)
put_ActiveConnection(VARIANT vConn)
putref_ActiveConnection(IDispatch *pconn)

get_BOF(VARIANT_BOOL *pb)

get_Bookmark(VARIANT *pvBookmark)
put_Bookmark(VARIANT vBookmark)

get_CacheSize(long *pl)
put_CacheSize(long CacheSize)

get_CursorLocation(CursorLocationEnum *plCursorLoc)
put_CursorLocation(CursorLocationEnum lCursorLoc)

get_CursorType(CursorTypeEnum *plCursorType)
put_CursorType(CursorTypeEnum lCursorType)

get_DataMember(BSTR *pbstrDataMember)
put_DataMember(BSTR bstrDataMember)

get_DataSource(IUnknown **ppunkDataSource)
putref_DataSource(IUnknown *punkDataSource)

get_EditMode(EditModeEnum *pl)

get_EOF(VARIANT_BOOL *pb)

get_Filter(VARIANT *Criteria)
put_Filter(VARIANT Criteria)

get_LockType(LockTypeEnum *plLockType)
put_LockType(LockTypeEnum lLockType)

get_MarshalOptions(MarshalOptionsEnum *peMarshal)
put_MarshalOptions(MarshalOptionsEnum eMarshal)

get_MaxRecords(long *plMaxRecords)
put_MaxRecords(long lMaxRecords)

get_PageCount(long *pl)

get_PageSize(long *pl)
put_PageSize(long PageSize)

get_RecordCount(long *pl)

get_Sort(BSTR *Criteria)
put_Sort(BSTR Criteria)

get_Source(VARIANT *pvSource)
put_Source(BSTR bstrConn)
putref_Source(IDispatch *pcmd)

get_State(LONG *plObjState)

get_Status(long *pl)

get_StayInSync(VARIANT_BOOL *pbStayInSync)
put_StayInSync(VARIANT_BOOL bStayInSync)

get_Fields(ADOFields **ppvObject)

杂项
get_Collect(VARIANT Index, VARIANT *pvar)
put_Collect(VARIANT Index, VARIANT value)

事件
EndOfRecordset(VARIANT_BOOL *fMoreData, EventStatusEnum *adStatus,
_ADORecordset *pRecordset)

FetchComplete(ADOError *pError, EventStatusEnum *adStatus,
_ADORecordset *pRecordset)

FetchProgress(long Progress, long MaxProgress, EventStatusEnum *adStatus,
_ADORecordset *pRecordset)

FieldChangeComplete(LONG cFields, VARIANT Fields, ADOError *pError,
EventStatusEnum *adStatus, _ADORecordset *pRecordset)

MoveComplete(EventReasonEnum adReason, ADOError *pError,
EventStatusEnum *adStatus, _ADORecordset *pRecordset)

RecordChangeComplete(EventReasonEnum adReason, LONG cRecords,
ADOError *pError, EventStatusEnum *adStatus,
_ADORecordset *pRecordset)

RecordsetChangeComplete(EventReasonEnum adReason, ADOError *pError,
EventStatusEnum *adStatus, _ADORecordset *pRecordset)

WillChangeField(LONG cFields, VARIANT Fields, EventStatusEnum *adStatus,
_ADORecordset *pRecordset)

WillChangeRecord(EventReasonEnum adReason, LONG cRecords,
EventStatusEnum *adStatus, _ADORecordset *pRecordset)

WillChangeRecordset(EventReasonEnum adReason, EventStatusEnum *adStatus,
_ADORecordset *pRecordset)

WillMove(EventReasonEnum adReason, EventStatusEnum *adStatus,
_ADORecordset *pRecordset

(3)_Field (ADO for VC++ 语法)


方法
AppendChunk(VARIANT Data)

GetChunk(long Length, VARIANT *pvar)

属性
get_ActualSize(long *pl)

get_Attributes(long *pl)
put_Attributes(long lAttributes)

get_DataFormat(IUnknown **ppiDF)
put_DataFormat(IUnknown *piDF)

get_DefinedSize(long *pl)
put_DefinedSize(long lSize)

get_Name(BSTR *pbstr)

get_NumericScale(BYTE *pbNumericScale)
put_NumericScale(BYTE bScale)

get_OriginalValue(VARIANT *pvar)

get_Precision(BYTE *pbPrecision)
put_Precision(BYTE bPrecision)

get_Type(DataTypeEnum *pDataType)
put_Type(DataTypeEnum DataType)

get_UnderlyingValue(VARIANT *pvar)

get_Value(VARIANT *pvar)
put_Value(VARIANT Val)


(完)