-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 
Author Message
 Post subject: Intercepting database calls with NHibernate
PostPosted: Mon Jan 08, 2007 3:49 pm 
Beginner
Beginner

Joined: Thu Dec 21, 2006 11:38 am
Posts: 30
Is there any way to do this?

I need to be able to inject Thread.CurrentThread.CurrentPrincipal.Identity.Name to a param on a stored procedure called when NHibernate builds the IDbCommand to perform an update.

Any current way to do this, and if not, where's a good place to start... SingleTableEntityPersister?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 08, 2007 6:02 pm 
Contributor
Contributor

Joined: Sat Sep 24, 2005 11:25 am
Posts: 198
Here is how:
http://www.ayende.com/Blog/2006/12/26/L ... eters.aspx


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 08, 2007 10:51 pm 
Senior
Senior

Joined: Sat Mar 25, 2006 9:16 am
Posts: 150
It looks like the original poster wants to set parameters when doing an insert or update. The blog post you linked to only helps when using filters with formulas (i.e., select / read only).

Without knowing the details of this scenario, I would try using ILifecycle or an interceptor to set the properties of any object you want to persist.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 11, 2007 3:38 pm 
Beginner
Beginner

Joined: Thu Dec 21, 2006 11:38 am
Posts: 30
The previous poster is correct.

When updating/inserting new objects, we want to use stored procedures that will create and maintain history on particular tables. 'PACKAGE' and 'COMPONENT'.

When inserting/updating PACKAGE or COMPONENT, our sprocs have all the fields defined + one parameter at the end "USER_ID" which is to be fed the Thread.CurrentPrincipal.Identity.Name string.

In order to do this effectively, somekind of db interception is needed, at a much lower level than IInterceptor and ILifecycle provide.

I'm curious if the newer Hibernate 3 is considering this usage pattern?

As for implementation, I'm going to have the session or sessionfactory have a IDbInterceptor property that the IBatcher will call if not null.

Initially I see IDbInterceptor's interface as:
Code:
using System.ComponentModel;
using System.Data;

namespace NHibernate
{
  public interface IDbInterceptor
  {
    void OnCreate(IDbCommand command, CancelEventArgs e)
    void OnUpdate(IDbCommand command, CancelEventArgs e)
    void OnDelete(IDbCommand command, CancelEventArgs e)
    void OnSelect(IDbCommand command, CancelEventArgs e)
  }
}


The IBatcher implementation (NonBatchingBatcher) will check the current session for IDbInterceptor just before executing the command on the connection.

I would assume the NHibernate (and Hibernate team) may be interested in this, if so, just contact me at ensoft@enewtonenterprises.com. I will also make a formal request to add this code into the NHibernate branch.

Lets discuss this a bit so it can meet developers' needs.

(Somebody else will have to do the java port and test it)


Last edited by ensoft on Thu Jan 11, 2007 4:05 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 11, 2007 3:44 pm 
Beginner
Beginner

Joined: Thu Dec 21, 2006 11:38 am
Posts: 30
After a little consideration, (and in the interest of getting this done since I'm running against a deadline) I'm guessing it might be better to have the Domain objects implement the IDbInterceptor interface and have the Entity persisters check for this.

It seems to be the fastest clean way to implement this functionality.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 11, 2007 4:50 pm 
Beginner
Beginner

Joined: Wed Jul 05, 2006 12:45 pm
Posts: 21
We achieved similar functionality like this:

1. Each domain object which needs this "auditing" extends or implements an interface which has modificationUser and modificationDate properties.

2. These properties are set by an AuditInterceptor like this:

Code:
    class AuditInterceptor : EmptyInterceptor
    {
        private SystemUser modificationUser = null;

        public AuditInterceptor(SystemUser modificationUser)
        {
            this.modificationUser = modificationUser;
        }

        public SystemUser ModificationUser
        {
            get { return modificationUser; }
            set { modificationUser = value; }
        }


        #region IInterceptor Members

        public override bool OnFlushDirty(object entity, object id, object[] currentState, object[] previousState, string[] propertyNames, NHibernate.Type.IType[] types)
        {
            Boolean dirtied = false;

            for (int ii = 0; ii < propertyNames.Length; ii++) {
                if (propertyNames[ii] == "ModificationDate") {
                    currentState[ii] = DateTime.Now;

                    dirtied = true;
                } else if (propertyNames[ii] == "ModificationUser") {
                    currentState[ii] = modificationUser;

                    dirtied = true;
                }
            }

            return dirtied;
        }

        public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, NHibernate.Type.IType[] types)
        {
            Boolean dirtied = false;

            for (int ii = 0; ii < propertyNames.Length; ii++) {
                if (propertyNames[ii] == "ModificationDate") {
                    state[ii] = DateTime.Now;

                    dirtied = true;
                } else if (propertyNames[ii] == "ModificationUser") {
                    state[ii] = modificationUser;

                    dirtied = true;
                }
            }

            return dirtied;
        }

        #endregion
    }


This interceptor is used when opening a session, and initialized with the current user pricipal (your Thread.CurrentThread.Principal.Identity).

Then your stored procedure (or trigger in our case) can simply read those values.

HTH.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 11, 2007 5:04 pm 
Senior
Senior

Joined: Sat Mar 25, 2006 9:16 am
Posts: 150
Exactly.... the desired behavior is easily accomplished by inheriting your entity classes from an interface or base class which defines the UserID property. This property is then set by the interceptor. Piece of cake. Certainly no need to extend NH to do this. It's probably one of the most common uses of interceptor that I have seen.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 11, 2007 6:32 pm 
Beginner
Beginner

Joined: Thu Dec 21, 2006 11:38 am
Posts: 30
That's assuming that ModificationUser/ModificationDate is in the mapping, which is something I'm trying to remove from the domain objects and the mapping.

With that in mind, having the ability to apply database corporate policy into database commands would be a more natural fit. I'm surprised Hibernate doesn't have this functionality as most other OR/M's seem to build themselves around always giving the ability to modify the base level database calls.

And thankfully, I was able to accomplish this task already, in fairly short order. AbstractEntityPersister.Update()/Insert() try to cast the domain object to IDbInterceptor and make the call through the interface when possible.

Also, I would really like to see Hib/NHib name the parameters when preparing the statement. I realize the numbering scheme "cuts down" on bandwidth, but when specifying <sql-update> <sql-insert> keeping the parameter order in-tact is a runtime hit-or-miss proposition.

I'd like to look into making named parameters a configurable setting or a policy of "when using Hib/NHib auto-gen sql, use numbers, otherwise name the parameters."

What do you guys think?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 11, 2007 8:45 pm 
Senior
Senior

Joined: Sat Mar 25, 2006 9:16 am
Posts: 150
Well I can see that you are hell bent on not adding a couple lines to your XML files and would rather re-invent the wheel and extend NH with some hundreds of lines of code that must be documented, debugged, and maintained. Good luck to you sir!


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 12, 2007 4:42 am 
Contributor
Contributor

Joined: Sat Sep 24, 2005 11:25 am
Posts: 198
ensoft,
Put it in the mapping, implement the properties via explicit interface implementation, and you are set.
The user will usually not be able to see them.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 12, 2007 4:53 pm 
Beginner
Beginner

Joined: Thu Dec 21, 2006 11:38 am
Posts: 30
Ok, I'll try that.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.