MFC中使用ADO进行数据库操作
参考FROM:http://hi.baidu.com/sunkanghome/item/e1fda510b3186359f1090ee2
数据库与数据库编程:
- 当前各种主流数据库有很多,包括Oracle, MS SQL Server, Sybase, Informix, MySQL, DB2, Interbase / Firebird, PostgreSQL, SQLite, SAP/DB, TimesTen, MS ACCESS等等。
- 数据库编程是对数据库的创建、读写等一列的操作。数据库编程分为数据库客户端编程与数据库服务器端编程。数据库客户端编程主要使用ODBC API、ADO、ADO.NET、OCI、OTL等方法;数据库服务端编程主要使用OLE DB等方法。
- 数据库编程需要掌握一些访问数据库技术方法,还需要注意怎么设计高效的数据库、数据库管理与运行的优化、数据库语句的优化。
ADO编程的一般步骤:
- 创建一个Connection对象
- 打开数据源,建立同数据源的连接
- 执行一个SQL命令
- 使用结果集
- 终止连接
ADO最重要的三个对象:
- 连接对象(Connection)
- 命令对象(Command)
- 记录集对象(RecordSet)
在使用这三个对象的时候,需要定义与之相对应的智能指针:_ConnectionPtr、_CommandPtr、_RecordsetPtr
使用智能指针要:定义指针变量、创建其实例(实例化)、调用方法和属性。该智能指针在析构对象时,自动调用Release方法,即使用后不需要手动释放内存,代码更加简洁。
但需要调用Close方法,关闭连接Connection或者记录集RecordSet。
一、ADO编程预处理操作
1.1 导入ADO动态链接库:
在工程的stdafx.h中加入如下语句:
#import "c:\\Program Files\\Common Files\\System\\ADO\\msado15.dll" rename_namespace("ADOCG") rename("EOF","adoEOF") //rename("BOF","adoBOF") no_namespace using namespace ADOCG;
注:import代码要在一行中完成,换行需添加'\'
1.2 初始化OLE/COM库环境:
在基于MFC的应用里,在应用类的InitInstance成员函数中初始化OLE/COM库环境,直接使用AfxOleInit,在退出应用时,该函数自动负责COM资源的释放,比较方便,不用在
ExitInitInstance中添加相关操作:
BOOL CYourApp::InitInstance() { AfxEnableControlContainer(); //初始化OLE DLLs if(!AfxOleInit()) { AfxMessageBox("初始化OLE DLL失败!"); Return FALSE; } ...... }
二、ADO进行数据库连接:
2.1 在App类的头文件中定义变量:
_ConnectionPtr m_pConnection;
2.2 创建智能指针的实例:
在App类的cpp文件InitInstance方法中:
m_pConnection.CreateInstance("ADODB.Connection"); //或者m_pConnection.CreateInstance(__uuidof(Connection));
使用'.'而不是->创建m_Connection实例,然后m_pConnection->open方法创建连接。
2.3 设置连接字符串,以便指定需要的连接
2.3.1 使用JET数据库引擎实现对Acess2000类型的数据库info.mdb的连接
CString strSQL=_T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=info.mdb;User ID=admin;Passward=;");
2.3.2 使用OLE DB提供者实现对SQL Server的标准安全连接串
CString str SQL=_T("Provider=SQLOLEDB;Data Source=local;Initial Catalog=DVDRentDB_Data.MDF;User ID=sa;Password=123456;");
或者是在此处不设置User ID和Password,而直接在Open的第2、3个参数中设置。
CString strConnection="Provider=SQLOLEDB;DataSource=local;Initial Catalog=DVDRentDB_Data.MDF"; m_pConnection->Open((_bstr_t)strSQL,"sa","820415",adModeUnknown);
注意:
- 上面设置连接字符串的时候,如果过长需要分行时,则每一行都要加上双引号,在最后加上分号即可。
- 如果是本地服务器,则Data Source=loca l或 本地服务器名(主机名)
- 若数据库没有设置密码,在连接字符串中可以将其省略,但User ID不能省
- 若数据库和程序文件不在同一文件夹下,直接写数据库名即可,在InitialCatalog中不需加上该数据库的存储器地址
2.3.3 使用OLE DB提供者实现对远程SQL Server的标准安全连接串
strConnect=_T("Provider=sqloledb;Network Library=DBMSSOCN;" "Data Source=130.120.110.001,1433;" "Initial Catalog=MyDateBaseName;" "User ID=MyUserName;Password=MyPassword;");
2.4 实现对数据库的连接
在ADO的操作中建议使用try...catch( )来捕获错误信息,因为它有时会经常出现一些意想不到的错误
try { m_pConnection->Open( (_bstr_t) strSQL," "," ",adModeUnknown); } catch(_com_error e) //捕捉异常 { CString strError; strError.Format( "连接数据库发生异常! \r \n错误信息:%s",e.ErrorMessage( ) ); AfxMessageBox(errormessage); //显示错误信息 }
综上:InitInstance方法中可:
if(!AfxOleInit()) { AfxMessageBox("初始化OLE DLL失败!"); Return FALSE; } m_pConnection.CreateInstance("ADODB.Connection"); //或者m_pConnection.CreateInstance(__uuidof(Connection)); try { m_pConnection->ConnectionTimeout = 3; //连接ACCESS2000 m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=d:/Attendence/AttendenceDB.mdb","","",adModeUnknown); } catch(_com_error e) { AfxMessageBox(e.Description() + _T("\n数据库连接失败")); }
2.5 关闭数据库连接
一般重载App类的ExitInstace( )函数,调用m_pConnection的Close方法关闭连接即可:
m_pConnection->Close( );
m_pConnection=NULL;
注意:由于初始化COM库调用的是AfxOleInit,这种方法初始化COM库的优点就在于资源的释放也是自动进行的,所以不必担心资源泄漏的问题。
三、数据库操作:
_CommandPtr接口
该接口返回一个记录集, 在使用_CommandPtr接口时,可以利用全局_ConnectionPtr接口,也可以在_CommandPtr接口里直接使用连接串。如果只执行一次或者几次数据库访问操作,后者是比较好的选择。但是,如果频繁访问数据库,并要返回很多记录集,那么应该使用全局_ConnectionPtr接口创建一个数据库连接,然后使用_CommandPtr接口执行存储过程和SQL语句。
_RecordsetPtr接口
该接口是一个记录集对象, 与前两种对象相比,它对记录集提供了更多的控制功能,如记录锁定、游标控制等。
3.1 使用Recordset对象操作数据库:
假定已经使用连接指针m_pConnection对象创建了数据源的连接:
3.1.1 创建记录集
_RecordsetPtr m_pRecordset; //声明记录集指针 m_pRecordset.CreateInstance(__uuidof(Recordset)); //创建实例
3.1.2 打开记录集
记录集指针创建完毕后,调用该指针的Open方法打开记录集。
//读取数据库: CString sqlHasRecord; sqlHasRecord.Format("SELECT * FROM WorkUser WHERE MemberID = '%s'" , m_StringNumber); HRESULT hr = m_pRecordset->Open(
sqlHasRecord.GetBuffer(0),
_variant_t((IDispatch*)theApp.m_pConnection, true),
adOpenDynamic,
adLockPessimistic,
adCmdText
);
Open函数声明如下:
HRESULT Recordset15::Open ( const _variant_t & Source, //sql语句、表名、command对象 const _variant_t & ActiveConnection, //已经建立好的连接 enum CursorTypeEnum CursorType, //用于设置在打开Recordset时提供者应使用的游标类型,默认值adOpenForwardOnly enum LockTypeEnum LockType, //用于设置在打开Recordset时提供者应使用的锁定类型,默认值adLockReadOnly long Options ) ; //获取Source(即Open第一个参数)的方式
如果第一个参数是sql语句则选择adCmdText
如果第一个参数是表名则选择adCmdTable
//读取数据库: CString sqlHasRecord; sqlHasRecord.Format("SELECT * FROM WorkUser WHERE MemberID = '%s'" , m_StringNumber); HRESULT hr = m_pRecordset->Open(sqlHasRecord.GetBuffer(0), _variant_t((IDispatch*)theApp.m_pConnection, true), adOpenDynamic, adLockPessimistic, adCmdText); if (SUCCEEDED(hr)) { while (!m_pRecordset->adoEOF || !m_pRecordset->BOF)//遍历返回的每一条记录 { CString m_StringID; m_StringID = (LPCSTR)_bstr_t(m_pRecordset->GetCollect("FeatureID"));//读取id
m_pRecordset->MoveNext();
} } m_pRecordset->Close(); //记录用完之后需要关闭
3.1.3 遍历记录集
一般在返回记录集时,通常要遍历结果记录集,以便查看或编辑某一条记录:
注:为了避免发生异常:
MoveFirst、MovePrev之前,需要使用记录集的指针BOF属性来检测当前的记录集指针 是否位于第一条记录之前;
MoveLast、MoveNext之前需要使用记录集指针的EOF属性来检测当前的记录集指针 是否位于最后一条记录之后.
记录集定位
两种定位方法:前者通过设置或者获取AbsolutePosition属性,其值从1开始,并且当前记录为记录集中第一条记录时等于1, 后者通过设置或获取BookMark属性.
3.1.4 访问记录集
读取字段值:
m_pRecordset->GetCollect (字段名); // 字符串 -> 字段名 or 整型(long) -> 字段对应的序号
设置字段值:
m_pRecordset->PutCollect (字段名,新值);
//两个方法的原型: _variant_t GetCollect ( const _variant_t & Index ) void PutCollect ( const _variant_t & Index , const _variant_t &pvar )
3.1.5 记录集更新(添加、编辑、删除)
添加新的记录:AddNew
编辑当前记录:Edit
删除当前记录:Delete
AddNew方法:直接在表的末尾续加新记录,该方法可以使用参数,在参数中指定要添加的新纪录;
也可以不使用参数,而在后面使用PutCollect方法,并需使用Update函数保存新纪录。
Update方法:用于保存从调用AddNew方法以来所作的任何更改。
//在表的末尾增加新纪录 m_pRecordset->AddNew(); //------------------ m_pRecordset->PutCollect("姓名",_variant_t(m_strName)); m_pRecordset->PutCollect("工作单位",_variant_t(m_strComName)); m_pRecordset->PutCollect("单位地址",_variant_t(m_strComAddr)); //------------------ m_pRecordset->Update();//更新数据库-将新纪录存入数据库
3.1.6 记录集关闭
在对记录集的操作完成后,必须及时关闭记录集。
if ( m_pRecordset != NULL ) { m_pRecordset ->Close( ); m_pRecordset =NULL; }
3.2 使用Connection对象操作数据库
1.编辑SQL语句
2.执行connection对象的Excute()方法
//_ConnectionPt m_pConnection 智能指针对象
strSql.Format(_T("UPDATE WorkUser SET MemberName = '%s', MemberPosition = '%s' WHERE MemberID = '%s' "),m_StringName, m_StringPosition, m_StringNumber); try { m_pConnection->Execute(_bstr_t(strSql), 0, adCmdText); } catch(_com_error e) { MessageBox(e.Description()); return; }
注:_variant_t和_bstr_t这两个类分别封装并管理VARIANT和BSTR这两种数据类型,VARIANT和BSTR这两种类型是COM中使用的数据类型。
四、实例演示
4.1 初始化引入相关的库+Connection对象的创建和数据库的连接
#import "c:\\Program Files\\Common Files\\System\\ADO\\msado15.dll" rename_namespace("ADOCG") rename("EOF","adoEOF") //rename("BOF","adoBOF") using namespace ADOCG;
//... ... _ConnectionPtr m_pConnection;
//... ... if(!AfxOleInit()) { AfxMessageBox("初始化OLE DLL失败!"); Return FALSE; } m_pConnection.CreateInstance("ADODB.Connection"); try { m_pConnection->ConnectionTimeout = 3; //连接ACCESS2000 m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=d:/Attendence/AttendenceDB.mdb","","",adModeUnknown); } catch(_com_error e) { AfxMessageBox(e.Description() + _T("\n数据库连接失败")); }
4.2 Recordset对象的声明与创建实例:
_RecordsetPtr m_pRecordset;
m_pRecordset.CreateInstance(__uuidof(Recordset)); //创建实例
4.3 读取记录内容:
CString sqlHasRecord; sqlHasRecord.Format("SELECT * FROM WorkUser WHERE MemberID = '%s'" , m_StringNumber); HRESULT hr = m_pRecordset->Open(sqlHasRecord.GetBuffer(0), _variant_t((IDispatch*)theApp.m_pConnection, true), adOpenDynamic, adLockPessimistic, adCmdText); if (SUCCEEDED(hr)) { while (!m_pRecordset->adoEOF || !m_pRecordset->BOF)//遍历返回的每一条记录 { CString m_StringID; m_StringID = (LPCSTR)_bstr_t(m_pRecordset->GetCollect("FeatureID"));//读取id m_pRecordset->MoveNext(); } } m_pRecordset->Close(); //记录用完之后需要关闭
4.4 插入新记录:
CString strSql; strSql.Format("INSERT INTO WorkUser(MemberName, MemberID, MemberPosition, FeatureID, BeDeleted, SendedToClient) VALUES('%s', '%s', '%s', %d, 0, 0)",“ZhangSan”,”14S051000”, “Student”, 16); try { (theApp.m_pConnection)->Execute(_bstr_t(strSql), 0, adCmdText); } catch(_com_error e) { MessageBox(e.Description()); return; }
4.5 更新记录:
strSql.Format(_T("UPDATE WorkUser SET MemberName = '%s', MemberPosition = '%s' WHERE MemberID = '%s' "),m_StringName, m_StringPosition, m_StringNumber); try { (theApp.m_pConnection)->Execute(_bstr_t(strSql), 0, adCmdText); } catch(_com_error e) { MessageBox(e.Description()); return; }
4.6 删除记录:
CString strSql; strSql.Format(_T("DELETE FROM WorkUser WHERE FeatureID = %s "), FeatureID[i]); try { (theApp.m_pConnection)->Execute(_bstr_t(strSql), 0, adCmdText); } catch(_com_error e) { MessageBox(e.Description()); return FALSE; }
4.7 读取字节流文件:
读取图像数据:
try { CString sql; sql.Format("select * from WorkUser where MemberID='%s'",MemberID); HRESULT hr = m_pRecordset->Open(sql.GetBuffer(0),_variant_t((IDispatch *)theApp.m_pConnection,true),adOpenDynamic,adLockPessimistic,adCmdText); if(SUCCEEDED(hr)) { if(m_pRecordset->adoEOF||m_pRecordset->BOF) { MessageBox(_T("数据库中没有相应的记录")); return; } else { long lDataSize = m_pRecordset->GetFields()->GetItem(imgColName.GetBuffer(0))->ActualSize; if (lDataSize>0) { _variant_t varBLOB; varBLOB = m_pRecordset->GetFields()->GetItem(imgColName.GetBuffer(0))->GetChunk(lDataSize); if(varBLOB.vt == (VT_ARRAY | VT_UI1)) { if(buffer) ///重新分配必要的存储空间 { char *pBuf = NULL; SafeArrayAccessData(varBLOB.parray,(void **)&pBuf); memcpy(buffer,pBuf,lDataSize); ///复制数据到缓冲区buffer SafeArrayUnaccessData (varBLOB.parray); } } } else { MessageBox(_T("数据库中的图像数据为空!")); return; } } } } catch (...) { MessageBox(_T("数据库访问出错")); } m_pRecordset->Close();
4.8 保存字节流文件:
CString sql; sql.Format("select * from WorkUser where MemberID='%s'",MemberID); HRESULT hr = m_pRecordset->Open(sql.GetBuffer(0),_variant_t((IDispatch *)theApp.m_pConnection,true),adOpenDynamic,adLockPessimistic,adCmdText); if(SUCCEEDED(hr)) { if (!m_pRecordset->adoEOF||!m_pRecordset->BOF) { char *pBuf = buffer; VARIANT varBLOB; SAFEARRAY *psa; SAFEARRAYBOUND rgsabound[1]; if(pBuf) { rgsabound[0].lLbound = 0; rgsabound[0].cElements = bufLength; psa = SafeArrayCreate(VT_UI1, 1, rgsabound); //分配的数据类型为unsigned int (1 byte 长度的类型) for (long i = 0; i < (long)bufLength; i++) SafeArrayPutElement (psa, &i, pBuf++); varBLOB.vt = VT_ARRAY | VT_UI1; varBLOB.parray = psa; m_pRecordset->GetFields()->GetItem(imgColName.GetBuffer(0))->AppendChunk(varBLOB); } m_pRecordset->Update(); } else { MessageBox(_T("数据库中没有相应用户的记录!")); return; } } m_pRecordset->Close()