OLEDB存取BLOB型數(shù)據(jù)


title: OLEDB存取BLOB型數(shù)據(jù)
tags: [OLEDB, 數(shù)據(jù)庫(kù)編程, VC++, 數(shù)據(jù)庫(kù), BLOB型數(shù)據(jù)]
date: 2018-04-20 20:49:37
categories: windows 數(shù)據(jù)庫(kù)編程
keywords: OLEDB, 數(shù)據(jù)庫(kù)編程, VC++, 數(shù)據(jù)庫(kù), 打開(kāi)數(shù)據(jù)源對(duì)象


現(xiàn)代數(shù)據(jù)庫(kù)系統(tǒng)除了支持一些標(biāo)準(zhǔn)的通用數(shù)據(jù)類型以外,大多數(shù)還支持一種稱之為BLOB型的數(shù)據(jù)。
BLOB全稱為big large object bytes, 大二進(jìn)制對(duì)象類型,這種類型的數(shù)據(jù)通常用于存儲(chǔ)文檔、圖片、音頻等文件,這些文件一般體積較大,保存這些文件可以很方便的管理和檢索這類信息。在MS SQLSERVER中常見(jiàn)的BLOB數(shù)據(jù)類型有text、ntext(n表示unicode)、image、nvarchar、varchar、varbinary等。其中image基本可以用來(lái)保存一切二進(jìn)制文件,比如word、Excel、音頻、視頻等等類型。
針對(duì)BLOB型數(shù)據(jù),OLEDB也提供了對(duì)它的支持

使用BLOB型數(shù)據(jù)的利弊

一般數(shù)據(jù)庫(kù)對(duì)BLOB型數(shù)據(jù)有特殊的處理方式,比如壓縮等等,在數(shù)據(jù)庫(kù)中存儲(chǔ)BLOB數(shù)據(jù)可以方便的進(jìn)行檢索,展示,備份等操作。但是由于BLOB型數(shù)據(jù)本身比較大,存儲(chǔ)量太大時(shí)數(shù)據(jù)量太大容易拖慢數(shù)據(jù)庫(kù)性能,所以一般的說(shuō)法都是盡量不要在數(shù)據(jù)庫(kù)中存儲(chǔ)這類信息。特別是圖片,音視頻。針對(duì)這類文件一般的做法是將其保存在系統(tǒng)的某個(gè)路徑鐘中,而在數(shù)據(jù)庫(kù)中存儲(chǔ)對(duì)應(yīng)的路徑

操作BLOB型數(shù)據(jù)的一般方法

一般針對(duì)BLOB不能像普通數(shù)據(jù)那樣操作,而需要一些特殊的操作,在OLEDB中通過(guò)設(shè)置綁定結(jié)構(gòu)中的一些特殊值最終指定獲取BLOB型數(shù)據(jù)的一個(gè)ISequentialStream接口指針,最終會(huì)通過(guò)這個(gè)接口來(lái)進(jìn)行BLOB型數(shù)據(jù)的讀寫(xiě)操作

判斷一個(gè)列是否是BLOB型數(shù)據(jù)

判斷某個(gè)列是否是BLOB型數(shù)據(jù)一般通過(guò)如下兩個(gè)條件:

  1. pColumnInfo[i].wType == DBTYPE_IUNKNOW : 包含當(dāng)列信息的DBCOLUMNSINFO 結(jié)構(gòu)體對(duì)象的wType值為DBTYPE_IUNKNOW,該列的類型為DBTYPE_IUNKNOW,該條件也被稱為列類型判定
  2. pColumnInfo[i].dwFlags & DBCOLUMNFLAGS_ISLONG :當(dāng)列信息中的dwFlag值為DBCOLUMNFLAGS_ISLONG,也就是說(shuō)該列的標(biāo)識(shí)中包含DBCOLUMNFLAGS_ISLONG屬性,該判定條件也被稱之為列標(biāo)識(shí)判定
    當(dāng)這兩個(gè)條件之一成立之時(shí),我們就可以斷定這列為BLOB型數(shù)據(jù)

