NHibernate: SaveOrUpdate by <natural-id> - nhibernate

Is there an easy way to make NH INSERT or UPDATE an entity depending on whether there is already an entity with same <natural-id />?
The entity is mapped to another (root) one using <many-to-one cascade="save-update" />.

Not automatically. You will have to read from the database to see if a record for that natural-id already exists and then determine if you need to do an insert or update. Which of course means that the cascade won't work.
I was looking for something similar in How do I Insert or Update (or overwrite) a record using NHibernate?

Related

Nhibernate efficient cascade delete

On a delete context with nHibernate, when deleting a parent with child collection
I would like to know why Nhibernate do a delete line by line for children (on child PK)
DELETE FROM children where Id=1
DELETE FROM children where Id=2
...
DELETE FROM parent where id=1
Why nhibernate can't do
DELETE FROM children where parentId=1
DELETE FROM parent where id=1
It will be more efficient if parent have 100k children for example.
I search in many topics without finding a correct anwser. I did some tests too but witout success
An idea ?
That is a case, where we can use NHibernate extensibility points. The doc
19.3. Custom SQL for create, update and delete
NHibernate can use custom SQL statements for create, update, and
delete operations. The class and collection persisters in NHibernate
already contain a set of configuration time generated strings
(insertsql, deletesql, updatesql etc.). The mapping tags <sql-insert>,
<sql-delete>, and <sql-update> override these strings:
<class name="Person">
<id name="id">
<generator class="increment"/>
</id>
<property name="name" not-null="true"/>
<sql-insert>INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? )</sql-insert>
<sql-update>UPDATE PERSON SET NAME=UPPER(?) WHERE ID=?</sql-update>
<sql-delete>DELETE FROM PERSON WHERE ID=?</sql-delete>
</class>
So, if standard deletion is not useful, we can provide our own process, including some stored procedure
<sql-delete>exec deletePerson ?</sql-delete>
Summary, in most cases, the standard model is working and effective enough. In case we need to improve SQL .. we can ...

Is it possible to delete a collection of root entities with all their children using NHibernate

