我可以使用 Criteria 执行 t-sql 命令来选择表中列的最大值吗?
'从客户中选择 @cus_id = max(id) + 1'
塔
奥利
我可以使用 Criteria 执行 t-sql 命令来选择表中列的最大值吗?
'从客户中选择 @cus_id = max(id) + 1'
塔
奥利
使用投影:
session.CreateCriteria(typeof(Customer))
.SetProjection( Projections.Max("Id") )
. UniqueResult();
Max(id) + 1 是一种非常糟糕的生成 id 的方法。如果这是您的目标,请找到另一种生成 id 的方法。
编辑:回答 LnDCobra:
这很糟糕,因为当你进行插入时,很难确保你得到的 max(id) 仍然是 max(id)。如果另一个进程插入一行,您的插入将具有相同的 id,并且您的插入将失败。(或者,相反,如果您的插入先发生,其他进程的插入将失败。)
为了防止这种情况,您必须防止任何其他插入/使您的 get 和后续插入原子,这通常意味着锁定表,这会损害性能。
如果您只锁定写操作,则其他进程将获得 max(id),这与您获得的 max(id) 相同。您进行插入并释放锁,它会插入重复的 id 并失败。或者它也尝试锁定,在这种情况下它会等待你。如果你也锁定阅读,每个人都在等你。如果它也锁定写入,那么它不会插入重复的 id,但它会等待您的读取和写入。
(它打破了封装:你应该让 rdbms 找出它的 id,而不是连接到它的客户端程序。)
通常,此策略将:
* 中断
* 需要一堆“管道”代码才能使其工作
* 显着降低性能
* 或所有三个
与仅使用 RDBMS 的内置序列或生成的自动增量 ID 相比,它会更慢、更不健壮,并且需要更难维护代码。
最好的方法是制作额外的序列表。您可以在哪里维护序列目标和价值。
public class Sequence : Entity
{
public virtual long? OwnerId { get; set; }
public virtual SequenceTarget SequenceTarget { get; set; }
public virtual bool IsLocked { get; set; }
public virtual long Value { get; set; }
public void GenerateNextValue()
{
Value++;
}
}
public class SequenceTarget : Entity
{
public virtual string Name { get; set; }
}
public long GetNewSequenceValueForZZZZ(long ZZZZId)
{
var target =
Session
.QueryOver<SequenceTarget>()
.Where(st => st.Name == "DocNumber")
.SingleOrDefault();
if (target == null)
{
throw new EntityNotFoundException(typeof(SequenceTarget));
}
return GetNewSequenceValue(ZZZZId, target);
}
protected long GetNewSequenceValue(long? ownerId, SequenceTarget target)
{
var seqQry =
Session
.QueryOver<Sequence>()
.Where(seq => seq.SequenceTarget == target);
if (ownerId.HasValue)
{
seqQry.Where(seq => seq.OwnerId == ownerId.Value);
}
var sequence = seqQry.SingleOrDefault();
if (sequence == null)
{
throw new EntityNotFoundException(typeof(Sequence));
}
// re-read sequence, if it was in session
Session.Refresh(sequence);
// update IsLocked field, so we acuire lock on record
// configure dynamic update , so only 1 field is being updated
sequence.IsLocked = !sequence.IsLocked;
Session.Update(sequence);
// force update to db
Session.Flush();
// now we gained block - re-read record.
Session.Refresh(sequence);
// generate new value
sequence.GenerateNextValue();
// set back dummy filed
sequence.IsLocked = !sequence.IsLocked;
// update sequence & force changes to DB
Session.Update(sequence);
Session.Flush();
return sequence.Value;
}
OwnerId - 当您需要为同一实体维护不同的序列时,基于某种所有者。例如,您需要维护合同中文档的编号,然后OwnerId will be = contractId