大虾帮忙:用ADO进行事务处理出现的问题(50分)

  • 主题发起人 主题发起人 devecom
  • 开始时间 开始时间
D

devecom

Unregistered / Unconfirmed
GUEST, unregistred user!
我在用ADO进行事务处理时出现过这样的问题:
用ADOConnection连接数据库,再用ADOQuery进行处理,方法为:
ADOQuery1.Sql.add('begin transaction');
//SQL statement
ADOQuery1.Execsql;
ADOQuery1.Sql.add('commit transaction');
问题为:假如对某个表进行插入操作,但有关键字重复,它不会插入进去,这很正常,
但并不报错,并且如果还有别的SQL语句的话它照样会执行,并不回滚(这可能没错,因)
为我没有这条语句,但为什么不出错,把语句拷到SQL Explorer 中执行就会出现重复关键字
错误,但其它的SQL照样执行成功.我试过ADOConnection1.beginTrans ,但同样没用.
请高手帮帮我!怎样捕捉到事务中SQL语句的错误.Try..Except..end 没用,因为它根本就不
报错.
 
你为什么不用ado自己的事务管理,而要用sql
 
我试过ADO本身的ADOConnection1.BeginTrans和ADOConnection1.CommiTrans
但也没用,关键是怎样捕捉SQL执行中的错误!
 
我用ADO的事务处理很好,回滚很正常啊!
我试过对多张表在一个事务中分别进行插入、删除和更新,如果一个出错,
所有事务都会回滚的。
用connection.BeginTran;
try
SQL过程

Connection.CommitTran;
except
Connection.RollBackTran;
end;
我想可能是你的SQL过程是有Commit之类的语句,使事务在你要求之前就提交了。
老兄能把你的SQL过程贴出来瞧瞧吗?
 
你应该在ADODATASET 的BEFOREPOST事件中通过BEGINTRANS启动事务管理,然后在
ONPOSTERROR事件中通过ROLLBACKTRANS回滚。捕捉SQL的数据提交错误并据此做出处理
应该在ADODATASET的ONPOSTERROR事件中进行。
 