BLOG型數(shù)據(jù)的綁定

在進(jìn)行BLOB型數(shù)據(jù)的綁定也有特殊要求,主要體現(xiàn)在下面幾點(diǎn):

  1. 綁定結(jié)構(gòu)的cbMaxLength 需要設(shè)置為0
  2. 綁定結(jié)構(gòu)的wType設(shè)置為DBTYPE_IUNKNOW
  3. 為結(jié)構(gòu)的pObject指針?lè)峙鋬?nèi)存,大小等于DBOBJECT結(jié)構(gòu)的大小
  4. 指定pObject的成員
    pObject->iid = IID_ISequentialStream
    pObject->dwFlags = STGM_READ
  5. 為行緩沖長(zhǎng)度加上一個(gè)IStream指針的長(zhǎng)度,此時(shí)數(shù)據(jù)源不再提供查詢到的數(shù)據(jù)而提供一個(gè)接口指針,后續(xù)對(duì)BLOB數(shù)據(jù)的操作都使用該指針進(jìn)行
    最后使用完后記得釋放pObject所指向的內(nèi)存空間

讀取BLOB數(shù)據(jù)

根據(jù)前面所說(shuō)的創(chuàng)建綁定結(jié)構(gòu),并為綁定結(jié)構(gòu)賦值,最終可以從結(jié)果集中獲取到一個(gè)ISequentialStream接口指針。調(diào)用接口的Read方法可以讀取到BLOB列中的數(shù)據(jù),而B(niǎo)LOB數(shù)據(jù)的長(zhǎng)度存儲(chǔ)在綁定時(shí)指定的數(shù)據(jù)長(zhǎng)度內(nèi)存偏移處,這與普通列的長(zhǎng)度存放返回方式是一樣的,一般BLOB數(shù)據(jù)都比較長(zhǎng),這個(gè)時(shí)候就需要分段讀取。
在使用ISequentialStream接口操作BLOB型數(shù)據(jù)時(shí)需要注意的一個(gè)問(wèn)題是,有的數(shù)據(jù)庫(kù)不支持在一個(gè)訪問(wèn)器中訪問(wèn)多個(gè)BLOB數(shù)據(jù)列。一般BLOB數(shù)據(jù)列及其的消耗資源,并且數(shù)據(jù)庫(kù)鼓勵(lì)我們?cè)谠O(shè)計(jì)數(shù)據(jù)庫(kù)表結(jié)構(gòu)的時(shí)候做到一行只有一列BLOB數(shù)據(jù),因此很多數(shù)據(jù)庫(kù)并不支持在一個(gè)訪問(wèn)器中讀取多個(gè)BLOB數(shù)據(jù)。
要判斷數(shù)據(jù)庫(kù)是否支持在一個(gè)訪問(wèn)器中讀取多個(gè)BLOB數(shù)據(jù),可以獲取DBPROP_MULTIPLESTORAGEOBJECTS屬性,該屬性屬于屬性集DBPROPSET_ROWSET,它是一個(gè)只讀屬性,如果該屬性的值為TRUE表示支持,為FALSE表示不支持。
下面是一個(gè)讀取BLOB型數(shù)據(jù)的例子,數(shù)據(jù)庫(kù)中的表結(jié)構(gòu)為:id(int)、text(image)、png(image)、jpg(image)

