根据rowversion值更新记录?

时间:2018-08-27 13:52:05

标签: sql sql-server-2008 coldfusion sql-update rowversion

我最近实现了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,这对我来说是新的。

2 个答案:

答案 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;