在Microsoft Access VBA中将数据库表一次锁定给一个用户

在Microsoft Access VBA中将数据库表一次锁定给一个用户,vba,ms-access,locking,race-condition,acid,Vba,Ms Access,Locking,Race Condition,Acid,我想知道是否可以一次将数据库表锁定给一个用户,以便在数据库上执行ACID术语,并允许使用VBA在Microsoft Access数据库中一次执行一个完整事务。我在伪代码中寻找以下内容: 锁定所有表或某些表。 执行所有SQL或其他内部操作。 执行事务并解锁所有表或某些表。 但是,在客户端失败的情况下,例如客户端应用程序挂起,用户必须在客户端应用程序仍在处理事务时强制关闭客户端应用程序,客户端应用程序将恢复更改,而不会实际执行事务,并解锁锁。我要找的是读写锁,当数据库已经被锁定时,试图锁定数据库的客

我想知道是否可以一次将数据库表锁定给一个用户,以便在数据库上执行ACID术语,并允许使用VBA在Microsoft Access数据库中一次执行一个完整事务。我在伪代码中寻找以下内容:

锁定所有表或某些表。 执行所有SQL或其他内部操作。 执行事务并解锁所有表或某些表。
但是,在客户端失败的情况下,例如客户端应用程序挂起,用户必须在客户端应用程序仍在处理事务时强制关闭客户端应用程序,客户端应用程序将恢复更改,而不会实际执行事务,并解锁锁。我要找的是读写锁,当数据库已经被锁定时,试图锁定数据库的客户端必须等待它被解锁?

在工作区的事务中包装您的事务:

Dim wks     As DAO.Workspace
Dim dbs     As DAO.Database

Set wks = DBEngine(0)
Set dbs = wks.Databases(0)

wks.BeginTrans
    ' Do stuff using dbs and DAO.
wks.CommitTrans

Set dbs = Nothing
Set wks = Nothing
如果可能出现错误,请包括跳过CommitTrans并调用以下命令的错误处理程序:

wks.RollBack

事实上,我找到了一种方法,通过一个虚构的黑客锁类型来模拟单用户读写访问。下面将描述我是如何做到这一点的

所以,我在access中创建了一个小表,比如说,名为Lock,有一列名为SomeValue。此列必须是主键,并且可以是任何值,因此我将其设置为number类型。此表将存储将在其中制作的所有锁,尝试获取锁的各方必须就锁的值达成一致。例如,两个客户端将尝试获取值为1的锁,因此它们应该请求锁1,然后释放锁1

首先,我通过将锁的值传递给试图获取锁的一侧,来进行两个帮助器查询,以设置和释放锁:

设置锁查询:

INSERT INTO Lock (SomeValue)
VALUES ([GetLockValue]);
DELETE *
FROM Lock
WHERE SomeValue=[GetLockValue];
释放锁查询:

INSERT INTO Lock (SomeValue)
VALUES ([GetLockValue]);
DELETE *
FROM Lock
WHERE SomeValue=[GetLockValue];
然后,这里是TrySetLock函数,它将尝试设置传入值的锁,并返回设置结果,其中1是一个通过,0是一个失败和SetLock子函数,它将等待直到传入值的锁为空以获取它-它使用自旋锁方法获取锁:

Public Function TrySetLock(LockValue As Integer) As Integer
    Dim dbs As dao.Database
    Dim qdf As dao.QueryDef
    Set dbs = CurrentDb
    Set qdf = dbs.QueryDefs("SetLock")
    qdf.Parameters("GetLockValue").Value = LockValue
    qdf.Execute
    TrySetLock = qdf.RecordsAffected
End Function

Public Sub SetLock(LockValue As Integer)
    Do While TrySetLock(LockValue) = 0
    Loop
End Sub
这是ReleaseLock子模块,它将通过传入的值释放锁-此子模块将始终成功,即使不存在此类锁:

Public Sub ReleaseLock(LockValue As Integer)
    Dim dbs As dao.Database
    Dim qdf As dao.QueryDef
    Set dbs = CurrentDb
    Set qdf = dbs.QueryDefs("ReleaseLock")
    qdf.Parameters("GetLockValue").Value = LockValue
    qdf.Execute
End Sub
正如您在这里看到的,我使用了SQL和Microsoft Access表的主键属性来确保插入或此处提到的通过锁定一次只能对一方或客户端成功,除非第一方移除或释放双方同意的锁值相同的锁,否则另一方将永远不会成功


但是,如果一个客户机未能释放锁(例如客户机的程序被冻结,并且不得不强制杀死该程序),那么这将导致阻塞依赖同一锁的所有客户机的问题。我想知道,当一个程序被强制终止时,类模块的析构函数是否会被调用?如果它被调用,那么我认为这个问题可以通过创建一个具有某个值的锁类来解决,该类的析构函数将释放该锁,而不必让其他客户端等待该锁。

但这也不会阻止读取。我以前试过!!你应该在你的问题中提到这一点,并解释为什么你认为这不能解决你的问题。我仍然不清楚为什么在事务运行时其他用户不应该具有读取权限。ACID虽然Access不完全符合它的要求,但可以保证数据库中不会出现不一致的状态:要么事务成功完成,要么根本没有。两者之间什么都没有。在另一个用户的事务中并发读取是ACID保证的一项核心功能。@Leviathan是对的。如果用户有权访问该文件,则无法阻止读取。阅读是最低可能的访问级别。只有在实际没有用户使用数据库时,才能阻止访问;然后,第一个用户可以以独占方式打开该文件,您可以称之为单用户模式。这样其他用户就无法打开数据库。然而,这不能在VBA中完成-它是由命令行控制的。这是您想要的开始