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);
}
}
第一次写博客,希望大家能够不吝指正!