8.1.1 创建删除数据库

将数据库卷挂载好之后,就可以在卷内新创建一个EDB数据库,调用函数CeCreateDatabaseWithProps()实现:

CEOID CeCreateDatabaseWithProps(

  PCEGUID pGuid,

  CEDBASEINFOEX* pInfo,

  DWORD cProps,

  CEPROPSPEC* prgProps

);

l 参数pGuid为挂载数据库的全局标识CEGUID

l 参数pInfo指定数据库的属性,为一个指向结构体CEDBASEINFOEX的指针。

参数cProps指定参数prgProps数组的元素个数。

l 参数prgProps指定新建数据库的属性,为一个CEPROPSPEC结构体数组。在EDB中必须设置这些属性,才能向数据库内写入数据,结构体CEPROPSPEC的定义如下:

typedef struct CEPROPSPEC {

  WORD wVersion;

  CEPROPID propid;

  DWORD dwFlags;

  LPWSTR pwszPropName;

  DWORD cchPropName;

} CEPROPSPEC;

n 成员wVersion为结构体版本号,必须设置为1

n 成员propid为字段ID,表示数据库中的字段。

n 成员dwFlags为字段标志,可以取下面值之一或组合:DB_PROP_COMPRESSED(将字段压缩存储)和DB_PROP_NOTNULL(字段不能为空,也不能设置为NULL)

成员 pwszPropName为字段的名称,为可选。如果没有指定字段的名称,系统将产生一个内部名称。

n 成员cchPropName为可选的字段名称的长度,最大可为CEDB_MAXDBASENAMELEN (128).

如果创建EDB数据库成功,函数将返回数据库的对象标志(OID);创建失败将返回NULL。调用GetLastError()获取错误信息,可能的错误值有:

n E_FAIL: CEDBASEINFOEX结构体中指定数据库名称的szDbaseName成员的长度超过CEDB_MAXDBASENAMLEN。

n ERROR_ACCESS_DENIED: 试图创建一个包含2个可排序主键的数据库

n ERROR_ALREADY_EXISTS: 参数prgProps包含的某个字段ID或字段名字已经存在。

n ERROR_BAD_LENGTH: 某个字段名称的长度超过128

n ERROR_DISK_FULL: 磁盘空间不足,无法创建数据库。

n ERROR_DUP_NAME: 指定名字的数据库在卷中已经存在。

n ERROR_INVALID_PARAMETER: 某个参数不合法。

n ERROR_NOT_FOUND: 参数pGuid指定的卷不存在。

n ERROR_NOT_SUPPORT: CEDBASEINFOEX结构体中使用CEDB不支持的值,如CEDB_VALIDMODTIMECEDB_SYSTEMDB

用户还可以删除挂载的数据库卷内的指定数据库,通过函数CeDeleteDatabase()实现:

BOOL CeDeleteDatabase( 

  PCEGUID pGuid

  CEOID oid

);

l 参数pGuid指定将被从中删除数据库的挂载数据库卷的CEGUID

l 参数oid指定将被删除的数据库的对象ID,这个数据库在指定的数据库卷内必须存在。

删除数据库成功,函数返回TRUE;失败返回FALSE。注意用户不能删除一个正在打开的数据库。

8.1.2 创建会话

Windows CE系统中,只有EDB数据库支持事务(Transaction)处理。事务保证对数据库的一系列更新操作作为一个原子操作提交,要么所有更新都生效,要么全不生效,事务防止数据库由于奔溃或掉电导致的部分更新而进入不一致状态。为了支持数据库的事务功能,需要使用基于会话(Session)连接并打开EDB数据库。Win 32中的CeCreateSession()函数为EDB数据库创建一个会话对象:

HANDLE CeCreateSession(

  CEGUID* pGuid

);

l 参数pGuid为挂载的数据库卷的全局标识CEGUID,这个值不能设置为NULL

如果创建会话对象成功,函数返回新建会话对象的句柄;如果调用失败则返回INVALID_HANDLE_VALUE。调用函数GetLastError()获取出错信息,可取的错误值有:

ERROR_INVALID_PARAMETER参数pGuid被设置为NULL

