我用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种办法请高手给出例子,因为目前我还没有这方面的知识。

解决方案 »

  1.   

    我的理解是不是你的表tb1,tb2,tb3和数据库绑定了,
    如果是这种情况,现在通用的做法是对表显示相应的数据,一般都是一次抽取,表和数据库不绑定,
    如果发生操作,或者想显示最新数据,则在相应的事件中重新从数据库读取数据,在表中显示
    这样的话,不应该出现你说的情况表和数据库绑定虽然代码简单,但现在一般都不推荐这么做
      

  2.   

    不知道你用的是什么失误类型?
    ADO的事务模型还是基于COM的MTS?
    把你的代码贴出来看看
      

  3.   

    想了很久,我想用这个例子应该可以说清楚是怎么回事Option Explicit
    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"出错时,错误处理程序需要回滚事务
    我如何在错误处理程序中判断错误是出在已经开始了事务,还是没有开始事务呢?
      

  4.   

    会不会是因为数据库事务还没有处理完呢?你在第2次begaintrans那里加一个断点,运行到那里之后停一会儿再继续看看行不行。关于标志的问题,看过一个M$的例子,用了一个boolean变量表示是否开始了事务。
      

  5.   

    在每个子窗体的FormLoad中写ado连接数据库,Close事件中写关闭连接。这样就可以做到各不相干了,即使是打开的相同的库。
      

  6.   

    to: rappercn(rapper)
       事务是肯定处理完成了的!因为我在实际程序中,是一个窗口用事务处理了数据后,关闭窗口,回到另一个窗口中,再对表进行操作时出的错.标志的问题没有直接的办法吗?TO:Jhzyz(Frank Feng黑名单上的人)
        你的方法是行不能的,我的程序是多窗口处理,就是打开这个窗口后,可以在不关闭此窗口的情况下打开另一个窗口进行数据处理,而在另一个窗口如果进行了事务处理,即使关闭了窗口,回到原窗口再读取表中的数据都会出错.
      

  7.   

    没有仔细看你的代码。但是如果你想要得到事务的状态的话有两种方法处理。
    一、使用类事件
    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
      

  8.   

    二、使用Connection的Attributes进行指定是否启动新事务处理——但是并不是所有的数据源都支持。SQL Server支持,Access没试过。Attributes 属性设置为 dXactCommitRetaining,在调用 CommitTrans 后提供者将自动启动新事务。如果 Attributes 属性设置为adXactAbortRetaining,在调用 RollbackTrans 后提供者将自动启动新事务。
      

  9.   

    这个看得不是很明白,能详细解释一下吗?
    Attributes 属性设置为 dXactCommitRetaining,在调用 CommitTrans 后提供者将自动启动新事务。如果 Attributes 属性设置为adXactAbortRetaining,在调用 RollbackTrans 后提供者将自动启动新事务。
      

  10.   


     Db.CursorLocation = adUseClient
    应该可以了!!!!!!!!!!
      

  11.   

    意思是,设置该属性后。Connection会自动根据设置处理下一个新事务。
      

  12.   

    TO: zjsm96441125(流星物语)
        试过了,同样错误
      

  13.   

    TO: zjsm96441125(流星物语)
        不好意思,又试过,在表打开之前,就要设置
        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
    就是形如这种处理是不行的,有什么解决办法吗?
      

  14.   

    Option Explicit
    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
      

  15.   

    在ADODB.Connection中使用事务处理时,如进行了rollback操作,那么在该连接下打开的所有记录计都会处于超自然状态
    解决方法
    1.
    最好像casta(casta) 所说:把记录集游标设为本机,以静态游表打开,取回数据后与连接断开,需要更新时再连接
    2.
    a.定义一全局时间型变量,每次rollback后对其赋值
    b.没一子窗体也定义一时间型变量,每次刷新后对其赋值
    c.每次数据操作前比较两时间值,如子窗体的时间型变量小于全局时间型变量,则私案进行记录集刷新
      

  16.   

    事务的处理我已经解决了,我只是想知道:
    如何实现多重事务处理
    Db.BeginTrans
            R1.AddNew
            R1("a") = "a"
            R1.Update
        Db.BeginTrans   '在这里,会提示错误,不能开启更多的事务
            R2.AddNew
            R2("a") = "a"
            R2.Update
        Db.CommitTrans
    Db.CommitTrans
      

  17.   

    实际上如果是你以上这样的话ado就能解决。
    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看来还是需要很多的时间。
    不知道对你有没有帮助。