zzh0918 和杜宝两位的方法我都试过,可还是不行,我的SQL语句是这样的:
(ADOC_Fpb是一个ADOConnection,QueryMain是一个ADOQuey)
ADOC_Fpb.beginTrans;
Try
QueryMain.Close;
QueryMain.Sql.Clear;
// QueryMain.Sql.add('begin transaction');
QueryMain.Sql.add('Update 就业方案表'+Mainform.MpYear);
QueryMain.Sql.add('Set 批打次数=批打次数+1,');
QueryMain.Sql.add('打印次数=打印次数+1,');
QueryMain.Sql.add('报到证号='+#39+MBdzh+#39+',');
QueryMain.Sql.add('派遣时间='+#39+MSDate+#39+',');
QueryMain.Sql.add('派遣期限='+#39+MEDate+#39);
QueryMain.Sql.add('Where 报到证序列号='+#39+Mxlh+#39);
//主要是两条SQL语句,上面一条为Update,下面一条为Insert,两条必须同时执行成功
//Mxlh为关键字报到证序列号,如果有重复的话上面的Update应该回滚0
QueryMain.Sql.Add('Insert Into 报到证使用表');
QueryMain.Sql.Add('Values(');
QueryMain.Sql.Add(#39+MBdzh+#39+','+#39+MSDate+#39+','); //报到证号
QueryMain.Sql.Add(#39+'打印报到证'+#39+',');
QueryMain.Sql.Add(#39+MainForm.MUserName+#39+',');
QueryMain.Sql.Add(#39+Mxlh+#39+',');
MTempStr:=Trim(QueryList.Fieldbyname('姓名').AsString);
QueryMain.Sql.Add(#39+MTempStr+#39+',');
QueryMain.Sql.Add(#39+MSchool+#39+','+#39+#39+',');
QueryMain.Sql.Add(#39+MainForm.MPYear+#39);
QueryMain.Sql.Add(')');
// QueryMain.Sql.add('commit transaction');
QueryMain.Execsql;
ADOC_Fpb.committrans;;
except
ADOC_Fpb.RollBackTrans;
showmessage('错误,请检查是否该报到证已经使用了,或其他问题!'+#13+'该学生可能需要重新打印');
Exit;
end;

大虾请帮帮小弟,很急!
 
zzh0918 和杜宝的方法应该是没有问题的,看了半天也看不出你的代码哪里有问题,继续关
注。
 
关键在于那条Insert语句如果有关键字重复在Delphi中并不报错,相当于Delphi并没有
捕捉到这个错误,在SQL Explorer中会出错觉(关键字重复)
 
老大:这不就是问题吗?
你在开始的时候ADOConnection.BeginTrans;
ADO将向数据库服务器提交一条 :begin transaction
表示其后所有的操作将作为一个事务处理;
然后你的脚本自己向服务器提交了一条Begin Transaction,
最后自己还Commit Transaction了
在服务器上形成了:
Begin Transaction //ADO加
Begin Transaction //你自己加的
Update ....
Insert ....
Commit Transcation//你自己加的
Commit Transaction//ADO加
的事务嵌套。
Insert的错误在你自己的事务中,又没有作出错的回滚,你的事务就结束了
而对于ADO来说,自己事务嵌套中,只有一个你的事务的操作,而这
个操作是正常结束的(提示当然是正常结束了!),所以没有或得出错信息。
建议你取消脚本中的Begin Transaction ,Commit Transaction再试试。
 
里面的begin transaction 和 commit Transaction 我已经注销了
 
把语句分开写,类是下面的语句:
ADOConnection1.BeginTrans;
try
// ADOQuery1.ExecSQL;
ADOQuery1.Close;
ADOQuery1.SQL.Clear;
ADOQuery1.Sql.Append ('INSERT INTO BBB VALUES('+#39+'BBB'+#39+')');
ADOQuery1.ExecSQL;
ADOQuery1.Close;
ADOQuery1.SQL.Clear;
ADOQuery1.Sql.Append ('INSERT INTO AAA VALUES('+#39+'AAA'+#39+')');
ADOQuery1.ExecSQL;
ADOQuery1.Close;
ADOConnection1.CommitTrans;
except
ShowMessage('AAAA');
ADOConnection1.RollbackTrans;
end;
这样就能产生例外处理了,关键是 ADOQuery 一次仅仅提交一个修改语句,如果
修改成
ADOQuery1.Close;
ADOQuery1.SQL.Clear;
ADOQuery1.Sql.Append ('INSERT INTO BBB VALUES('+#39+'BBB'+#39+')');
ADOQuery1.Sql.Append ('INSERT INTO AAA VALUES('+#39+'AAA'+#39+')');
ADOQuery1.ExecSQL;
这样当AAA表关键字重复的时候,BBB不能得到正确的Roll。我想你的错误就在这里。
Good Luck.
 
lanny是正确的,问题已解决!
 
to devecom:
实际上在ado 中数据的提交是不需要自己用sql的insert 来进行提交的,你只需要用
adoquery1.insert;来插入一条空纪录,填入数据后用adoquery1.post或adoquery1.update
来提交。在adoquery1beforepost事件中用adoconnection1.begintrans来启动事务管理。
在adoquery1afterpost事件中用adoconnection1.committrans来确定事务管理成功。
在adoquery1posterror事件中用以下语句
adoconnection1.rollbacktrans;
action:=daabort;来取消更新数据的动作。在执行时你用ms profiler观察。以上过程和你
在程序中用sql写的启动事务管理、插入语句的执行过程是一模一样。
有这样简单的办法为什么还要用你那么复杂的办法?你为什么不用ADO 原型而要用DELPHI
封装的ADO?
 
To zzh0918
按你的方法,不绝的 ADOQuery 在重复使用上会存在问题吗?
使用原生的 ADO 效率并不能提高多少,但会麻烦很多。
不过用原生 ADO 捕获错误是个好的方法。
 
TO QUESTION :
DELPHI 中封装的本身就是原生ADO,我的意思是既然ADOCONNECT中本身就提供了和你自己
用SQL 同样的功能的事务管理和错误捕捉为还要自己用SQL语句来启动事务管理和提交数据
 
大虾们,小弟终于找到了解决这个问题的方法了,实际上lanny的方法也不行,应该这样:
1.SQL Server 有一个事务的设置参数: set XACT_Abort,默认为off,为off时如果发了一串
SQL语句,它只回滚出错的那条,其它的不回滚,所以要在程序中加一条 set XACT_Abort on
2.但按照上面的方法Delphi并捕捉不到那个错误,所以更好的一个方法是(想起来没道理)执行
SQL语句时不用Execsql,而用Open,但Delphi的Open时一定要有一条Select 语句,否则Delphi
会报错。所以Open 之后加一条: select @@rowCount之后就没事了,并且Delphi还可能捕捉
到错误,这时的 set XACT_Abort on就可有可无了。
两种办法,不知各位大虾有何看法?
 
set XACT_Abort 的确是个好的参数,可以在程序启动的时候发送。
又长知识了,你可以使用ADOCommand 构件执行你没有返回值的语句,这样效率非常高。
不需要用Open,如果以后构件的模式修改了,你的程序可能就要完蛋。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
887
DelphiTeacher的专栏
D
后退
顶部