ERROR_NOT_FOUND: 指定的卷没有被挂载或参数pGuid为一个非法的GUID

使用会话完毕,需要调用函数CloseHandle()关闭会话句柄,释放资源。

8.1.3 打开数据库

在对数据库进行查询或更新操作之前,需要先打开数据库。可以有两种打开数据库的方式:基于会话连接的支持事务功能的方式打开数据库,和不支持事务的方式打开数据库。

这里只介绍前者,在创建好会话对象后,函数CeOpenDatabaseInSession()使用基于会话连接的方式打开数据库:

HANDLE CeOpenDatabaseInSession(

  HANDLE hSession,

  PCEGUID pGuid,

  PCEOID poid,

  LPWSTR lpwszName,

  SORTORDERSPECEX* pSort,

  DWORD dwFlags,

  CENOTIFYREQUEST* pRequest

);

l 参数hSession指定使用的会话对象句柄。可以将这个参数设置为NULL,这时系统会自动新建一个会话,而且对打开数据库的任一更新操作都会被立即提交给数据库,保证每一个更新操作的原子性。如果不为NULL,那么创建该会话对象的数据库卷的CEGUID必须与参数pGuid指定的一致,否则函数调用将失败。

l 参数pGuid指定挂在数据库卷的CEGUID。如果这个参数设置为一个合法的GUID,那么将使用指定的卷;如果这个参数被设置为由CREATE_INVALIDEDBGUID宏返回的非法值,那么后面的poid参数将被忽略,系统将搜索所有的挂载数据库卷并使用第一个名字与参数lpwszName指定的匹配的数据库卷。

l 参数poid为输入输出参数,返回打开的数据库对象标识(OID)。既可以通过OID打开数据库,也可以通过名称打开。将poid设置为非0的数据库OID,将忽略参数lpwszName指定的数据库名。如果希望通过名称来打开数据库,就将这个参数设置为0,函数完成后,这个参数将返回指定数据库对象的OID

l 参数lpwszName指定将打开的数据库的名称。当参数poid被设置为0时,将使用这个名称和pGuid来打开数据库。

l 参数pSort指定数据库打开时使用的排序规则。如果这个参数被设置为NULL表示不使用任何排序规则。

l 参数dwFlags可以取值为0, CEDB_AUTOINCREMENT或EDB_USE_CEDB_WRITE_SEMANTICS。当取值为CEDB_AUTOINCREMENT时,表示当用户调用CeReadRecordPropsEx()读取一条记录时,数据库的当前指针会自动地往下移动到排序顺序的下一条记录位置。当取值为0时,表示读取一条记录时,数据库的当前指针不会自动往下移动,而需要用户手工移动到下一条记录位置。当取值为EDB_USE_CEDB_WRITE_SEMANTICS时,表示将已在挂载卷内存在的数据库打开并添加动态属性。

l 参数pRequest为指向数据结构CENOTIFYREQUEST的指针,用于指定当其他线程或进程对数据库进行修改时,如何发送通知消息到指定的窗口。如果将这个参数设置为NULL,表示应用不需要收到任何通知消息。结构体CENOTIFYREQUEST的定义如下:

typedef struct _CENOTIFYREQUEST {

  DWORD dwSize;

  HWND hwnd;

  DWORD dwFlags;

  HANDLE hHeap;

  DWORD dwParam;

} CENOTIFYREQUEST;

其中,成员dwSize指定结构CENOTIFYREQUEST的大小,成员hwnd指定用于接收通知消息的窗口句柄,成员dwFlags指定处理消息的方式,成员hHeap为一个堆句柄,用于为接收到消息分配空间,如果设置为NULL,系统将在进程的默认堆上分配内存空间。最后一个成员dwParam为用户自定义的返回参数。

如果打开数据库成功,将返回被打开的数据库的句柄。打开数据库出错将返回INVALID_HANDLE_VALUE,GetLastError()函数将返回出错信息,可能的值如表8-3所示。

返回错误码

说明

ERROR_FILE_NOT_FOUND

参数poidlpwszName指定的数据库不存在

ERROR_INVALID_HANDLE

