步骤清单 要解决冲突问题,我们应首先理解何谓冲突。 在向统一数据库中上载行时可能会发生冲突。如果两个用户修改不同远程数据库中相同的行,则当这两行中的第二个到达 MobiLink 同步服务器时将出现冲突。在冲突发生时,您应定义一个过程以计算正确值,或至少使用日志文件记录冲突。 在同步的下载阶段,远程数据库中不会出现冲突。如果下载的行中包含一个新的主键,则该行的值将插入到新的一行。如果该主键与一个现有行的主键相匹配,则会更新该行中的值。 冲突与错误是两个不同的概念。冲突处理是应用程序应该包含的一部分。 只有在更新行的过程中才会检测到冲突。尝试更新自上一次同步后已更新或删除的行将会导致冲突。缺省情况下: *如果在尝试插入一行时发现此行已经插入,则会导致错误。 *如果在尝试删除一行时发现此行已经删除,则此尝试删除的操作将被忽略。 这些是为方便操作而定义的内置行为。如果需要不同的行为,可以使用一个或多个上载事件自行实现。 我们在demo的前半部分展示了有冲突而未解决的情况。当两个不同的客户端分别将相同的数据进行不同更改后,再在不同时间上载,后上载的数据成了最终结果。这可能是我们不想要的情况。我们需要对冲突进行一定的检测、然后再根据我们的用户规则进行处理。 在 MobiLink 客户端向 MobiLink 同步服务器发送一个更新后的行时,发送的数据中不仅包含该行的新值(后映像),而且还将包含旧行值的副本(前映像)。如果前映像与统一数据库中的当前值不匹配,表示检测到了冲突。 检测冲突的方法有两种: *定义一个 upload_fetch 脚本。 *定义一个 upload_update 脚本,该脚本在 WHERE 子句中包含所有非主键列。 如果为同一个表定义这两个脚本,则只使用 upload_fetch 脚本。 仅当使用 upload_fetch 脚本或适当的 upload_update 脚本时,MobiLink 同步服务器才检测冲突。如果提供了 upload_fetch 脚本,MobiLink 同步服务器会将上载的前映像与 upload_fetch 脚本返回的包含相同主键值的行值进行比较。如果存在前映像中的值与当前统一数据库中的值不匹配的情况,则表示 MobiLink 同步服务器检测到了冲突。 我们这里用upload_fetch: CALL ml_add_table_script('remote1','upload_fetch','SELECT empno, empname, gender, deptno, last_modified FROM emp WHERE empno = ?') | 出现冲突时,可以使用临时表(或永久表)和 resolve_conflict 脚本加以解决。 例如,我们可以先建两个临时表,然后在upload_old_row_insert表事件中把统一数据库的旧值插入专门存放旧值的临时表,然后再 upload_new_row_insert表事件中把想要更新的值插入专门存放新值的临时表。接下来就可以在resolve_conflict表事件中解决冲突了。 当然,除了通过resolve_conflict事件解决冲突,我们也可利用upload_update、end_upload来解决冲突。在本部分的屏幕录像中,我们先展示了没有自定义冲突处理时候MobiLink对冲突的默认解决方案,即后者决定的策略。在此之后,我们又自定义了新的MobiLink冲突解决方案,让部门号较小的更新成为最终值。 本部分的脚本如下: 先建立两个目录:remote1 & remote2。 把数据库分别拷贝。 启动个数据库,中心叫center,其余两个一个叫remote1,一个叫remote2。 对3个库分别执行: ALTER TABLE emp ADD last_modified TIMESTAMP DEFAULT TIMESTAMP dbisql:remote1:CREATE PUBLICATION Lab_05(TABLE emp)CREATE SYNCHRONIZATION USER "remote1"CREATE SYNCHRONIZATION SUBSCRIPTION TO Lab_05FOR "remote1"TYPE 'TCPIP'ADDRESS 'host=localhost;port=2439'OPTION ScriptVersion='remote1' remote2:CREATE PUBLICATION Lab_05(TABLE emp)CREATE SYNCHRONIZATION USER "remote2"ALTER SYNCHRONIZATION SUBSCRIPTION TO Lab_05FOR "remote2"TYPE 'TCPIP'ADDRESS 'host=localhost;port=2439'OPTION ScriptVersion='remote1'center:CALL ml_add_table_script('remote1', 'emp', 'download_cursor', 'SELECT * FROM emp WHERE last_modified > ?'); CALL ml_add_table_script('remote1', 'emp', 'upload_update', 'UPDATE emp SET empname=?, gender=?, deptno=? WHERE empno=?');COMMIT | 命令行下: dbmlsrv9 -c "dsn=center" -v+ -x tcpip -dl -o mlserver.mls -zu+dbmlsync -c "dsn=remote1" -v -o dbmlsync1.out -e "sv=remote1"dbmlsync -c "dsn=remote2" -v -o dbmlsync2.out -e "sv=remote1" | 对remote2: UPDATE emp SET deptno = '0005' WHERE empno = 1COMMIT | 再次同步remote2 dbmlsync -c "dsn=remote2" -v -o dbmlsync2.out -e "sv=remote1" 对remote1 UPDATE emp SET deptno = '0002' WHERE empno = 1COMMIT | 同步remote1 dbmlsync -c "dsn=remote1" -v -o dbmlsync1.out -e "sv=remote1" | 对center: CREATE GLOBAL TEMPORARY TABLE emp_new(empno INTEGER NOT NULL PRIMARY KEY,empname VARCHAR(20),gender BIT,deptno CHAR(4),last_modified TIMESTAMP);CREATE GLOBAL TEMPORARY TABLE emp_old(empno INTEGER NOT NULL PRIMARY KEY,empname VARCHAR(20),gender BIT,deptno CHAR(4),last_modified TIMESTAMP)CALL ml_add_connection_script('remote1','end_upload','DROP TABLE emp_new;DROP TABLE emp_old');CALL ml_add_table_script('remote1','upload_fetch','SELECT empno, empname, gender, deptno, last_modified FROM emp WHERE empno = ?')CALL ml_add_table_script('remote1','emp','upload_old_row_insert','INSERT INTO emp_old VALUES(?,?,?,?)');CALL ml_add_table_script('remote1','emp','upload_new_row_insert','INSERT INTO emp_new VALUES(?,?,?,?)');CALL ml_add_table_script('remote1','emp','resolve_conflict','CALL ResolveConflictDemo()');COMMIT;ALTER OR REPLACE PROCEDURE ResolveConflictDemo()BEGINUPDATE emp eSET e.deptno = en.deptnoFROM emp_new enWHERE e.empno = en.empnoAND en.deptno < e.deptno;DELETE FROM emp_new;DELETE FROM emp_old;END; | |