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编程的一般步骤:

  1.   创建一个Connection对象  
  2.   打开数据源,建立同数据源的连接  
  3.   执行一个SQL命令 
  4.   使用结果集
  5.   终止连接

 

ADO最重要的三个对象:

  1. 连接对象(Connection)
  2. 命令对象(Command)
  3. 记录集对象(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中使用的数据类型。

      为了C++中的变量应用到ADO编程中,只能进行数据类型的转换。通过_variant_t和_bstr_t这两个类,就可以方便的把C++类型变量转换成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();

 

 

 

 SafeArrayDestroy

 

 

 

  

 

posted @ 2014-11-26 20:18  sunnycs  阅读(14203)  评论(0编辑  收藏  举报