参数hSession值为INVALID_HANDLE_VALUE.

ERROR_INVALID_PARAMETER

其它的参数内至少一个为非法值

ERROR_NOT_ENOUGH_MEMORY

内存空间不足,无法分配数据库句柄

ERROR_NOT_FOUND

参数pGuid指定的数据库卷不存在

8.1.4 获取数据库的会话

如果希望从打开的数据库句柄获取对应的会话信息,调用函数CeGetDatabaseSession()实现:

HANDLE CeGetDatabaseSession(

  HANDLE hDatabase

);

l 参数hDatabase为打开的数据库句柄,必须通过调用CeOpenDatabaseInSession()获取。

如果函数调用成功,返回会话句柄。失败则返回INVALID_HANDLE_VALUE。可能返回的错误码有:

l ERROR_INVALID_HANDLE:参数hDatabase被设置为NULLINVALID_HANDLE_VALUE。

l ERROR_INVALID_PARAMETER:参数hDatabase不为NULL,但是指向一个非法的数据库卷。

l ERROR_NOT_ENOUGH_MEMORY:内存空间不足。

8.1.5 查找记录

数据库打开成功之后,就可以对其进行读写访问。但是在访问之前,需要首先将数据库指针移动到指定的记录位置,就是首先需要定位到用户感兴趣的记录位置,函数CeSeekDatabaseEx()用于定位到一条记录:

CEOID CeSeekDatabaseEx(

  HANDLE hDatabase,

  DWORD dwSeekType,

  DWORD dwValue,

  WORD wNumVals,

  LPDWORD lpdwIndex

);

l 参数hDatabase为打开的数据库句柄。

l 参数dwSeekType指定定位记录的方式,可取值如表8-4所示。

8-4 可取的定位数据库记录的方式

定位方式

说明

CEDB_SEEK_BEGINNING

从数据库的开始位置依次往后查找,直到定位到指定的记录,参数dwValue指定从开始位置的记录下标

CEDB_SEEK_CEOID

根据记录的对象ID(OID)来定位记录,参数dwValue指定记录的OID,这种类型的定位操作非常高效

CEDB_SEEK_CURRENT

从数据库指针的当前位置开始向前或向后查找指定个数的记录,参数dwValue指定移动的个数,如果dwValue为正数,将向前移动查找;如果为负数,将向后移动查找。

CEDB_SEEK_END

从数据库的结束位置开始向后查找,参数dwValue指定移动的记录个数

CEDB_SEEK_PREFIX

查找数据时使用前缀,只能用于字符串和BLOB数据类型

CEDB_SEEK_VALUEFIRSTEQUAL

从数据库的开始位置向后查找直到第一个值与指定的值相等的记录,该值为参数dwValue指向的CEPROPVAL结构数组和参数wNumVals指定的数组大小。如果定位操作失败,数据库指针将留在数据库结束位置,函数将返回0

CEDB_SEEK_VALUEGREATER

从数据库开始位置向后查找直到找到第一个“大于”指定值的记录。注意不管排序方式总是向后查找。对于升序,这个将找到大于指定值的最小的记录。而对于降序,将找到小于指定值的最大的记录。如果没有满足条件的记录,数据库指针将留在数据库结束位置,函数返回0

CEDB_SEEK_VALUEGREATEROREQUAL

从数据库的开始位置向后查找,直到第一个“大于等于”指定值的记录

CEDB_SEEK_VALUENEXTEQUAL

从数据库指针的当前位置开始,按照排列次序指向下查找一个位置,并判断这下一个记录的值是否与指定值相等。如果想等,返回下一个记录的对象ID;否则将返回0,数据库指针留在数据库末尾。

CEDB_SEEK_VALUESMALLER

CEDB_SEEK_VALUEGREATER相对,从数据库末尾位置开始总是向前查找,知道第一个“小于”指定值的记录。对于升序,找到小于指定值的最大的记录;对于降序,找到大于指定值的最小记录。如果满足条件的记录找不到,数据库指针将留在数据库的末尾位置,函数返回0

CEDB_SEEK_VALUESMALLEROREQUAL

