12

I need to write a row to the database regardless of whether it already exists or not. Before using NHibernate this was done with a stored procedure. The procedure would attempt an update and if no rows were modified it would fallback to an insert. This worked well because the application doesn't care if the record exists.

With NHibernate, the solutions I have found require loading the entity and modifying it, or deleting the entity so the new one can be inserted. The application does have to care if the record already exists. Is there a way around that?

Does the Id Matter?

Assigned Id

The object has a keyword as an assigned id and is the primary key in the table.

I understand that SaveOrUpdate() will call the Save() or Update() method as appropriate based on the Id. Using an assigned id, this won't work because the id isn't an unsaved-value. However a Version or Timestamp field could be used as an indicator instead. In reality, this isn't relevant because this only reflects on whether the object in memory has been associated with a record in the database; it does not indicate if the record exists or not in the database.

Generated Id

If the assigned id were truly the cause of the problem, I could use a generated id instead of the keyword as the primary key. This would avoid the NHibernate Insert/Update issue as it would effectively always insert. However, I still need to prevent duplicate keywords. With a unique index on the keyword column it will still throw an exception for a duplicate keyword even if the primary key is different.

Another Approach?

Perhaps the problem isn't really with NHibernate, but the way this is modeled. Unlike other areas of the application, this is more data-centric rather object-centric. It is nice that NHibernate makes it easy to read/write and eliminates the stored procedures. But the desire to simply write without regard to existing values doesn't fit well with the model of an object's identity model. Is there a better way to approach this?

g .
  • 8,110
  • 5
  • 38
  • 48
  • so you know, saveOrUpdate() actually does try to load the object from oracle. Not sure why it won't work with your assigned id as opposed to generated, but as long as you do everything in one session, it won't cost you any extra time. – Elie Nov 28 '08 at 17:05
  • When the object didn't exist in the database, I got an error saying that it couldn't update the object. A Google search turned up references to the fact that SaveOrUpdate() doesn't work with an assigned id. – g . Nov 28 '08 at 17:32
  • I have updated the question to explain the problem in more detail and my improved understanding of NHibernate – g . Jul 06 '09 at 09:58

7 Answers7

7

