/// <summary>通过 OLEDB 模板保存二进制数据到数据库的 BLOB 字段,代码量减少至 MSDN 上推荐代码的几十分之一,SDK、WTL、MFC 通用</summary>
/// <param name="session">传入的 session 应已经正常连接数据库</param>
/// <param name="cmdBlob">外部声明的目的是在出现错误时,外部代码能通过其获取错误,否则也可改为内部声明</param>
/// <param name="sSelectSQL">查询二进制数据的 SQL 语句,需满足:返回一个 BLOB 查询字段且返回唯一记录。例如:SELECT Photo FROM TB_PHOTO WHERE ID=1</param>
/// <param name="pIStream">已写入了二进制(BLOB) 数据的 IStream 接口。</param>
/// <returns>标准 HRESULT</returns>
HRESULT SaveBLOB(CSession& session, CCommand<CManualAccessor>& cmdBlob, LPCTSTR sSelectSQL, IStream* pIStream)
{
_ASSERT(session.m_spOpenRowset != NULL); // 传入的 session 应已经正常连接数据库
_ASSERT(sSelectSQL != NULL); // 例如:SELECT Photo FROM TB_PHOTO WHERE ID=1
_ASSERT(pIStream != NULL); // 已写入了二进制(BLOB) 数据的 IStream 接口。
HRESULT hResult = 0;
// 行属性
CDBPropSet propSet(DBPROPSET_ROWSET); // 这是行属性
propSet.AddProperty(DBPROP_IRowsetChange, true); // 设置支持 IRowsetChange 接口,否则后面 CManualAccessor 的 m_spRowsetChange 接口为空
propSet.AddProperty(DBPROP_UPDATABILITY, DBPROPVAL_UP_CHANGE); // 设置结果集允许修改
// 借用 CManualAccessor 特性简化代码,否则额外需要很长的代码以获取一些接口
if (FAILED(hResult = cmdBlob.Open(session, sSelectSQL, &propSet)))
return hResult;
// 借用 OLEDB 模板函数填充绑定结构 DBBINDING
DBOBJECT dbObject = { STGM_READ, __uuidof(ISequentialStream) };
DBBINDING dbBinding = { 0 };
CAccessorBase::Bind(&dbBinding, 1, DBTYPE_IUNKNOWN, sizeof(IUnknown*), 0, 0, DBPARAMIO_NOTPARAM, 0, 0, 0, &dbObject);
// 获取 IAccessor 接口并根据 DBBINDING 自行创建 HACCESSOR (CManualAccessor 中未直接提供该 IAccessor 接口)
CComPtr<IAccessor> pIAccessor = NULL;
if (FAILED(hResult = cmdBlob.m_spRowsetChange->QueryInterface(IID_IAccessor, (void**)&pIAccessor)))
return hResult;
HACCESSOR hAccessor = DB_NULL_HACCESSOR;
if (FAILED(hResult = pIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &dbBinding, sizeof(ISequentialStream*), &hAccessor, NULL)))
return hResult;
// cmdBlob.CreateAccessor(1, &dbBinding, sizeof(ISequentialStream*));
// 借用 OLEDB 模板函数,效果与 cmdBlob.GetNextResult() 相同
if (S_OK != (hResult = cmdBlob.MoveNext()))
return hResult;
// 复位 IStream 至开始位置
if (FAILED(hResult = pIStream->Seek({ 0 }, 0, NULL)))
return hResult;
// IStream 继承自 ISequentialStream, 因此可以直接从 IStream 获取 ISequentialStream 接口
ISequentialStream* pISequentialStream = NULL;//(不能用 CComPtr,这里的 ISequentialStream 接口似乎会自动释放 ...)
if (FAILED(hResult = pIStream->QueryInterface(IID_ISequentialStream, (void**)&pISequentialStream)))
return hResult;
// CManualAccessor 自带的 SetData 不能实现二进制写入,需直接调用 IRowsetChange 的 SetData 方法
return cmdBlob.m_spRowsetChange->SetData(cmdBlob.m_hRow, hAccessor, &pISequentialStream);
}
OLEDB 保存 BLOB 到数据库的最简洁代码
最新推荐文章于 2021-02-21 05:04:53 发布