ODBC方式操作ACCESS数据库OLE类型字段

在实际项目的过程中,往往需要在数据库中存储图片等二进制数据。这篇博客主要介绍下怎么用c++语言和ACCESS数据库完成这项任务。包括两个部分:文件的存储、文件的读取。
此外,连接数据库的方式有多种,包括:ADO、ODBC、OLE等。这篇博客只介绍ODBC方式。

存储数据

CFile file(filepath,CFile::modeRead);   //打开文件
int len = file.GetLength();   //获取文件字节数
HGLOBAL hm = ::GlobalAlloc(GMEM_MOVEABLE,len);  //分配len大小的全局空间,作为缓冲区
LPVOID lp = ::GlobalLock(hm) ;  //这一步是必须的
int n = file.Read(lp,len) ; // 将文件内容读取到缓冲区,返回的n表示读取的长度,n=len 那么就对了

CDatabase dbase;  
//  这里假设dbase已经正确连接数据库
CRecordset cr(&dbase);
SQLHSTMT hstmt = cr.m_hstmt;  //statement句柄
SQLWCHAR * cmd = _T("insert into tbName (colName) values(?)")  //注1
SQLRETURN  ret = SQLPrepare(htsmt, cmd, SQL_NTS); 
SQLLEN sqllen = len;
SQLBindParameter(hstmt,1,SQL_PARAM_INPUT,SQL_C_BINARY,SQL_VARBINARY,len,0,lp,len,&sqllen);  //注2
ret = SQLExecute(hstmt);   //执行SQL语句,完成存储操作,ret = 0,表示成功
cr.close();
dbase.close();
::GlobalUnLock(lp);
::GlobalFree(hm);

注1:这个语句是具体的SQL语句,在这里是往colName字段插入我们的OLE类型数据(插入后在ACCESS中显示的是“长二进制数据”)。values中的“?”很重要,其实就是占位符的意思,告诉数据库引擎,这里是需要绑定参数的。
注2:SQLBindParameter函数将缓冲区中的数据与SQL语句中需要的数据绑定,其实就是告诉数据库去哪读取需要的数据。该函数的原型是:

SQLRETURN SQLBindParameter(
      SQLHSTMT        StatementHandle,     // statement句柄
      SQLUSMALLINT    ParameterNumber,    // 参数位于语句中的序号,最小为1
      SQLSMALLINT     InputOutputType,    // 入参/出参类型标识
      SQLSMALLINT     ValueType,          // 对应的C数据类型标识
      SQLSMALLINT     ParameterType,     // 对应的SQL数据类型标识
      SQLULEN         ColumnSize,          // 字段长度,其实就是ACCESS数据库中对应插入字段的长度
      SQLSMALLINT     DecimalDigits,       // 如果是浮点数,则对应字段精度,否则为0
      SQLPOINTER      ParameterValuePtr,   // 参数缓存区指针
      SQLLEN          BufferLength,        // 缓存区长度
      SQLLEN *        StrLen_or_IndPtr);   //对于非字符串,用于表示参数字节长度;对于字符串,可用SQL_NTS值。

需要注意的是参数ValueType和ParameterType,需要对应起来,不然读取的时候会出现错读的现象。对于二进制数据,就可以使用SQL_C_BINARY和SQL_VARBINARY。
还有就是ColumnSize参数。这个参数是表示数据库字段宽度:如果数据库中对应字段的整数型,那ColumnSize = 4;如果是浮点型,那么就是8;如果是字符,那么就是设置的字符长度(最大是255);在这里是OLE对象类型,这个类型在ACCESS数据库中没有长度设置(好像最大是一个G),所以我就用的存储所需的实际大小值。
BufferLength和StrLen_or_IndPtr不要混淆,对于存储二进制数据,BufferLength表示参数缓冲区的大小,StrLen_or_IndPtr表示的是实际需要的参数大小;这两个是可以不一样的,因为缓冲区完全可以设置的比需要的大。这是在本例中,设置的大小是一样的。

读取二进制数据

CDatabase dbase;  
//  这里假设dbase已经正确连接数据库
CRecordset rs(&dbase);
CString cmd = _T("select colNmae,lenb(colName) as len from table ");
if (rs.open(CRecordeset::forwardOnly,cmd))
{
	while (!rs.IsEOF())
	{
		CDBVariant variant;
		rs.GetFieldValue(_T("colName"),variant);
		LPVOID pdata = ::GlobalLock(variant.m_pbinary->m_hdata);
		rs.GetFieldValue(_T("len"),variant);
		int len = variant.m_iVal;
		//从pdata指针开始读取len长度字节,即为所需的内容
		::GlobalUnLock(pdata);
	}
}

第一次写博客,希望大家能够不吝指正!

更多推荐

ODBC方式操作ACCESS数据库OLE类型字段