Nhibernate Criteria: 'select max(id)…'

后端 未结 3 1277
攒了一身酷
攒了一身酷 2021-01-02 08:28

Can I use a Criteria to execute a t-sql command to select the max value for a column in a table?

\'select @cus_id = max(id) + 1 from customers\'

Ta

O

相关标签:
3条回答
  • 2021-01-02 08:51

    Max(id) + 1 is a very bad way to generate ids. If that's your goal, find another way to generate ids.

    Edit: in answer to LnDCobra:

    it's bad because it's hard to make sure that the max(id) you got is still the max(id) when you do the insert. If another process inserts a row, your insert will have the same id, and your insert will fail. (Or, conversely, the other process's insert will fail if your insert happened first.)

    To prevent this, you have to prevent any other inserts/make your get and subsequent insert atomic, which generally means locking the table, which will hurt performance.

    If you only lock against writes, the other process gets max(id), which is the same max(id) you got. You do your insert and release the lock, it inserts a duplicate id and fails. Or it tries to lock too, in which case it waits on you. If you lock against reads too, everybody waits on you. If it locks against writes also, then it doesn't insert the duplicate id, but it does wait on your read and your write.

    (And it breaks encapsulation: you should let the rdbms figure out its ids, not the client programs that connect to it.)

    Generally, this strategy will either:
    * break
    * require a bunch of "plumbing" code to make it work
    * significantly reduce performance
    * or all three

    and it will be slower, less robust, and require more hard to maintain code than just using the RDBMS's built in sequences or generated autoincrement ids.

    0 讨论(0)
  • 2021-01-02 08:51

    Best approach is to make additional Sequences table. Where you can maintain sequence target and value.

    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 - when you need to maintain different sequences for same entity, based on some kind of owner. For example you need to maintain numbering for document within contract, then OwnerId will be = contractId

    0 讨论(0)
  • 2021-01-02 08:56

    Use Projection:

    session.CreateCriteria(typeof(Customer))
      .SetProjection( Projections.Max("Id") )
      . UniqueResult();
    
    0 讨论(0)
提交回复
热议问题