Here's my problem: I have a set of ids. These are the ids of a collection of root entities. Now I want to delete all these root entities, efficiently.
I can't do a WHERE Id IN (1, 2, 3) type of clause, as I'm deleting root entities with children.
I'm wondering if it's possible to avoid retrieving all root entities and deleting them one by one. The problem with that approach isn't so much the SELECT, it's have lots of separate DELETE statement.
Is it possible for NHibernate to batch this, including the delete of all the children? Extra complexity: children can have their own children.
So I'd want NHibernate to first delete the 'bottom-most' children with an IN-clause (maybe multiples ones), then the children with an IN-clause, and then finally the root entities with an IN-clause.
If this isn't possible, what's a good approach to delete multiple root entities efficiently with NHibernate?
UPDATE
This is not valid in your case where you had a list of ids (didn't read your question properly).
First, batch deletes of the "parents" can be made setting adonet.batch_size, http://nhibernate.info/doc/nh/en/index.html#performance-batch-updates.
END UPDATE
Secondly, to avoid reading the children (and generate deletes for these), you can set the cascading delete to happen on db level by setting ON CASCADE DELETE as a foreign key constraints.
Eg (note the on-delete attr)
<set name="TheCollection" inverse="true" cascade="save-update">
<key name="theKey" on-delete="cascade"/>
<one-to-many class="TheType"/>
</set>
Note that
this only works on inverse (bidirectional) relationships.
your db schema needs to be updated with this fk constraint
don't use cascade="all" or similar where deletes are included
Might help using the Session.Delete statement passing an array of IDs? I believe this will delete the whole map for those objects matching the IDs in the array.
Session.Delete("from MyTable t where t.ID in :IDs", IDs, NHibernate.NHibernateUtil.Guid);
where IDs is an array of Guids in this case, but could be any object.

NHibernate prevent cascade delete

Suppose I have a class Foo. I also have a view on Foo called Foo_Foo that lists a many-to-many association between Foos. I mapped this association as a simple immutable set on each Foo, with cascade="none":
<set name="association" table="Foo_Foo" cascade="none" mutable="false">
<key column="ParentFoo" />
<many-to-many class="Foo, MyAssembly" column="BaseFoo" />
</set>
However, when I try to delete a Foo, NHibernate tries and rightly fails to delete the Foo.association.
How can I prevent NHibernate from trying to delete the association to a view?
The collection belongs to Foo. You can't share the collection, so there is no need to keep it in the database. Cascade is used to tell NH if the referenced Foos should be also deleted or not.
Why do you want the Foo_Foo records to keep in the database? If this should be a bidirectional many-to-many self reference, it doesn't work like this.
Edit, after understanding the question.
Cascade doesn't work in your case, because it affects only the referenced Foos.
To avoid inserts / updates and deletes of the collection table, you may try one of the following:
First obvious attempt is mutable="false", which you already tried. I don't really understand why it isn't working. You may ask in the Nhibernate user group.
Less obvious, but promising is inverse="true". Inverse tells NH that the collection is mapped somewhere else and doesn't need to be stored from here. So it just omits inserts, but I don't know about deletes.
If this doesn't work, you need to explore more complex solutions. You could map it as a one-to-many of an intermediate entity which references the Foos. The intermediate entity is a mapping to the view. It is immutable (which still may lead to delete statements). In this case, cascade="false" will work (because it is the referenced entity). It will also work configure insert, update and delete sql statements (which are empty), but this is most probably not even necessary.

nhibernate mapping: delete collection, insert new collection with old IDs

my issue lokks similar to this one: (link)
but i have one-to-many association:
<set name="Fields" cascade="all-delete-orphan" lazy="false" inverse="true">
<key column="[TEMPLATE_ID]"></key>
<one-to-many class="MyNamespace.Field, MyLibrary"/>
</set>
(i also tried to use )
this mapping is for Template object. this one and the Field object has their ID generators set to identity.
so when i call session.Update for the Template object it works fine, well, almost:
if the Field object has an Id number, UPDATE sql request is called, if the Id is 0, the INSERT is performed. But if i delete a Field object from the collection it has no effect for the Database. I found that if i also call session.Delete for this Field object, everything will be ok, but due to client-server architecture i don't know what to delete.
so i decided to delete all the collection elements from the DB and call session.Update with a new collection. and i've got an issue: nhibernate performs the UPDATE operation for the Field objects that has non-zero Id, but they are removed from DB!
maybe i should use some other Id generator or smth..
what is the best way to make nhibernate perform "delete all"/"insert all" routine for the collection?
Is the entity you are updateing already associated with the session? (ie do you load the entity and modify that loaded instance)?
It sound like you are trying to tell nhibernate to update a detached entity, in this case nhiberante cannot know what entities as been added/removed in the collection. In this case you could use Merge:
var mergedEntity = session.Merge(entityPasedFromClient)
The merge operation will fetch the enity from the db compare it with the one that as been sent from the client and merge them, that way the entity that nhiberante fetch from the db (and is associated with the session) is modified and later fetched, the merged entity is returned (this will not be the same instance as the entity you pass the merge operation).
I am not sure I understand the last part of your question:
"so i decided to delete all the collection elements from the DB and call session.Update with a new collection. and i've got an issue: nhibernate performs the UPDATE operation for the Field objects that has non-zero Id, but they are removed from DB!"
Are the field items updated and then removed?

Single class maps to two table join

looking at an existing NHibernate implementation that maps a single class to two database tables that are joined. The current functionality is read-only. The table join is actually hidden away via a readonly view and it's the view that's referred to in the NHibernate mapping. Works fine for readonly behaviour. Except I need to add Insert, Update, Delete behaviour and only one of the tables needs to be inserted/updated/deleted. How best to do this? I can take readonly off the view of course, I could replicate the join in the NHibernate mapping, but how do I tell NHibernate that insert/update/delete affects one table only?
Thanks DC
You can set the update and insert attributes on the property mappings to false and they will be excluded from updates and inserts:
<property name="MyProperty" update="false" insert="false" />
Keep in mind that your view will need to have a single primary key defined to enable updates or inserts.
You can map to a view instead of a table, then define custom sql for your inserts, updates and deletes.
I would recommend calling a stored procedure.
See the docs: http://nhibernate.info/doc/nh/en/index.html#querysql-cud
I've used this technique quite a lot and it works fine.