从数据库末尾位置开始向前查找,直到第一个“小于等于”指定值的记录

l 参数dwValue根据不同的定位方式,可能指定查找的偏移量,也可能为指向要搜索的属性值数组的指针,属性值由结构体CEPROPVAL表示,定义如下:

typedef struct _CEPROPVAL {

  CEPROPID propid;

  WORD wLenData;

  WORD wFlags;

  CEVALUNION val;

} CEPROPVAL, * PCEPROPVAL;

成员propid代表属性的标识,高字(word)部分为应用自定义的标识,低字部分为预定义的常量值,代表成员val的数据类型,低字部分的值可取为表10-4所示。成员wLenData被保留,必须设置为0。成员wFlags指定字段的属性,在CeSeekDatabaseEx()函数中可以忽略。成员val为字段的值,是数据库支持的不同数据类型组成的一个联合体。

10-4 属性的数据类型

说明

CEVT_BLOB

CEBLOB结构

CEVT_BOOL

Boolean

CEVT_FILETIME

FILETIME结构

CEVT_I2

16位带符号整数

CEVT_I4

32位带符号整数

CEVT_LPWSTR

为以null结束的字符串

CEVT_R8

64位带符号整数

CEVT_UI2

16位无符号整数

CEVT_UI4

32位无符号整数

l 参数wNumVals表示在参数dwValue里的CEPROPVAL结构体数组大小。

l 参数lpdwIndex为输出参数,用于返回记录在数据库中的索引,如果不需要获取记录的索引值,将这个参数设置为NULL

定位记录成功,函数将返回记录的对象ID;失败则返回0

下面的代码演示如何在数据库中查找宽度属性小于等于80的第一条记录:

CEOID oid;

DWORD dwIndex;

CEPROPVAL property;

//首先将数据库指针移动到起始位置

Oid = CeSeekDatabaseEx(hDB, CEDB_SEEK_BEGINNING, 0, 1, &dwIndex);

//设置定位条件

Property.propid = PID_WIDTH;

Property.wLenData = 0;

Property.wFlags = 0;

Property.val.lVal = 80;

Oid = CeSeekDatabaseEx(hDB, CEDB_SEEK_VALUESMALLEROREQUAL,

  (DWORD)&property, 1, &dwIndex);

If(oid == 0)

{

//没有找到满足条件的记录

}

Else{

//找到记录,参数dwIndex返回该记录在数据库中的索引

}

8.1.6 读取记录数据

在定位到指定的记录后,就可以对记录进行读取或写入操作了。一条记录包含多个属性(在数据库中为列),函数CeReadRecordPropsEx()读取当前记录的属性值:

CEOID CeReadRecordPropsEx(
   HANDLE hDbase,
   DWORD dwFlags,
   LPWORD lpcPropID,
   CEPROPID* prgPropID,
   LPBYTE* lplpBuffer,
   LPDWORD lpcbBuffer,
   HANDLE hHeap
    );

l 参数hDbase为打开的数据库句柄。

l 参数dwFlags为读取标识,如果设置为CEDB_ALLOWREALLOC,系统将调用LocalAlloc()函数为参数ppbBuffer指定缓冲分配内存空间。如果缓冲空间不足,服务器将为缓冲重新分配空间。如果设置为0,表示系统在读取记录属性时,不会进行内存操作。

参数lpcPropID为输入输出参数,指定由参数prgPropID表示属性ID数组包含的元素个数,如果参数prgPropID被设置为NULL,那么这个参数将返回实际读取的属性个数。

参数prgPropID为一个指向包含属性ID数组的指针,指定需要读取的字段。如果这个参数被设置为NULL,将读回记录中的所有字段,而此时lpcPropID将返回字段的个数。

参数lplpBuffer指定用于接收读回的字段的缓冲区,注意这个参数不是指向缓冲区的指针,而是指向缓冲区指针的指针,这是因为系统可能重新分配缓冲区,函数将会修改指向缓冲区的指针。每次使用完缓冲区后,都必须释放。

