我用VB的ADO设计的系统遇到了下面的问题:
假设我的MDI主窗口下有form1,form2,form3三个子窗口
在主程序main中我定义了公共变量cn用来打开数据库,在程序开始时就打开了数据库
现在的问题是:
假设form1,form2,form3窗口都开启,并都打开了各自的表tb1,tb2,tb3
这时候,如果我在form1中使用了cn的事务处理的处理数据,则在事务处理完成后,如果我再回到form2,form3对tb2,tb3表进行操作,就会出错,说表处理超自然状态,要使用requery方法重新调用数据才行.
我想到的解决办法和想法
1、使用on error 方法来判断处理,出错时,如果错误原因是因为使用了事务,则对表使用requery方法重新调用。2、定义一个共公的数据变量 dim obj() as object,将目前所有的已经打开的表都放入这个数组中(比如,打开tb1,则set obj(n)=tb1),再写一个过程,对obj()数组中的所有对象进行requery,并在进行过事务处理程序的结束处调用这个过程。3、在每个窗口中写一个接收消息的处理函数,一旦数据库有进行过事务处理,就发消息到各窗口,通知各窗口做相应的处理
请问
1、还有什么办法可以避免这个问题?
2、我如何判断数据库已经开始了一个事务处理,还没有结束?
3、我目前使用第1种办法来处理。
4、第2种办法是否需要很大的系统开销,特别是打开的表很多的时候(系统开销是否为2 X 打开的表数目)
5、第3种办法请高手给出例子,因为目前我还没有这方面的知识。
假设我的MDI主窗口下有form1,form2,form3三个子窗口
在主程序main中我定义了公共变量cn用来打开数据库,在程序开始时就打开了数据库
现在的问题是:
假设form1,form2,form3窗口都开启,并都打开了各自的表tb1,tb2,tb3
这时候,如果我在form1中使用了cn的事务处理的处理数据,则在事务处理完成后,如果我再回到form2,form3对tb2,tb3表进行操作,就会出错,说表处理超自然状态,要使用requery方法重新调用数据才行.
我想到的解决办法和想法
1、使用on error 方法来判断处理,出错时,如果错误原因是因为使用了事务,则对表使用requery方法重新调用。2、定义一个共公的数据变量 dim obj() as object,将目前所有的已经打开的表都放入这个数组中(比如,打开tb1,则set obj(n)=tb1),再写一个过程,对obj()数组中的所有对象进行requery,并在进行过事务处理程序的结束处调用这个过程。3、在每个窗口中写一个接收消息的处理函数,一旦数据库有进行过事务处理,就发消息到各窗口,通知各窗口做相应的处理
请问
1、还有什么办法可以避免这个问题?
2、我如何判断数据库已经开始了一个事务处理,还没有结束?
3、我目前使用第1种办法来处理。
4、第2种办法是否需要很大的系统开销,特别是打开的表很多的时候(系统开销是否为2 X 打开的表数目)
5、第3种办法请高手给出例子,因为目前我还没有这方面的知识。
如果是这种情况,现在通用的做法是对表显示相应的数据,一般都是一次抽取,表和数据库不绑定,
如果发生操作,或者想显示最新数据,则在相应的事件中重新从数据库读取数据,在表中显示
这样的话,不应该出现你说的情况表和数据库绑定虽然代码简单,但现在一般都不推荐这么做
ADO的事务模型还是基于COM的MTS?
把你的代码贴出来看看
Sub test()
Dim Db As New ADODB.Connection
Dim R1 As New ADODB.Recordset
Dim R2 As New ADODB.Recordset
'on error goto lbErr '#b-1
Db.Open "Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=xz"
Db.Execute "create table t1(a varchar(10),b varchar(20))"
Db.Execute "create table t2(a varchar(10),b varchar(20))"
R1.Open "t1", Db, adOpenKeyset, adLockOptimistic
R2.Open "t2", Db, adOpenKeyset, adLockOptimistic '#b-2
Db.BeginTrans
R1.AddNew
R1("a") = "a"
R1("b") = "b" '#b-3
R1.Update
Db.CommitTrans
Db.BeginTrans
R2.AddNew '#a-1 在这里时出错:"ITransaction::Commit 或 ITransaction::Abort 被调用,并且对象处于 zombie 状态。"
R2("a") = "a"
R2("b") = "b"
R2.Update
Db.CommitTrans
R1.Close
R2.Close
Db.Execute "drop table t1"
Db.Execute "drop table t2"
Db.Close
'lbErr: '#b-4
End Sub程序运行到标记'#a-1'的地方出错
当然,如果在这一句之前执行一句:"R2.Requery"就不会出错了但因为我的程序是多窗口处理的,不同的窗口可能都打开了各自的数据表,如何在执行了数据库的事务处理后,能够发一个消息通知所有其它数据处理程序进行刷新呢?
标记"#b-1,#b-2,#b-3,#b-4'是想说明另一个问题,就是如果使"#b-1,#b-4"的语句生效的话,那么错误处理的程序如何知道数据库是否执行了事务?
换句话说:在"#b-2"处出错时,错误处理程序不需要回滚事务
而在"#b-3"出错时,错误处理程序需要回滚事务
我如何在错误处理程序中判断错误是出在已经开始了事务,还是没有开始事务呢?
事务是肯定处理完成了的!因为我在实际程序中,是一个窗口用事务处理了数据后,关闭窗口,回到另一个窗口中,再对表进行操作时出的错.标志的问题没有直接的办法吗?TO:Jhzyz(Frank Feng黑名单上的人)
你的方法是行不能的,我的程序是多窗口处理,就是打开这个窗口后,可以在不关闭此窗口的情况下打开另一个窗口进行数据处理,而在另一个窗口如果进行了事务处理,即使关闭了窗口,回到原窗口再读取表中的数据都会出错.
一、使用类事件
1.用WithEvent申明Connection。
2.创建该Connection对象。
3.分别处理该对象的CommitTransComplete、RollbackTransComplete事件。
例如:
Dim WithEvents connEvent as Connection
Dim conn as New ConnectionPrivate Sub MySub()
set connEvent = conn ' Enable event support.
conn.Open(...)
...
set connEvent = Nothing ' Disable event support.
...
End SubPrivate Sub connEvent_ConnectComplete(ByVal err as ADODB.Error, & _
adStatus as ADODB.EventStatus, ByVal pConnection as ADODB.Connection)
' Check the error object only if adStatus
' equals adStatusErrorsOccurred.
...
End Sub
Attributes 属性设置为 dXactCommitRetaining,在调用 CommitTrans 后提供者将自动启动新事务。如果 Attributes 属性设置为adXactAbortRetaining,在调用 RollbackTrans 后提供者将自动启动新事务。
Db.CursorLocation = adUseClient
应该可以了!!!!!!!!!!
试过了,同样错误
不好意思,又试过,在表打开之前,就要设置
Db.CursorLocation = adUseClient
否则同样错误.我想再请问一下,Db.CursorLocation的不同设置对数据处理有什么影响?另外,用我的这种方法打开数据库的话,是不支持多重事务的
Db.BeginTrans
R1.AddNew
R1("a") = "a"
R1.Update
Db.BeginTrans
R2.AddNew
R2("a") = "a"
R2.Update
Db.CommitTrans
Db.CommitTrans
就是形如这种处理是不行的,有什么解决办法吗?
Sub test()
Dim Db As New ADODB.Connection
Dim R1 As ADODB.Recordset
Dim R2 As ADODB.Recordset
'on error goto lbErr '#b-1
Db.Open "Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=xz" Db.Execute "create table t1(a varchar(10),b varchar(20))"
Db.Execute "create table t2(a varchar(10),b varchar(20))" set r1=new adodb.recordset '注意
R1.Open "t1", Db, adOpenKeyset, adLockOptimistic
Db.BeginTrans
R1.AddNew
R1("a") = "a"
R1("b") = "b" '#b-3
R1.Update
Db.CommitTrans
R1.Close set r2=new adodb.recordset '注意
R2.Open "t2", Db, adOpenKeyset, adLockOptimistic '#b-2
Db.BeginTrans
R2.AddNew '#a-1 在这里时出错:"ITransaction::Commit 或 ITransaction::Abort 被调用,并且对象处于 zombie 状态。"
R2("a") = "a"
R2("b") = "b"
R2.Update
Db.CommitTrans
R2.Close Db.Execute "drop table t1"
Db.Execute "drop table t2"
Db.Close
'lbErr: '#b-4
End Sub或者
Option Explicit
Sub test()
Dim Db As New ADODB.Connection
'on error goto lbErr '#b-1
Db.Open "Provider=SQLOLEDB.1;Persist Security Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=xz" Db.Execute "create table t1(a varchar(10),b varchar(20))"
Db.Execute "create table t2(a varchar(10),b varchar(20))" Db.BeginTrans
db.execute "insert into t1......"
db.committrans db.begintrans
db.execute "insert into t2......"
db.committrans....... Db.Execute "drop table t1"
Db.Execute "drop table t2"
Db.Close
'lbErr: '#b-4
End Sub
解决方法
1.
最好像casta(casta) 所说:把记录集游标设为本机,以静态游表打开,取回数据后与连接断开,需要更新时再连接
2.
a.定义一全局时间型变量,每次rollback后对其赋值
b.没一子窗体也定义一时间型变量,每次刷新后对其赋值
c.每次数据操作前比较两时间值,如子窗体的时间型变量小于全局时间型变量,则私案进行记录集刷新
如何实现多重事务处理
Db.BeginTrans
R1.AddNew
R1("a") = "a"
R1.Update
Db.BeginTrans '在这里,会提示错误,不能开启更多的事务
R2.AddNew
R2("a") = "a"
R2.Update
Db.CommitTrans
Db.CommitTrans
on error goto ine
Db.BeginTrans
R1.AddNew
R1("a") = "a"
R1.Update
'Db.BeginTrans '在这里,会提示错误,不能开启更多的事务
R2.AddNew
R2("a") = "a"
R2.Update
'Db.CommitTrans
Db.CommitTrans
goto inerr
ine:
Db.RollbackTrans '如果出现异常事务回滚
inerr:这只是简单的事务,还谈不上商业逻辑(BA),实际上BA的应用是更加广泛的。
ADO的事务应用在商业逻辑面前就略显单薄了。
这就引入了COM+事务。一个很强大的特性。
关于COM+事务的原理如果说写两本书都不为过。但是很多商业逻辑是架构在COM+的事务基础上的。简单的说就是组件之间的事务通讯。而不是依赖程序本身控制。
简单的原理我给你举个例子。
首先。你的明白什么叫做原子,原子就是组件中不可以分割的最小单位。
你把数据库的操作封装到组件(COM)中。这是了;逻辑中的最小单位。
然后写一些其他的组件调用原子。这些组件可以组成事务。Transaction
就是说,由我来定义组件之间的成功与否的标准。
我可以把原子事务按照我的逻辑组合起来,必须我允许通过的时候,事务才会结束,否则以前做的信息回滚。这就叫逻辑。
COM+的事务强大就在它的事务嵌套。就是说,事务之间可以套事务,就是你刚才想要的方法。
这个实现的好处就是,你可以不断的加事务层。小逻辑包括在大逻辑之中,大逻辑又包括在更大的逻辑中,这样就实现了(BA),商业逻辑。
只要有了商业逻辑,很多问题可以迎刃而解。比如你从银行取钱,如果操作失败的话,你的帐户不会有任何的损失。这就是(BA)的强大。逻辑可以包含所有的一切,把损失减到最小。
呵呵,一口气说了这么多,不知道你明白了没有。
建议你多多阅读COM +(如果你有兴趣的话)
我当初写商业逻辑的时候,也是费了很大的力气。但是现在看来,一切都是值得的。因为它态强大了。
现在虽然微软主张摒弃COM,使用web service代替,但是由于在事务(BA)处理模式上的缺陷,(对于webservice中的事务,我专门做过试验,BUG还是很多。而且不支持事务嵌套,即webservice的嵌套)。所以我想web service要代替COM看来还是需要很多的时间。
不知道对你有没有帮助。