Share via


TN045:针对 Long Varchar/Varbinary 的 MFC/数据库支持

注意

以下技术说明在首次包括在联机文档中后未更新。 因此,某些过程和主题可能已过时或不正确。 要获得最新信息,建议你在联机文档索引中搜索热点话题。

此说明介绍如何使用 MFC 数据库类检索和发送 ODBC SQL_LONGVARCHAR 和 SQL_LONGVARBINARY 数据类型。

长型 Varchar/Varbinary 支持概述

ODBC SQL_LONG_VARCHAR 和 SQL_LONGBINARY 数据类型(这里称为长数据列)可以容纳大量数据。 有 3 种方法可以处理这些数据:

  • 将其绑定到 CString/CByteArray

  • 将其绑定到 CLongBinary

  • 不要完全绑定它,而是手动检索并发送长数据值,独立于数据库类。

这三种方法中的每一种都有优点和缺点。

查询的参数不支持长数据列。 它们仅支持 outputColumns。

将长数据列绑定到 CString/CByteArray

优点:

此方法易于理解,并且你使用熟悉的类。 该框架为具有 DDX_TextCString 提供 CFormView 支持。 你可以使用 CStringCByteArray 类实现许多常规字符串或集合功能,且可以控制本地分配的内存量来保存数据值。 该框架在 EditAddNew 函数调用期间维护字段数据的旧副本,且可以自动检测对数据的更改。

注意

由于 CString 旨在处理字符数据,而 CByteArray 旨在处理二进制数据,因此建议将字符数据 (SQL_LONGVARCHAR) 放入 CString 中,将二进制数据 (SQL_LONGVARBINARY) 放入 CByteArray 中。

CStringCByteArray 的 RFX 函数具有附加参数,可用于重写已分配内存的默认大小,以保存数据列的检索值。 请注意以下函数声明中的 nMaxLength 参数:

void AFXAPI RFX_Text(CFieldExchange* pFX,
    const char *szName,
    CString& value,
    int nMaxLength = 255,
    int nColumnType =
    SQL_VARCHAR);

void AFXAPI RFX_Binary(CFieldExchange* pFX,
    const char *szName,
    CByteArray& value,
    int nMaxLength = 255);

如果将长数据列检索到 CStringCByteArray,则返回的最大数据量默认为 255 个字节。 超出此范围的任何内容将被忽略。 在这种情况下,框架将引发异常 AFX_SQL_ERROR_DATA_TRUNCATED。 幸运的是,可以显式将 nMaxLength 增加到更大的值,最大为 MAXINT

注意

MFC 使用 nMaxLength 的值来设置 SQLBindColumn 函数的本地缓冲区。 这是用于存储数据的本地缓冲区,实际上不会影响 ODBC 驱动程序返回的数据量。 RFX_TextRFX_Binary 只使用 SQLFetch 进行一次调用,以从后端数据库检索数据。 每个 ODBC 驱动程序对可以在单个提取中返回的数据量有不同的限制。 此限制可能比 nMaxLength 中设置的值要小得多,在这种情况下,将引发异常 AFX_SQL_ERROR_DATA_TRUNCATED。 在这些情况下,切换到使用 RFX_LongBinary 而不是 RFX_TextRFX_Binary,以便检索所有数据。

ClassWizard 会将 SQL_LONGVARCHAR 绑定到 CString,或将 SQL_LONGVARBINARY 绑定到 CByteArray。 如果要将超过 255 个字节分配到要检索长数据列的位置,则可以为 nMaxLength 提供显式值。

当长数据列绑定到 CStringCByteArray 时,更新字段的方式与其绑定到 SQL_VARCHAR 或 SQL_VARBINARY 时相同。 在 Edit 期间,数据值会缓存,稍后在调用 Update 来检测数据值更改并相应地为该列设置 Dirty 和 Null 值时会进行比较。

将长数据列绑定到 CLongBinary

如果长数据列可能包含超过 MAXINT 个字节的数据,则应考虑将其检索到 CLongBinary 中。

优点:

这会检索整个长数据列,一直到可用内存。

缺点:

数据保存在内存中。 对于非常大量的数据,这种方法的成本也非常高。 必须为绑定数据成员调用 SetFieldDirty,以确保该字段包含在 Update 操作中。