l 参数lpcbBuffer为输入输出参数,表示参数lplpBuffer指定的缓冲区的字节数。函数返回时,这个参数将获取实际拷贝到缓冲区内的数据量。如果缓冲区太小无法容下记录数据,并且没有设置系统自动重新分配,这个参数可以用于计算获取数据的缓冲区大小。

l 参数hHeap为指向应用创建的堆句柄,用于系统重新分配内存。这个参数只在dwFlags被设置为CEDB_ALLOWREALLOC时才有意义。

如果读取记录数据成功,函数将返回记录的对象ID(OID);失败将返回0。调用GetLastError()函数获取错误码信息,可能的值为:

ERROR_INSUFFICIENT_BUFFER:代表参数dwFlags没有包含CEDB_ALLOWREALLOC标识,而且参数lpcbBuffer指定的缓冲区大小不够大,无法接收字段数据。此时函数返回时,参数lpcbBuffer将包含用于读取字段数据的正确缓冲区大小。

ERROR_INVALID_HANDLE:代表非法的数据库句柄,参数hDbase为NULLINVALID_HANDLE_VALUE

ERROR_INVALID_PARAMETER:代表下面三个参数至少其中一个被设置为NULL: lpcPropID, lplpBuffer,lpcbBuffer。

ERROR_KEY_DELETED:代表当前记录已经被删除

ERROR_NO_DATA:代表记录不包含指定的请求字段。

ERROR_NO_MORE_ITEMS:代表数据库指针当前不指向任何一个记录,需要首先定位记录

ERROR_NOT_ENOUGH_MEMORY:代表内存分配失败。

ERROR_SHARING_VIOLATION:代表当前记录被其它进程锁住,无法读取。

如果数据库在打开时设置了CEDB_AUTOINCREMENT,那么这个函数在读取记录数据成功后,数据库指针将自动往下移动到下一个记录位置。

8.1.7 写记录

出了读取记录的数据外,用户还可以对记录值进行更新或者向数据库内插入一条新的记录,函数CeWriteRecordProps()实现写记录:

CEOID CeWriteRecordProps(

  HANDLE hDatabase,

  CEOID oidRecord,

  WORD cPropID,

  CEPROPVAL* prgPropVal

);

l 参数hDatabase指定打开的数据库句柄。

l 参数oidRecord指定将被更新的记录的对象ID,这个参数可以设置为0,这时将在数据库中新建一个记录,并使用指定的字段值填充新建的记录。

l 参数cPropID指定字段数组的字段个数,参数prgPropVal指定字段数组。

l 参数prgPropVal指向包含将写入到记录中的字段值的数组,每个字段使用CEPROPVAL结构表示。

如果写入记录成功,函数返回被写入记录的对象ID;失败将返回0

注意写入记录将被系统缓存直到将数据库回写,这个函数将数据库指针留在被写入的记录位置。

8.1.8 删除记录

删除数据库中的一条记录使用函数CeDeleteRecord()实现:

BOOL CeDeleteRecord(

  HANDLE hDatabase,

  CEOID oidRecord

);

l 参数hDatabase为打开的数据库句柄。

l 参数oidRecord为将被删除的记录的OID,这个参数不能被设置为NULL

删除记录成功,函数返回TRUE;失败将返回FALSE,可能的错误码为:

ERROR_INVALID_HANDLE:参数hDatabase被设置为NULLINVALID_HANDLE_VALUE

ERROR_INVALID_PARAMETER:参数oidRecord被设置为NULL或非法值

ERROR_NOT_FOUND:参数oidRecord指定的记录不存在。

ERROR_SHARING_VIOLATION:表示记录被其它进程锁定,发送冲突。

如果数据库在打开时没有设置CEDB_AUTOINCREMENT标志,而且删除的记录为当前数据库指针指向的记录时,那么下一次读取记录的操作将失败(需要重新定位记录)。如果设置了CEDB_AUTOINCREMENT标志,系统将自动往后移动数据库指针到下一个记录位置。

8.1.9 使用流读写记录

