9

我可以使用 Criteria 执行 t-sql 命令来选择表中列的最大值吗?

'从客户中选择 @cus_id = max(id) + 1'

奥利

4

3 回答 3

24

使用投影

session.CreateCriteria(typeof(Customer))
  .SetProjection( Projections.Max("Id") )
  . UniqueResult();
于 2009-08-14T15:34:41.567 回答
15

Max(id) + 1 是一种非常糟糕的生成 id 的方法。如果这是您的目标,请找到另一种生成 id 的方法。

编辑:回答 LnDCobra:

这很糟糕,因为当你进行插入时,很难确保你得到的 max(id) 仍然是 max(id)。如果另一个进程插入一行,您的插入将具有相同的 id,并且您的插入将失败。(或者,相反,如果您的插入先发生,其他进程的插入将失败。)

为了防止这种情况,您必须防止任何其他插入/使您的 get 和后续插入原子,这通常意味着锁定表,这会损害性能。

如果您只锁定写操作,则其他进程将获得 max(id),这与您获得的 max(id) 相同。您进行插入并释放锁,它会插入重复的 id 并失败。或者它也尝试锁定,在这种情况下它会等待你。如果你也锁定阅读,每个人都在等你。如果它也锁定写入,那么它不会插入重复的 id,但它会等待您的读取和写入。

(它打破了封装:你应该让 rdbms 找出它的 id,而不是连接到它的客户端程序。)

通常,此策略将:
* 中断
* 需要一堆“管道”代码才能使其工作
* 显着降低性能
* 或所有三个

与仅使用 RDBMS 的内置序列或生成的自动增量 ID 相比,它会更慢、更不健壮,并且需要更难维护代码。

于 2009-08-14T14:45:32.427 回答
0

最好的方法是制作额外的序列表。您可以在哪里维护序列目标和价值。

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

于 2011-12-29T14:38:26.317 回答