void ReadBLOB(IRowset *pIRowset)
{
    COM_DECLARE_INTERFACE(IColumnsInfo);
    COM_DECLARE_INTERFACE(IAccessor);

    DBORDINAL cColumns = 0;
    DBCOLUMNINFO* rgColumnsInfo = NULL;
    LPOLESTR lpszColumnsName = NULL;
    DBBINDING* rgBindings = NULL;
    DBBINDING** ppBindings = NULL; //綁定結(jié)構(gòu)數(shù)組
    DWORD *puDataLen = NULL; //當(dāng)前訪問(wèn)器所需內(nèi)存大小
    DWORD *pulColCnt = NULL; //當(dāng)前訪問(wèn)器中包含的項(xiàng)
    ULONG ulBindCnt = 0; //訪問(wèn)器的數(shù)量
    ULONG uBlob = 0; //當(dāng)前有多少blob數(shù)據(jù)
    HACCESSOR* phAccessor = NULL;
    HROW* hRow = NULL;
    DBCOUNTITEM ulGetRows = 0;
    ULONG uCols = 0;

    PVOID pData1 = NULL; //第1個(gè)訪問(wèn)器中數(shù)據(jù)的緩沖
    PVOID pData2 = NULL; //第2個(gè)訪問(wèn)器中數(shù)據(jù)的緩沖
    PVOID pData3 = NULL; //第3個(gè)訪問(wèn)器中數(shù)據(jù)的緩沖

    HRESULT hRes = pIRowset->QueryInterface(IID_IColumnsInfo, (void**)&pIColumnsInfo);
    COM_SUCCESS(hRes, _T("查詢接口pIColumnsInfo失敗,錯(cuò)誤碼為:%08x\n"), hRes);
    hRes = pIColumnsInfo->GetColumnInfo(&cColumns, &rgColumnsInfo, &lpszColumnsName);
    COM_SUCCESS(hRes, _T("獲取結(jié)果集列信息失敗,錯(cuò)誤碼為:%08x\n"), hRes);

    ppBindings = (DBBINDING**)COM_ALLOC(sizeof(DBBINDING*));
    rgBindings = (DBBINDING*)COM_ALLOC(sizeof(DBBINDING) * cColumns);
    pulColCnt = (DWORD*)COM_ALLOC(sizeof(DWORD));
    puDataLen =  (DWORD*)COM_ALLOC(sizeof(DWORD));
    for (int i = 0; i < cColumns; i++)
    {
    //如果當(dāng)前訪問(wèn)器對(duì)應(yīng)的綁定結(jié)構(gòu)的數(shù)組的首地址為空,將當(dāng)前綁定結(jié)構(gòu)指針作為綁定結(jié)構(gòu)數(shù)組的首地址
        if (NULL == ppBindings[ulBindCnt])
        {
            ppBindings[ulBindCnt] = &rgBindings[i];
        }

        ++pulColCnt[ulBindCnt];
        rgBindings[i].bPrecision = rgColumnsInfo[i].bPrecision;
        rgBindings[i].bScale = rgBindings[i].bScale;
        rgBindings[i].cbMaxLen = 10 * sizeof(WCHAR);
        rgBindings[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
        rgBindings[i].dwPart = DBPART_LENGTH | DBPART_STATUS | DBPART_VALUE;
        rgBindings[i].eParamIO = DBPARAMIO_NOTPARAM;
        rgBindings[i].iOrdinal = rgColumnsInfo[i].iOrdinal;
        rgBindings[i].obStatus = puDataLen[ulBindCnt];
        rgBindings[i].obLength = puDataLen[ulBindCnt] + sizeof(DBSTATUS);
        rgBindings[i].obValue = rgBindings[i].obLength + sizeof(ULONG);
        rgBindings[i].wType = DBTYPE_WSTR;

        if (rgColumnsInfo[i].wType == DBTYPE_IUNKNOWN ||
            rgColumnsInfo[i].dwFlags & DBCOLUMNFLAGS_ISLONG)
        {
            rgBindings[i].cbMaxLen = 0;
            rgBindings[i].wType = DBTYPE_IUNKNOWN;
            rgBindings[i].pObject = (DBOBJECT*)COM_ALLOC(sizeof(DBOBJECT));
            rgBindings[i].pObject->iid = IID_ISequentialStream;
            rgBindings[i].pObject->dwFlags = STGM_READ;
            uBlob++;
        }

        //記錄下每個(gè)訪問(wèn)器所需內(nèi)存的大小
        puDataLen[ulBindCnt] = rgBindings[i].obValue + rgBindings[i].cbMaxLen;
        if (rgBindings[i].wType == DBTYPE_IUNKNOWN)
        {
            puDataLen[ulBindCnt] = rgBindings[i].obValue + sizeof(ISequentialStream*);
        }

        puDataLen[ulBindCnt] = UPGROUND(puDataLen[ulBindCnt]);
        //判斷當(dāng)前是否需要?jiǎng)?chuàng)建單獨(dú)的訪問(wèn)器
        if ((uBlob || rgBindings[i].iOrdinal == 0))
        {
            ulBindCnt++;
            ppBindings = (DBBINDING**)COM_REALLOC(ppBindings, sizeof(DBBINDING*) * (ulBindCnt + 1));
            puDataLen = (DWORD*)COM_REALLOC(puDataLen, sizeof(DWORD) * (ulBindCnt + 1));
            pulColCnt = (DWORD*)COM_REALLOC(pulColCnt, sizeof(DWORD) * (ulBindCnt + 1));
        }
    }

    //創(chuàng)建訪問(wèn)器
    phAccessor = (HACCESSOR*)COM_ALLOC( (ulBindCnt + 1) * sizeof(HACCESSOR));
    hRes = pIRowset->QueryInterface(IID_IAccessor, (void**)&pIAccessor);
    COM_SUCCESS(hRes, _T("查詢IAccessor接口失敗,錯(cuò)誤碼為:%08x\n"), hRes);
    for (int i = 0; i < ulBindCnt; i++)
    {
        hRes = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, pulColCnt[i], ppBindings[i], 0, &phAccessor[i], NULL);
        COM_SUCCESS(hRes, _T("創(chuàng)建訪問(wèn)器失敗,錯(cuò)誤碼為:%08x\n"), hRes);
    }

    //讀取其中的一行數(shù)據(jù)
    hRes = pIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &ulGetRows, &hRow);
    COM_SUCCESS(hRes, _T("讀取行數(shù)據(jù)失敗,錯(cuò)誤碼為:%08x\n"), hRes);

    //讀取第一個(gè)綁定結(jié)構(gòu)中的信息
    pData1 = COM_ALLOC(puDataLen[0]);
    hRes = pIRowset->GetData(hRow[0], phAccessor[0], pData1);
    for(int i = 0; i < pulColCnt[0]; i++)
    {
        if (ppBindings[0][i].wType == DBTYPE_IUNKNOWN)
        {
            DBSTATUS dbStatus = *(DBSTATUS*)((BYTE*)pData1 + ppBindings[0][i].obStatus);
            if (dbStatus == DBSTATUS_S_OK)
            {
                ULONG uFileLen = *(ULONG*)((BYTE*)pData1 + ppBindings[0][i].obLength);
                if (uFileLen > 0)
                {
                    DWORD dwReaded = 0;
                    PVOID pFileData = COM_ALLOC(uFileLen);
                    ZeroMemory(pFileData, uFileLen);
                    ISequentialStream  *pSeqStream = *(ISequentialStream**)((BYTE*)pData1 + ppBindings[0][i].obValue);
                    pSeqStream->Read(pFileData, uFileLen, &dwReaded);

                    WriteFileData(_T("1.txt"), pFileData, dwReaded);
                }
            }
        }
    }

    //后續(xù)的部分就不再寫(xiě)出來(lái)了,寫(xiě)法與上面的代碼類似
    pIRowset->ReleaseRows(1, hRow, NULL, NULL, NULL);
