1

I would like to intercept persistence operations over collection properties, decide by myself if it can be synchronized with database, and call a procedure when a persistence is decided to occur over all collection at once and not by each element.

How to do that ?

These collection properties are usually mapped with one-to-many or many-to-many associations. So, when we have something like this:

myEntity.List.Add(new Item());
myEntity.List.Add(new Item());
...
session.Save(myEntity);

For a mapping having two classes (entities) and an unidirectional many-to-many association, I would like to have only two sql statements occurring: INSERT INTO, and a PROCEDURE CALL which expects to receive a list of values comma-separated that is the key values from the List collection above. The collection of keys can only be saved on this system calling a procedure with a list of values (csv).

This kind of customizing is possible to be done ?

Luciano
  • 2,695
  • 6
  • 38
  • 53

2 Answers2

1

Well, I adopted a solution implementing NHibernate Listeners, so I have a listener like that:

public class CustomSaveUpdateEventListener : IPostInsertEventListener, IPostUpdateEventListener {

    private void ScheduleToCommit(AbstractPostDatabaseOperationEvent @event) {
        // Some filter
        if (@event.Entity.GetType().FullName.Equals(MY_ENTITY_TO_INTERCEPTS) {
            object o = @event.Entity;

            // Some logic...
            // ...
            // ...

            IQuery namedQuery = @event.Session.GetNamedSQLQuery(MY_NAMED_QUERY);
            //namedQuery.SetParameter(...); // <== You can set parameters
            //namedQuery.ExecuteUpdate(); // <== DO NOT EXCEUTE HERE, this launches more events recursively and can generate stackoverflow

            @event.Session.ActionQueue.RegisterProcess(new MyActionBeforeCommit(@event, namedQuery).SaveValuesOperation);
            }
        }
    }

    public void OnPostInsert(PostInsertEvent @event) {
        ScheduleToCommit(@event);
    }


    public void OnPostUpdate(PostUpdateEvent @event) {
        ScheduleToCommit(@event);
    }
}

And my object MyActionBeforeCommit() is scheduled to run only when and if a transaction is commited. So, in my "future action" I have:

public class MyActionBeforeCommit {
    private AbstractPostDatabaseOperationEvent e;

    private IQuery query;

    public MyActionBeforeCommit(AbstractPostDatabaseOperationEvent e, IQuery query) {
        this.e = e;
        this.query = query;
    }

    public void SaveValuesOperation() {
        // Here your query will be executed only when a transation is commited, if 
        // it's rolledback this won't be executed
        query.ExecuteUpdate(); 
    }
}

And finally we need to register the new listener on NHibernate configuration, like that:

Configuration.SetListener(ListenerType.PostInsert, new    CustomSaveUpdateEventListener());
Configuration.SetListener(ListenerType.PostUpdate, new CustomSaveUpdateEventListener());

And It works very well. This is a very nice NHibernate feature, very powerful.

Luciano
  • 2,695
  • 6
  • 38
  • 53
0

I've waited a while to respond, because I know a possible approach, but never did it myself.

It sounds like you need to write a custom NHibernate "Dialect". There is are some guidelines here and here how to do it.

You don't mention the database system you use, but your starting point would obviously be to take the the dialect for that system and find the places where you should put your customizations.

Overriding Dialects is fully supported by NHibernate. If this does not work for you, you may even have to look at a custom AbstractEntityPersister, but I don't know how to that with configuration file entries.

You can find NHibernate 3.2 sources here.

A completely different approach could be to use standard NHibernate persistence to a local database, e.g. sql-server CE, and execute a synchronization script for the "real" persistence.

Community
  • 1
  • 1
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • Thanks by answer . I'm trying another approach based on what NHibernate.Envers does. This Framework intercepts some operations using listeners and inject new database operations to save auditing information before the commit. I analyzed yesterday its source code and already tried some injections, as soon as I finish my solution I publish it here. Track it. – Luciano Feb 09 '12 at 13:46
  • OK, interesting. Maybe you can post an answer to your own question when you know more. – Gert Arnold Feb 09 '12 at 13:50