我最近实现了SQL rowversion
,以防止系统中的并发问题。更新表中的单行时,我在where子句中使用rowversion
。到目前为止,我已经测试过,似乎是一个不错的解决方案。现在,我正在寻找一种在系统中实现此功能的简便方法。当用户想要更新记录时,将运行以下SP:
CREATE PROCEDURE [dbo].[UpdateBuilding]
@Status BIT = NULL,
@Name VARCHAR(50) = NULL,
@Code CHAR(2) = NULL,
@OriginalRowVersion ROWVERSION
AS
SET NOCOUNT ON
SET XACT_ABORT ON
BEGIN
UPDATE dbo.Building
SET Status = @Status,
Name = @Name,
Code = @Code,
ActionDt = CURRENT_TIMESTAMP
WHERE RowVersion = @OriginalRowVersion
IF @@ROWCOUNT = 0
BEGIN
RAISERROR('Buildingwith code %s was modified or deleted by another user.', 16, 1, @Code);
END;
END;
如果要在上面执行SP,则需要传递必需的参数。这就是我在SQL Management Studio中调用SP的方式:
EXEC UpdateBuilding
@Status = 1,
@Name = "Rockefeller Center",
@Code = 436,
@OriginalRowVersion = 0x0000000000006955;
现在,我开始研究如何在我的系统中使用ColdFusion与Datatbase进行通信。这是有关如何在CF 2016中执行此过程的示例:
<cfstoredproc procedure="UpdateBuilding" datasource="#dsn#">
<cfprocparam dbvarname="@Status" value="#trim(arguments.status)#" cfsqltype="cf_sql_bit" />
<cfprocparam dbvarname="@Code" value="#trim(arguments.code)#" cfsqltype="cf_sql_char" maxlength="2" null="#!len(trim(arguments.code))#" />
<cfprocparam dbvarname="@Name" value="#trim(arguments.name)#" cfsqltype="cf_sql_varchar" maxlength="50" null="#!len(trim(arguments.name))#" />
<cfprocresult name="Result"/>
</cfstoredproc>
您可以看到所有值都与用户在表单中提交的参数一起传递。但是,基于PK
值(在我的情况下为“代码”列)进行更新非常简单。现在,我具有二进制值,这使一切变得更加复杂。首先,我使用JSON将数据发送到客户端。在JSON对象中发送rowversion
时,需要将该值转换为binary
,然后在用户提交表单时转换回来。我想知道是否有更好的方法来实现这一目标?理想情况下,我什至不向用户端发送rowversion
值。我将其保留在后端,一旦用户基于PK提交表单拉行版本值,然后调用存储过程。如果有人知道处理这种情况的好方法,请告诉我。我以前从未使用过rowversion
,这对我来说是新的。
答案 0 :(得分:0)
我使用类似的方法,其中有一个名为version
的类型为int
的列。我通过任何读取操作将其传递给客户端。如果客户端更新了一条记录,则它必须发回正在更新的记录的版本,并且更新将增加版本号。但是,我的方法设置了ColdFusion锁而不是DB锁。这是一些简化的逻辑:
function updateRecord (
required numeric recordID,
required struct updateData,
required numeric version) {
lock name="#arguments.recordID#" type="exclusive" timeout="1" throwontimeout=true {
qRecord = queryExecute() // get the record
if (qRecord.recordCount != 1) {
throw();
}
if (qRecord.version != arguments.version) {
throw();
}
// do the update using arguments.updateData
}
}
与此有关的一个问题是群集中的其他节点将不知道该命名锁。您将不得不想出另一种将代码段锁定到集群中其他请求的方法。如该线程中所述,有多种方法可以实现:
https://dev.lucee.org/t/distributed-lock-management/1004
如果这是一个问题,我确定还有其他解决方案可用。
答案 1 :(得分:0)
处理二进制文件并不像您想的那么困难。检索二进制值后, encode 作为适合发送给客户端(十六进制或base64)的字符串:
<cfset hexStringForClient = binaryEncode(queryName.theBinaryColumn, 'hex')>
当客户端返回编码后的字符串时, decode 将其返回为二进制,然后使用cf_sql_binary
将其传递给数据库:
<cfset binaryValue = binaryDecode(hexStringFromClient, 'hex')>
<cfstoredproc procedure="UpdateBuilding" datasource="#dsn#">
...
<cfprocparam dbvarname="@OriginalRowVersion" value="#binaryValue#" cfsqltype="cf_sql_binary" />
....
</cfstoredproc>
旁注as mentioned in your other thread,开放式并发检查使用WHERE子句中的主键和行版本值:
WHERE
Code = @Code
AND RowVersion = @OriginalRowVersion;