前面介绍的直接读写数据库记录,为用户提供比较直观的访问接口,但是这种方式只对小数据量的访问比较合适,如果需要访问大量的数据,就会比较低效。在EDB中还支持对记录的流读写,这对于大数据量的记录读写是很有用。首先将整个记录的数据都放到一个连续的数据流中,而不是以一个一个数据块的方式读写。在使用流读写记录之前需要先打开流,函数CeOpenStream()为一个记录打开流对象:

HANDLE CeOpenStream(

    HANDLE hDatabase,

    CEPROPID propid,

    DWORD dwMode

);

l 参数hDatabase为打开的数据库句柄。

l 参数propid指定将被以流对象打开的属性的属性ID,属性必须为CEVT_STREAM类型。

参数dwMode指定流对象的访问权限,可取以下两种值:

GENERIC_READ对流对象提供只读访问权限,不允许写入。

GENERIC_WRITE对流对象提供读写访问权限。

打开流对象成功,函数将返回流对象的句柄;失败则返回INVALID_HANDLE_VALUE

打开流对象后,就可以对流对象进行读写操作,函数CeStreamRead()用于从流对象中读取数据:

BOOL CeStreamRead(

  HANDLE hStream,

  LPBYTE lprgbBuffer,

  DWORD cbRead,

  LPDWORD lpcbRead

);

l 参数hStream为打开的流对象。

l 参数lprgbBuffer指定用于接收从流内读取数据的缓冲区。

l 参数cbRead指定需要从流对象读取的数据量,单位为字节。

l 参数lpcbRead为输出参数,返回本次读取操作实际从流对象中读取的数据量。

从流对象中读取数据成功,函数返回TRUE,失败则返回FALSE

如果打开流对象时指定的读写权限,就可以向流对象内写入数据,函数CeStreamWrite()用于从流的当前位置开始向流对象内写入指定大小的数据:

BOOL CeStreamWrite(

  HANDLE hStream,

  LPBYTE lprgbBuffer,

  DWORD cbWrite,

  LPDWORD lpcbWritten

);

l 参数hStream为打开的流对象句柄。

l 参数lprgbBuffer为包含将要写入到流对象中数据的缓冲区。

l 参数cbWrite指定将要写入到流对象中的数据量,单位为字节。

l 参数lpcbWritten为输出参数,返回实际写入到流对象中的数据量。

向流对象中写入数据成功,函数返回TRUE;失败则返回FALSE

实际上流对象内部也维护一个当前指针,而CeStreamRead()CeStreamWrite()函数总是从当前指针指向的位置开始读取或写入数据。如果用户需要从其它的位置开始读取写入数据,可以调用CeStreamSeek()函数来显式设置当前指针的位置:

BOOL CeStreamSeek(

  HANDLE hStream,

  DWORD cbMove,

  DWORD dwOrigin,

  LPDWORD lpcbNewOffset

);

l 参数hStream为打开的流对象句柄。

l 参数cbMove指定相对于参数dwOrigin指定的起始参考位置移动的字节数。

l 参数dwOrigin指定移动的起始参考位置,可取值为:

STREAM_SEEK_CUR以当前指针位置作为起始参考位置。这时,参数cbMove作为有符号数,正数表示向前(向末尾位置方向)移动,而负数表示向后(向开始位置方向)移动。

STREAM_SEEK_END以流对象的末尾位置为起始参考位置,向前移动。

STREAM_SEEK_SET以流对象的起始位置为起始参考位置,向后移动。

参数lpcbNewOffset为输出参数,如果移动指针位置成功,返回新的指针位置相对于流对象起始位置的偏移量。如果不需要,可以将这个参数设置为NULL

移动流对象指针位置成功,函数返回TRUE;失败返回FALSE。需要注意是,这个函数不负责检测移动的偏移量是否合法,即使用户指定的偏移量超出流对象的范围,这个函数还是会返回TRUE。比如一个流对象大小为80字节,但是指定将指针移动到相对开始位置的160字节,函数调用还是会成功返回,但是后续的操作将失败。利用这个函数还可以获取流对象的大小信息,具体方式是,将参数dwOrigin设置为STREAM_SEEK_END,参数cbMove设置为0,这样函数返回时,参数lpcbNewOffset将包含流的大小。

