8.1.1 创建删除数据库
将数据库卷挂载好之后,就可以在卷内新创建一个EDB数据库,调用函数CeCreateDatabaseWithProps()实现:
CEOID CeCreateDatabaseWithProps(
PCEGUID pGuid,
CEDBASEINFOEX* pInfo,
DWORD cProps,
CEPROPSPEC* prgProps
);
l 参数pGuid为挂载数据库的全局标识CEGUID
l 参数pInfo指定数据库的属性,为一个指向结构体CEDBASEINFOEX的指针。
l 参数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)。
n 成员 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_VALIDMODTIME或CEDB_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()获取出错信息,可取的错误值有:
n ERROR_INVALID_PARAMETER: 参数pGuid被设置为NULL。
n 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 |
参数poid或lpwszName指定的数据库不存在 |
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被设置为NULL或INVALID_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,表示系统在读取记录属性时,不会进行内存操作。
l 参数lpcPropID为输入输出参数,指定由参数prgPropID表示属性ID数组包含的元素个数,如果参数prgPropID被设置为NULL,那么这个参数将返回实际读取的属性个数。
l 参数prgPropID为一个指向包含属性ID数组的指针,指定需要读取的字段。如果这个参数被设置为NULL,将读回记录中的所有字段,而此时lpcPropID将返回字段的个数。
l 参数lplpBuffer指定用于接收读回的字段的缓冲区,注意这个参数不是指向缓冲区的指针,而是指向缓冲区指针的指针,这是因为系统可能重新分配缓冲区,函数将会修改指向缓冲区的指针。每次使用完缓冲区后,都必须释放。
l 参数lpcbBuffer为输入输出参数,表示参数lplpBuffer指定的缓冲区的字节数。函数返回时,这个参数将获取实际拷贝到缓冲区内的数据量。如果缓冲区太小无法容下记录数据,并且没有设置系统自动重新分配,这个参数可以用于计算获取数据的缓冲区大小。
l 参数hHeap为指向应用创建的堆句柄,用于系统重新分配内存。这个参数只在dwFlags被设置为CEDB_ALLOWREALLOC时才有意义。
如果读取记录数据成功,函数将返回记录的对象ID(OID);失败将返回0。调用GetLastError()函数获取错误码信息,可能的值为:
n ERROR_INSUFFICIENT_BUFFER:代表参数dwFlags没有包含CEDB_ALLOWREALLOC标识,而且参数lpcbBuffer指定的缓冲区大小不够大,无法接收字段数据。此时函数返回时,参数lpcbBuffer将包含用于读取字段数据的正确缓冲区大小。
n ERROR_INVALID_HANDLE:代表非法的数据库句柄,参数hDbase为NULL或INVALID_HANDLE_VALUE。
n ERROR_INVALID_PARAMETER:代表下面三个参数至少其中一个被设置为NULL: lpcPropID, lplpBuffer,lpcbBuffer。
n ERROR_KEY_DELETED:代表当前记录已经被删除
n ERROR_NO_DATA:代表记录不包含指定的请求字段。
n ERROR_NO_MORE_ITEMS:代表数据库指针当前不指向任何一个记录,需要首先定位记录
n ERROR_NOT_ENOUGH_MEMORY:代表内存分配失败。
n 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,可能的错误码为:
n ERROR_INVALID_HANDLE:参数hDatabase被设置为NULL或INVALID_HANDLE_VALUE。
n ERROR_INVALID_PARAMETER:参数oidRecord被设置为NULL或非法值
n ERROR_NOT_FOUND:参数oidRecord指定的记录不存在。
n 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类型。
l 参数dwMode指定流对象的访问权限,可取以下两种值:
n GENERIC_READ: 对流对象提供只读访问权限,不允许写入。
n 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指定移动的起始参考位置,可取值为:
n STREAM_SEEK_CUR: 以当前指针位置作为起始参考位置。这时,参数cbMove作为有符号数,正数表示向前(向末尾位置方向)移动,而负数表示向后(向开始位置方向)移动。
n STREAM_SEEK_END: 以流对象的末尾位置为起始参考位置,向前移动。
n STREAM_SEEK_SET: 以流对象的起始位置为起始参考位置,向后移动。
l 参数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种:
n CEDB_ISOLEVEL_DEFAULT或者CEDB_ISOLEVEL_READCOMMITTED:当前事务正在进行更改的数据不允许其它事务读取,直到事务提交完成。
n CEDB_ISOLEVEL_REPEATABLEREAD:当前事务正在进行更改的数据允许其它事务读取,但是不允许其它事务更改。
n 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,包括挂载及卸载数据库卷,枚举数据库卷和数据库,查询对象信息,回写数据库卷,创建删除数据库。创建会话,查找记录,读写数据库内的记录,使用流读写记录,以及怎么使用事务操作。