Nhibernate标准:'select max(id)......'

时间:2009-08-14 14:43:48

标签: nhibernate tsql icriteria

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

'从客户

中选择@cus_id = max(id)+ 1

TA

奥利

3 个答案:

答案 0 :(得分:23)

使用Projection

session.CreateCriteria(typeof(Customer))
  .SetProjection( Projections.Max("Id") )
  . UniqueResult();

答案 1 :(得分:15)

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

编辑:回答LnDCobra:

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

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

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

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

一般来说,这种策略要么:   *休息   *需要一堆“管道”代码才能使其正常工作   *显着降低性能
  *或全部三个

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

答案 2 :(得分:0)

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

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