Windows CE还允许用于动态地改变流对象的大小,函数CeStreamSetSize()用于重新设置流对象的大小:

BOOL CeStreamSetSize(

  HANDLE hStream,

  DWORD cbSize

);

l 参数hStream为打开的流对象句柄。

l 参数cbSize指定流对象新的大小。

改变流对象大小成功,函数返回TRUE;失败返回FALSE。注意只能对读写访问权限的流对象重新设置大小,就是说打开流对象时需要指定为GENERIC_WRITE,否则函数调用将失败。

对流对象进行写入操作,实际上只是将数据写入到内存中,并没有写回到数据库中。为了将数据写回到数据库中,需要调用函数CeStreamSaveChanges()来实现:

BOOL CeStreamSaveChanges(

  HANDLE hStream

);

l 参数hStream为打开的流对象句柄。

将流对象写回到数据库成功,函数返回TRUE;否则返回FALSE。注意一旦将流对象写回到数据库,流对象将自动变为只读模式,不再允许对流对象进行写入操作。

前面介绍的操作流对象的函数都是通过流对象句柄来完成,一旦使用流句柄完毕,需要将其关闭,函数CloseHandle()关闭流并释放流对象。

8.1.10 事务操作

事务是数据库提供的基本功能。事务时作为单个逻辑工作单元执行的一系列操作,事务必须具有四个属性:原子性、一致性、隔离性和持久性:

l 原子性:事务必须是原子工作单元,对数据的一系列修改,要么全部执行,要么全部都不执行,这也是我们最经常提到的特性。

l 一致性:事务完成时,必须使所有的数据都保持一致状态。

l 隔离性:由并发事务所作的修改必须与任何并发事务所作的修改隔离。

l 持久性:事务完成(提交)后,对系统的影响是永久性的。

Windows CE中,EDB数据库引擎提供事务支持,使用事务必须首先获取会话句柄,使用事务的一般步骤是,启动事务,对数据库执行一系列读写操作,最后提交事务。启动事务通过函数CeBeginTransaction()实现:

BOOL CeBeginTransaction(

  HANDLE hSession,

  CEDBISOLATIONLEVEL isoLevel

);

l 参数hSession为会话句柄。

l 参数isoLevel指定事务操作的隔离级别,EDB数据库引擎支持以下3种:

CEDB_ISOLEVEL_DEFAULT或者CEDB_ISOLEVEL_READCOMMITTED:当前事务正在进行更改的数据不允许其它事务读取,直到事务提交完成。

CEDB_ISOLEVEL_REPEATABLEREAD:当前事务正在进行更改的数据允许其它事务读取,但是不允许其它事务更改。

CEDB_ISOLEVEL_SERIALIZABLE:当前事务正在访问(包括读取和写入)的数据,其它事务不能并发访问(读写都不允许),只能串行化访问。

启动事务成功,函数返回TRUE;否则失败返回FALSE。注意EDB不允许创建嵌套事务,如果一个会话已经在一个(未提交)的事务中,使用这个会话再启动事务将导致失败。

结束一个事务使用函数CeEndTransaction(),这里可以指定是提交所有的操作还是进行回滚:

BOOL CeEndTransaction(

  HANDLE hSession,

  BOOL fCommit

);

l 参数hSession为会话句柄。

l 参数fCommit指定是否将事务操作提交到数据库中。如果设置为TRUE,表示提交事务;设置为FALSE表示回滚,事务中的操作全部取消。注意即使将事务提交到数据库中,还是需要等到调用CeFlushDBVol()回写数据库卷才能写回到磁盘中。

提交事务成功,函数返回TRUE;失败则返回FALSE。如果指定的会话句柄没有在事务(调用CeBeginTransaction)中,函数调用将失败。

8.1 总结

本章介绍了Windows CE7中数据库的基本概念,尤其针对EDB数据库,介绍EDB数据库支持的类型,限制。然后介绍EDB数据库开发的API,包括挂载及卸载数据库卷,枚举数据库卷和数据库,查询对象信息,回写数据库卷,创建删除数据库。创建会话,查找记录,读写数据库内的记录,使用流读写记录,以及怎么使用事务操作。