如果将长数据列检索到 CLongBinary 中,则数据库类将检查长数据列的总大小,然后分配一个足以容纳整个数据值的 HGLOBAL 内存段。 然后,数据库类将整个数据值检索到已分配的 HGLOBAL

如果数据源无法返回长数据列的预期大小,那么框架将引发异常 AFX_SQL_ERROR_SQL_NO_TOTAL。 如果尝试分配 HGLOBAL 失败,则会引发标准内存异常。

ClassWizard 会将 SQL_LONGVARCHAR 或 SQL_LONGVARBINARY 绑定到 CLongBinary。 选择 CLongBinary 作为“添加成员变量”对话框中的“变量类型”。 然后,ClassWizard 将向 DoFieldExchange 调用添加一个 RFX_LongBinary 调用,并递增绑定字段的总数。

若要更新长数据列值,请首先通过对 CLongBinary 的 m_hData 成员调用 ::GlobalSize 确保分配的 HGLOBAL 足够大以容纳新数据。 如果太小,请释放 HGLOBAL 并分配相应的大小。 然后,设置 m_dwDataLength 以反映新大小。

否则,如果 m_dwDataLength 大于要替换的数据大小,则可以释放和重新分配 HGLOBAL,或继续分配它。 请确保指示实际在 m_dwDataLength 中使用的字节数。

更新 CLongBinary 的工作原理

无需了解更新 CLongBinary 的工作原理,但如果选择下面所述的第三种方法,它可以作为一个关于如何向数据源发送长数据值的很好的示例。

注意

若要将 CLongBinary 字段包含在更新中,必须为该字段显式调用 SetFieldDirty。 如果对字段进行任何更改,包括将其设置为 Null,则必须调用 SetFieldDirty。 还必须调用 SetFieldNull,第二个参数为 FALSE,才能将字段标记为具有值。

更新 CLongBinary 字段时,数据库类使用 ODBC 的 DATA_AT_EXEC 机制(请参阅有关 SQLSetPos 的 rgbValue 参数的 ODBC 文档)。 当框架准备 insert 或 update 语句时,不要指向包含数据的 HGLOBAL,而是将 CLongBinary 的 address 设置为列的值,将长度指示器设置为 SQL_DATA_AT_EXEC。 稍后,将 update 语句发送到数据源时,SQLExecDirect 将返回 SQL_NEED_DATA。 这会提醒框架,此列的参数值实际上是 CLongBinary 的地址。 框架使用小型缓冲区调用一次 SQLGetData,期待驱动程序返回数据的实际长度。 如果驱动程序返回大型二进制对象 (BLOB) 的实际长度,MFC 将重新分配尽可能多的空间来提取 BLOB。 如果数据源返回 SQL_NO_TOTAL,指示它无法确定 BLOB 的大小,那么 MFC 将创建较小的块。 默认初始大小为 64K,后续块的大小将是此大小的两倍;例如,第二个是 128K,第三个是 256K,以此类推。 初始大小可配置。

未绑定:使用 SQLGetData 直接从 ODBC 检索/发送数据

使用此方法可以完全绕过数据库类,并自行处理长数据列。

优点:

如有必要,可以将数据缓存到磁盘,或动态决定要检索的数据量。

缺点:

不会获得框架的 EditAddNew 支持,必须自行编写代码来执行基本功能(Delete 可以正常工作,因为它不是列级操作)。

在这种情况下,长数据列必须位于记录集的选择列表中,但不应被框架绑定。 执行此操作的一种方法是通过 GetDefaultSQL 或作为 lpszSQL 参数向 CRecordsetOpen 函数提供自己的 SQL 语句,而不将额外的列与 RFX_ 函数调用绑定。 ODBC 要求未绑定字段显示在绑定字段右侧,因此请将未绑定的一个列或多个列添加到选择列表的末尾。

注意

由于长数据列没有被框架绑定,因此不会通过 CRecordset::Update 调用来处理对它的更改。 必须自行创建和发送所需的 SQL INSERT 和 UPDATE 语句。

另请参阅

按编号列出的技术说明
按类别列出的技术说明