AX的事物管理通过关键字ttsbegin/ttscommit/ttsabort来处理。用一个事务,要不全做,要不全不做。
常用的写法有:
1 try 2 { 3 ttsbegin; 4 //do something1 5 //do something2 6 ttscommit; 7 } 8 catch 9 { 10 ttsabort; 11 }
这里do something1和do something2,要不全做,其中有一个出现问题,则两个都不做。
另外,使用tts,还有下面一些小细节:
(1)ttsabort不会马上终止程序,而是继续运行后面与transaction无关的statements (best practise建议使用throw exception而取代ttsabort)。
(2)嵌套tts,直到最外层的ttscommit运行后,数据才真正更新到数据库中。
(3)系统内置有一个叫ttslevel的变量,默认是0。每次ttsbegin,ttslevel就加1,每次ttscommit,ttslevel就减1,而ttsabort会直接把ttslevel清0。也就是0这个状态才代表整个transaction运行完事。当使用ax的form保存数据的时候,系统会首先检测ttslevel(比validateWrite更早,在form的task的super里头运行),如果ttslevel不为0,就会提示以下错误,导致更新终止。
(我们可以使用new xApplication().ttslevel()来查看当前的ttslevel)
看些例子:
例1
1 //Default at the beginning TTSLEVEL = 0 2 TTSBEGIN; //TTSLEVEL = 1 3 //Do something 4 TTSBEGIN; //TTSLEVEL = 2 5 //Do something 6 TTSCOMMIT; //TTSLEVEL = 1 7 //Do something 8 TTSCOMMIT; //TTSLEVEL = 0.
例2
1 //Default at the beginningTTSLEVEL = 0 2 TTSBEGIN; //TTSLEVEL = 1 3 //Do something 4 try 5 { 6 TTSBEGIN; //TTSLEVEL = 2 7 //Do something 8 TTSCOMMIT; //TTSLEVEL = 1 (没错误情况) 9 } 10 catch 11 { 12 TTSABORT; //TTSLEVEL = 0 (有错误情况) 13 //Do something 14 } 15 TTSCOMMIT; //TTSLEVEL = 0 (没错误情况), 16 //TTSLEVEL = -1 (有错误情况), 这个绝对不是我们想要得到的结果.所以结合第一点,我们应尽量避免使用ttsabort
(4)ttsbegin和ttscommit中间的程序,在最外层ttscommit之前是不会更新数据库的。但如果通过AX代码更新数据,在没有commit之前依然能获取transaction前半段插入的数据。
试试下面例子:
1 static void testTTS(Args _args) 2 { 3 VendTable vendTable, vendTable2; 4 ; 5 6 ttsbegin; 7 vendTable.clear(); 8 vendTable.AccountNum = 'TestingTTS'; 9 vendTable.doInsert(); 10 select vendTable2 11 where vendTable2.AccountNum == 'TestingTTS'; 12 13 print vendTable2.AccountNum; 14 //取得记录 15 16 vendTable2 = VendTable::find('TestingTTS'); 17 18 print vendTable2.AccountNum; 19 //取得记录 20 21 pause; 22 23 delete_from vendTable 24 where vendTable.AccountNum == 'TestingTTS'; 25 ttscommit; 26 }
所以我们有理由怀疑AX会合并内存和数据库中的内容,便于我们编程。但是如果通过外部程序直接查询数据库,那在commit之前是不能读出那些在transaction中途插入或修改的数据的(有个具体例子是,我们使用Table来保存一些打印数据,然后调用外部水晶报表程序来打印报表。该水晶报表直接读取AX数据库,所以他打印报表那句必须在commit之后调用,否则不能读出相应数据)
(5)ttsbegin/ttscommit/ttsabort针对写入数据库的事务管理。那么对于只更新内存的临时表的写入,它们是不起作用的。对于临时表,我们需要使用
临时表变量.ttsbegin();
临时表变量.ttscommit();
临时表变量.ttsabort();
(6)update_recordset,delete_from,insert_recordset那些语句是单条SQL命令,无需事务控制。
感谢秋毫的fat0527总结。