__CLEAR_UP:
  //后面是清理的代碼

由于我們事先知道數(shù)據(jù)表的結(jié)構(gòu),它有3個(gè)BLOB型數(shù)據(jù),所以這里直接定義了3個(gè)緩沖用來(lái)接收3個(gè)BLOB型數(shù)據(jù)。為了方便檢測(cè),我們另外寫(xiě)了一個(gè)的函數(shù),將讀取出來(lái)的BLOB數(shù)據(jù)寫(xiě)入到文件中,事后以文件顯示是否正確來(lái)測(cè)試這段代碼
首先還是與以前一樣,獲取數(shù)據(jù)表的結(jié)構(gòu),然后進(jìn)行綁定,注意這里由于使用的是SQL Server,它不支持一個(gè)訪問(wèn)器中訪問(wèn)多個(gè)BLOB,所以這里沒(méi)有判斷直接綁定不同的訪問(wèn)器。
在綁定的時(shí)候使用ulBindCnt作為當(dāng)前訪問(wèn)器的數(shù)量,在循環(huán)里面有一個(gè)判斷當(dāng)(uBlob || rgBindings[i].iOrdinal == 0) && (ulBindCnt != cColumns - 1)條件成立時(shí)將訪問(wèn)器的數(shù)量加1,該條件表示之前已經(jīng)有blob型數(shù)據(jù)(之前SQL不支持一個(gè)訪問(wèn)器訪問(wèn)多個(gè)BLOB,如果之前已經(jīng)有BLOB數(shù)據(jù)了,就需要另外創(chuàng)建訪問(wèn)器)或者當(dāng)前是第0行(因?yàn)榈?行只允許讀,所以將其作為與BLOB型數(shù)據(jù)一樣處理),當(dāng)這些條件成立時(shí)會(huì)新增一個(gè)訪問(wèn)器,而隨著訪問(wèn)器的增加,需要改變ppBindings數(shù)組中的元素,該數(shù)組存儲(chǔ)的是訪問(wèn)器對(duì)應(yīng)的綁定結(jié)構(gòu)開(kāi)始的指針。數(shù)組puDataLen表示的是當(dāng)前訪問(wèn)器所需內(nèi)存的大小,pulColCnt表示當(dāng)前訪問(wèn)器中共有多少列,針對(duì)這個(gè)表最終這些結(jié)構(gòu)的內(nèi)容大致如下圖:


