views:

5945

answers:

5

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?

A: 

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
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 .
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
A: 

Use the session.SaveOrUpdate(object) method.

gcores
SaveOrUpdate() can't be used when the entity has an assigned id.
mockedobject
Yes it can. See the comment on my answer.
Elie
It can be used with an assigned id if the object has a Version or Timestamp field. That doesn't solve the problem though.
g .
A: 

You can do

Obj j = session.get(Object.class(), id);
if (j != null)
   session.merge(myObj);
else
   session.saveOrUpdate(myObj);
Elie
There isn't a merge() method. Is this something that is in hibernate but not NHibernate?
g .
If I have to get the object anyway, I can just delete it if it exists. I was hoping to avoid that though.
g .
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
SaveOrUpdate() can't be used when the entity has an assigned id.
mockedobject
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
+3  A: 

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);

ShDev
+4  A: 

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://devlicio.us/blogs/mike_nichols/archive/2008/07/29/when-flushing-goes-bad-assigned-ids-in-nhibernate.aspx

bingle