MFC中使用ADO进行数据库操作

https://www.cnblogs.com/sunniflyer/p/4118956.html

数据库与数据库编程:

当前各种主流数据库有很多,包括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中使用的数据类型。

 

      为了C++中的变量应用到ADO编程中,只能进行数据类型的转换。通过_variant_t和_bstr_t这两个类,就可以方便的把C++类型变量转换成COM中的变量了。

参考:http://baike.baidu.com/view/2798823.htm?fr=aladdin

com编程: http://baike.baidu.com/view/6923408.htm?fr=aladdin

四、实例演示

 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

 

 

 

 

 

 

 

   

 

 

 

Trouble is a Friend

你可能感兴趣的:(VC_MFC)