各個(gè)數(shù)組元素圖

綁定完成之后,后面就是根據(jù)數(shù)組中的內(nèi)容創(chuàng)建對(duì)應(yīng)的訪問(wèn)器,然后綁定、讀取數(shù)據(jù),針對(duì)BLOB數(shù)據(jù),我們還是一樣從對(duì)應(yīng)緩沖的obValue偏移處得到接口指針,然后調(diào)用接口的Read方法讀取,最后寫(xiě)入文件

BLOB數(shù)據(jù)的寫(xiě)入:

要寫(xiě)入BLOB型數(shù)據(jù)也需要使用ISequentialStream接口,但是它不像之前可以直接使用接口的Write方法,寫(xiě)入的對(duì)象必須要自己從ISequentialStream接口派生,并指定一段內(nèi)存作為緩沖,以便供OLEDB組件調(diào)用寫(xiě)方法時(shí)作為數(shù)據(jù)緩沖。這段緩沖必須要保證分配在COM堆上,也就是要使用CoTaskMemory分配內(nèi)存。
這里涉及到的對(duì)象主要有IStream、ISequentialStream、IStorage、ILockBytes,同樣,并不是所有數(shù)據(jù)源都支持這4類對(duì)象,具體支持哪些可以查詢DBPROPSET_DATASOURCEINFO屬性集中的DBPROP_STRUCTUREDSTORAGE屬性來(lái)判定,目前SQL Server中支持ISequentialStream接口。
雖然我們可以使用這種方式來(lái)實(shí)現(xiàn)讀寫(xiě)B(tài)LOB,但是每種數(shù)據(jù)源支持的程度不同,而且有的數(shù)據(jù)源甚至不支持這種方式,為了查詢對(duì)讀寫(xiě)B(tài)LOB數(shù)據(jù)支持到何種程度,可以查詢DBPROPSET_DATASOURCEINFO屬性集合的DBPROP_OLEOBJECTS屬性來(lái)判定
通常有以下幾種支持方式(DBPROP_OLEOBJECTS屬性的值,按位設(shè)置):

  1. DBPROPVAL_OO_BLOB: 就是之前介紹的接口方式,使用接口的方式來(lái)讀寫(xiě)B(tài)LOB數(shù)據(jù)
    DBPROPVAL_OO_DIRECTBIND: 可以直接綁定在行中,通過(guò)行訪問(wèn)器像普通列一樣訪問(wèn),也就是說(shuō)它不需要獲取專門的指針來(lái)操作,他可以就像操作普通數(shù)據(jù)那樣,分配對(duì)應(yīng)內(nèi)存就可以訪問(wèn),但是要注意分配內(nèi)存的大小,每行中對(duì)應(yīng)列中BLOB的數(shù)據(jù)長(zhǎng)度差別可能會(huì)很明顯,比如有的可能是一部長(zhǎng)達(dá)2小時(shí)的電影文件,而有的可能是一部短視頻,它們之間的差距可能會(huì)達(dá)到上G,而按照最小的來(lái)可能會(huì)發(fā)生截?cái)?,按最大的分配可能?huì)發(fā)生多達(dá)好幾個(gè)G的內(nèi)存浪費(fèi)
    DBPROPVAL_OO_IPERSIST:通過(guò)IPersistStream, IPersistStreamInit, or IPersistStorage三個(gè)接口的Persist對(duì)象訪問(wèn)
    DBPROPVAL_OO_ROWOBJECT: 支持整行作為一個(gè)對(duì)象來(lái)訪問(wèn),通過(guò)結(jié)果集對(duì)象的IGetRow接口來(lái)獲得行對(duì)象,但是這種模式會(huì)破壞第三范式,所以一般數(shù)據(jù)庫(kù)都不支持
    DBPROPVAL_OO_SCOPED: 通過(guò)IScopedOperations接口來(lái)暴露行對(duì)象,通過(guò)這個(gè)接口可以暴露一個(gè)樹(shù)形的結(jié)果集對(duì)象
    DBPROPVAL_OO_SINGLETON: 直接通過(guò)ICommand::Execute和IOpenRowset::OpenRowset來(lái)打開(kāi)行對(duì)象
    下面是插入BLOB數(shù)據(jù)的一個(gè)實(shí)例