I`m using

    public IList<T> GetByExample<T>(T exampleInstance)
    {
        return _session.CreateCriteria(typeof(T))
                    .Add(Example.Create(exampleInstance))
                    .List<T>();
    }

    public void InsertOrUpdate<T>(T target)
    {
        ITransaction transaction = _session.BeginTransaction();
        try
        {
            var res=GetByExample<T>(target);
            if( res!=null && res.Count>0 )
                _session.SaveOrUpdate(target);
            else
               _session.Save(target); 
            transaction.Commit();
        }
        catch (Exception)
        {
            transaction.Rollback();
            throw;
        }
        finally
        {
            transaction.Dispose();
        }
    }

but FindByExample method returns all objects alike not objects with the exact ID what do you suggest ? since I have only object as parameter I don't have access to its specific ID field so I cannot use session.get(Object.class(), id);

Gabe Moothart
  • 31,211
  • 14
  • 77
  • 99
ShDev
  • 71
  • 1
  • 2
6

Typically, NHibernate can rely on the unsaved-value to determine whether it should insert or create the entity. However, since you are assigning the ID, to NHibernate it looks like your entity has already been persisted. Therefore, you need to rely on versioning your object to let NHibernate know that it is a new object. See the following link for how to version your entity:

http://web.archive.org/web/20090831032934/http://devlicio.us/blogs/mike_nichols/archive/2008/07/29/when-flushing-goes-bad-assigned-ids-in-nhibernate.aspx

Stephen Holt
  • 2,360
  • 4
  • 26
  • 34
pondermatic
  • 6,453
  • 10
  • 48
  • 63
  • 1
    This link seems to be dead now. Does it exist anywhere else? – Chris Nov 30 '15 at 16:45
  • @Chris - http://web.archive.org/web/20090831032934/http://devlicio.us/blogs/mike_nichols/archive/2008/07/29/when-flushing-goes-bad-assigned-ids-in-nhibernate.aspx – Stephen Holt Sep 25 '18 at 16:11
  • @StephenHolt: Well found! Now if only I could remember what I was doing three years ago that this interested me... ;-) I'd advise editing that link into the answer too though (or I can). – Chris Sep 25 '18 at 16:45
  • 1
    @Chris - ha ha... I assume you've been waiting all this time for the link to be revealed, and now that you have it you can get on and complete that task :-) I've done the edit, although it'll have to be "peer reviewed" to take effect. – Stephen Holt Sep 25 '18 at 17:19
0

Use the session.SaveOrUpdate(object) method.

gcores
  • 12,376
  • 2
  • 49
  • 45
0

You can do

Obj j = session.get(Object.class(), id);
if (j != null)
   session.merge(myObj);
else
   session.saveOrUpdate(myObj);
Elie
  • 13,693
  • 23
  • 74
  • 128
  • There isn't a merge() method. Is this something that is in hibernate but not NHibernate? – g . Nov 28 '08 at 17:29
  • If I have to get the object anyway, I can just delete it if it exists. I was hoping to avoid that though. – g . Nov 28 '08 at 17:40
  • why would you delete it if it exists? Why not just overwrite it? Merge is in hibernate, I don't know about NHibernate. And at some level you have to check if the object exists, either via the hibernate API or manually. – Elie Nov 28 '08 at 17:50
  • 3
    SaveOrUpdate() can't be used when the entity has an assigned id. – mockobject Jun 17 '09 at 21:29
  • Actually, it can. That's precisely the purpose of saveOrUpdate(). save() cannot be used when you have an assigned id, update() cannot be used without one, and saveOrUpdate() bypasses that issue. – Elie Jun 19 '09 at 14:30
  • I just tried this with an assigned id entity and it didn't work. If `myObj` is a transient entity with an assigned id, but doesn't have a Version/Timestamp field, `SaveOrUpdate` does nothing (at least in NHibernate). – vgru Jun 12 '17 at 07:49
  • @Groo Perhaps you didn't commit? ;) Works for me. – BartoszKP Dec 18 '17 at 14:21
0

Query objects where keyword = x, take FirstOrDefault. If it's null, Add new object, if it exists, update object that you got and call saveOrUpdate on it.

WholeLifeLearner
  • 455
  • 4
  • 19
0

This worked for me:

Implementation

    public void InsertOrUpdate<TEntity, TId>(TEntity entity) where TEntity : IIdentificableNh<TId>
    {
        var anyy = session.Get<TEntity>(entity.Id);
        if (anyy != null)
        {
            session.Evict(anyy); //dispatch all data loaded, to allow updating 'entity' object.
            session.Update(entity);
        } 
        else
        {
            session.Save(entity);
        }
        
        session.Flush();
    }

Entity

public class Caracteristica : IIdentificableNh<int>
{
    public virtual int Id { get; set; }

    public virtual string Descripcion { get; set; }
}

I had to create an interface (IIdentificableNh) that allows me to access the Id property value.

Usage example:

session.InsertOrUpdate<Caracteristica, int>(new Caracteristica { Id = 2, Descripcion = "Caracteristica2" });
-1

call hibernate.saveOrUpdate() which will check if the object is in the database, update it if it is, and save (i.e. insert) it if it is not.

Elie
  • 13,693
  • 23
  • 74
  • 128
  • 5
    SaveOrUpdate() doesn't actually check the database. It uses the Id, Version, or Timestamp fields to determine if Save() or Update() should be called. – g . Jul 06 '09 at 10:05
  • Quite correct, but the bottom line is, you can let Hibernate determine which of the two methods to call. From an implementation point of view, this is just semantics. – Elie Jul 07 '09 at 15:37
  • This is an important point. If your session falls out of scope, then you'll most likely get a failure....duplicating the primary-key as the most common. – granadaCoder Oct 25 '13 at 17:27