我想知道下列行动方案是否可行:
我正在实现一个简单的数据输入应用程序。每个可编辑记录都驻留在一个表中。用户必须同时编辑多个记录(以减少数据输入时间)。我目前尝试做的是在记录中实现某种锁定机制。
我想到的第一件事就是执行(ExecuteNonQuery)UPDATE some_table SET status ='locked'WHERERE rownum = 1 RETURNING id INTO:locked_id with output parameter,给我锁定记录的ID。我可以执行另一个SELECT语句来读取我想要的任何其他信息,基于此ID。
虽然上述方法似乎适用于单个锁定记录,但我真的不知道如何为多个返回的行执行此操作。 - 如果在上面的示例中,WHERE子句是“rownum< 4”而不是“rownum = 1”,该怎么办?
我的OracleParameter应该是什么样的? 当我指定我的返回oracle参数时(使用单行),
OracleParameter p = cmd.Parameters.Add("ID", OracleDbType.Int32, 10, 0, ParameterDirection.Output);
ORA-24369。我尝试使用ArrayBindCount和数组作为参数的.Value属性,但无济于事。
另外,您是否发现整个特定锁定方式存在根本错误?
谢谢
编辑:一些澄清 - 1.有一个名为user_name的列,可以保存例如登录用户的名称 - 我将其与锁定UPDATE一起更新 2.还有另一列我称之为locked_timestamp,它保存记录被锁定的时间。可能会有一个在夜间运行的后台进程,并将锁定的记录重置为“未锁定” - 假设这不会经常发生,并且由于崩溃而保持“锁定”的记录比例很小与编辑记录的数量进行比较 3.并发更新应该由Oracle自动处理,是的,我在每次UPDATE期间使用事务 - 因此两个用户同时锁定记录的可能性很小 - 或者这就是我听到的(将尝试这样做,一旦我找到了如何更新...返回...多行)
答案 0 :(得分:2)
最后,经过几个小时的搜索和玩代码后,我得出了以下结论(除了头痛):
我从
获得了我想要的组合
try
{
conn.Open();
transaction = conn.BeginTransaction();
cmd = new OracleCommand();
cmd.Connection = GetConnection();
cmd.CommandText =
"BEGIN UPDATE some_table " +
"SET status = 'locked', " +
" locked_tstamp = SYSDATE, " +
" user_name = '" + user + "' " +
"WHERE rownum <= 4 " +
"RETURNING id BULK COLLECT INTO :id; END;";
cmd.CommandType = CommandType.Text;
cmd.BindByName = true;
cmd.ArrayBindCount = 4;
p = new OracleParameter();
p.ParameterName = "id";
p.Direction = ParameterDirection.Output;
p.OracleDbType = OracleDbType.Int64;
p.Size = 4;
p.ArrayBindSize = new int[] { 10, 10, 10, 10 };
p.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
cmd.Parameters.Add(p);
int nRowsAffected = cmd.ExecuteNonQuery();
// nRowsAffected is always -1 here
// we can check the number of "locked" rows only by counting elements in p.Value (which is returned as OracleDecimal[] here)
// note that the code also works if less than 4 rows are updated, with the exception of 0 rows
// in which case an exception is thrown - see below
...
}
catch (Exception ex)
{
if (ex is OracleException && !String.IsNullOrEmpty(ex.Message) && ex.Message.Contains("ORA-22054")) // precision underflow (wth)..
{
Logger.Log.Info("0 rows fetched");
transaction.Rollback();
}
else
{
Logger.Log.Error("Something went wrong during Get : " + ex.Message);
ret = null;
transaction.Rollback();
}
}
finally
{
// do disposals here
}
...
try
{
conn.Open();
transaction = conn.BeginTransaction();
cmd = new OracleCommand();
cmd.Connection = GetConnection();
cmd.CommandText =
"BEGIN UPDATE some_table " +
"SET status = 'locked', " +
" locked_tstamp = SYSDATE, " +
" user_name = '" + user + "' " +
"WHERE rownum <= 4 " +
"RETURNING id BULK COLLECT INTO :id; END;";
cmd.CommandType = CommandType.Text;
cmd.BindByName = true;
cmd.ArrayBindCount = 4;
p = new OracleParameter();
p.ParameterName = "id";
p.Direction = ParameterDirection.Output;
p.OracleDbType = OracleDbType.Int64;
p.Size = 4;
p.ArrayBindSize = new int[] { 10, 10, 10, 10 };
p.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
cmd.Parameters.Add(p);
int nRowsAffected = cmd.ExecuteNonQuery();
// nRowsAffected is always -1 here
// we can check the number of "locked" rows only by counting elements in p.Value (which is returned as OracleDecimal[] here)
// note that the code also works if less than 4 rows are updated, with the exception of 0 rows
// in which case an exception is thrown - see below
...
}
catch (Exception ex)
{
if (ex is OracleException && !String.IsNullOrEmpty(ex.Message) && ex.Message.Contains("ORA-22054")) // precision underflow (wth)..
{
Logger.Log.Info("0 rows fetched");
transaction.Rollback();
}
else
{
Logger.Log.Error("Something went wrong during Get : " + ex.Message);
ret = null;
transaction.Rollback();
}
}
finally
{
// do disposals here
}
...
答案 1 :(得分:1)
以下是有关此记录锁定方案的一些事项:
只需要几点思考。
分享并享受。