//自定義一個(gè)
class CSeqStream : public ISequentialStream
{
public:
    // Constructors
    CSeqStream();
    virtual ~CSeqStream();
public:
    virtual BOOL Seek(ULONG iPos); //將當(dāng)前內(nèi)存指針偏移到指定位置
    virtual BOOL CompareData(void* pBuffer); //比較兩段內(nèi)存中的值
    virtual ULONG Length()
    {
        return m_cBufSize;
    };
    virtual operator void* const()
    {
        return m_pBuffer;
    };
public:
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);
    STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppv);

  //讀寫(xiě)內(nèi)存的操作,這些是必須實(shí)現(xiàn)的函數(shù)
    STDMETHODIMP Read(
        /* [out] */ void __RPC_FAR *pv,
        /* [in]  */ ULONG cb,
        /* [out] */ ULONG __RPC_FAR *pcbRead);

    STDMETHODIMP Write(
        /* [in] */ const void __RPC_FAR *pv,
        /* [in] */ ULONG cb,
        /* [out]*/ ULONG __RPC_FAR *pcbWritten);
private:
    ULONG m_cRef;       // reference count
    void* m_pBuffer;    // buffer
    ULONG m_cBufSize;   // buffer size
    ULONG m_iPos;       // current index position in the buffer
};
//插入數(shù)據(jù)第一列BLOB數(shù)據(jù)
    //這里由于已經(jīng)事先知道每列的數(shù)據(jù)結(jié)構(gòu),因此采用偷懶的方法,一行行的插入
    pData1 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pdwDataLen[nCol]);
    for(int i = 0; i < pulColCnt[nCol]; i++)
    {
        if (DBTYPE_IUNKNOWN == ppBindings[nCol][i].wType)
        {
            *(DBSTATUS*)((BYTE*)pData1 + ppBindings[nCol][i].obStatus) = DBSTATUS_S_OK;
            CSeqStream *pSeqStream = new CSeqStream();
            GetFileData(_T("test.txt"), dwFileLen, pFileData);
            pSeqStream->Write(pFileData, dwFileLen, &dwWritten);
            pSeqStream->Seek(0); //寫(xiě)這個(gè)操作將緩存的指針偏移到了最后,需要調(diào)整一下,以便OLEDB組件在插入BLOB數(shù)據(jù)時(shí)從緩存中讀取
            HeapFree(GetProcessHeap(), 0, pFileData);
            *(ULONG*)((BYTE*)pData1 + ppBindings[nCol][i].obLength) = dwFileLen;
            *(ISequentialStream**)((BYTE*)pData1 + ppBindings[nCol][i].obValue) = pSeqStream;
            //此處不用release pSeqStream,COM組件會(huì)自動(dòng)釋放
        }else
        {
            //根據(jù)數(shù)據(jù)庫(kù)定義,此處應(yīng)該為ID
            *(ULONG*)((BYTE*)pData1 + ppBindings[nCol][i].obLength) = 10;
            if (DBTYPE_WSTR == ppBindings[nCol][i].wType)
            {
                StringCchCopy((LPOLESTR)((BYTE*)pData1 + ppBindings[nCol][i].obValue), 10, SysAllocString(OLESTR("1")));
            }
        }
    }

    hRes = pIRowsetChange->InsertRow(DB_NULL_HCHAPTER, phAccessor[nCol], pData1, &hNewRow);
    COM_SUCCESS(hRes, _T("插入第1列BLOB數(shù)據(jù)失敗,錯(cuò)誤碼為:%08x\n"), hRes);

在上面的代碼中首先定義一個(gè)派生類,用來(lái)進(jìn)行BLOB數(shù)據(jù)的讀寫(xiě),然后在后面的代碼中演示了如何使用它
在后面的一段代碼中,基本步驟和之前一樣,經(jīng)過(guò)連接數(shù)據(jù)源、創(chuàng)建回話對(duì)象,打開(kāi)表,然后綁定,獲取行訪問(wèn)器,這里由于代碼基本不變,為了節(jié)約篇幅所以省略它們,只貼出最重要的部分。
在插入的代碼中,首先查找訪問(wèn)器中的各個(gè)列的屬性,如果是BLOB數(shù)據(jù)就采用BLOB數(shù)據(jù)的插入辦法,否則用一般數(shù)據(jù)的插入辦法。插入BLOB數(shù)據(jù)時(shí),首先創(chuàng)建一個(gè)派生類的對(duì)象,注意此處由于后續(xù)要交給OLEDB組件調(diào)用,所以不能用棧內(nèi)存。我們先調(diào)用類的Write方法將內(nèi)存寫(xiě)入對(duì)應(yīng)的緩沖中,然后調(diào)用Seek函數(shù)將內(nèi)存指針偏移到緩沖的首地址,這個(gè)指針的作用就相當(dāng)于文件的文件指針,COM組件在調(diào)用對(duì)應(yīng)函數(shù)將它插入數(shù)據(jù)庫(kù)時(shí)會(huì)采用這個(gè)內(nèi)存的指針,所以必須將其置到首地址處。讓后將對(duì)象的指針?lè)湃氲綄?duì)應(yīng)的obvalues偏移中,設(shè)置對(duì)應(yīng)的數(shù)據(jù)大小為BLOB數(shù)據(jù)的大小,最后只要像普通數(shù)據(jù)類型那樣調(diào)用對(duì)應(yīng)的更新方法即可實(shí)現(xiàn)BLOB數(shù)據(jù)的插入

最后貼上兩個(gè)例子的詳細(xì)代碼地址
示例1:BLOB數(shù)據(jù)的讀取
示例2:BLOB數(shù)據(jù)的插入

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容