PDA

View Full Version : How to inject an Interceptor on the HibernateTemplate


PVG
02-19-2007, 01:58 PM
All,

I need an Interceptor to do some processing before the data is written to the database.

Where can I set this Interceptor in my Spring config files. In the documentation for the HibernateAccessor class I see that it is best set on the LocalSessionFactoryObject or on the HibernateTransactionManager.

On the LocaslSessionFactory I can't see any property to use for specifying the Interceptor. The only thing I can image is that it must be done using the HibernateProperties dictionary, but I very much doubt that this is the way to go.

Can anyone give me any information on this topic please.

KR

Patrick

boriska
03-05-2007, 10:38 AM
Hello,

I’m confused a little bit too; It should be possible to configure interceptors both via HibernateInterceptor (keyword “entityInterceptor”; see thread from spring-forum: http://forum.springframework.org/archive/index.php/t-10512.html) and on the AOP basis.
EntityInterceptor occurs in OpenSessionInViewModule.cs (http://fisheye1.cenqua.com/browse/springnet/Spring.Net.Integration/projects/Spring.Data.NHibernate/src/Spring/Spring.Data.NHibernate/Data/NHibernate/Support/OpenSessionInViewModule.cs?r1=1.9&r2=1.10) and SessionFactoryUtils.cs (http://fisheye1.cenqua.com/browse/springnet/Spring.Net.Integration/projects/Spring.Data.NHibernate/src/Spring/Spring.Data.NHibernate/Data/NHibernate/SessionFactoryUtils.cs?r=1.12), where the comment said:

Supports setting a Session-level Hibernate entity interceptor that allows
to inspect and change property values before writing to and reading from the
database. Such an interceptor can also be set at the SessionFactory level
(i.e. on LocalSessionFactoryObject), on HibernateTransactionManager, or on
HibernateInterceptor/HibernateTemplate.

<param name="sessionFactory">The session factory to create the
session with.</param>
<param name="entityInterceptor">Hibernate entity interceptor, or <code>null</code> if none.</param>
<param name="adoExceptionTranslator"> AdoExceptionTranslator to use for flushing the
Session on transaction synchronization (can be <code>null</code>; only used when actually
registering a transaction synchronization).</param>


Regards,
Boris

Erich Eichinger
03-08-2007, 07:43 AM
Hi,

There are 2 methods for applying an IInterceptor: Injecting on HibernateTemplate or by configuring and using OpenSessionInViewModule.

You can set an IInterceptor on a HibernateTemplate as follows (example taken from "Spring.Northwind"):

<object id="EntityInterceptor" type="...">
...
</object>

<object id="HibernateTemplate" type="Spring.Data.NHibernate, Spring.Data.NHibernate">
<property name="SessionFactory" ref="SessionFactory"/>
<property name="EntityInterceptor" ref="EntityInterceptor"/>
</object>

<object id="CustomerDao" type="Spring.Northwind.Dao.NHibernate.HibernateCustomerD ao, Spring.Northwind.Dao.NHibernate">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>


When using OpenSessionInViewModule, you must additionally configure OSIV by adding your IInterceptor's name to the <appSettings> configuration section:

<!--
Will cause OSIV to use IInterceptor with object-id "EntityInterceptor"
-->
<appSettings>
<add key="Spring.Data.NHibernate.Support.OpenSessionInViewMo dule" value="EntityInterceptor"/>
</appSettings>


hope this helps,
Erich

boriska
03-09-2007, 05:54 PM
Hello Erich,

thank you for your response.

I think, it should be

<object id="HibernateTemplate" type="Spring.Data.NHibernate.HibernateTemplate, Spring.Data.NHibernate">
<property name="SessionFactory" ref="SessionFactory"/>
<property name="EntityInterceptor" ref="EntityInterceptor"/>
</object>

I tried also to configure OSIV , in my case

<add key="Spring.Data.NHibernate.Support.OpenSessionInViewMo dule.SessionFactoryObjectName" value="sessionFactory"/>
<add key="Spring.Data.NHibernate.Support.OpenSessionInViewMo dule.EntityInterceptor" value="entityInterceptor"/>

but I didn't manage entityInterceptor to get invoked.
Maybe I have to consider Transaction stuff too( I use Transactional Proxy for my services)
http://forum.springframework.net/showthread.php?t=1890 seems to have simular issue. I wonder if there are any interdependences between
Spring.Transaction.Interceptor and Spring.Data.NHibernate.HibernateTemplate...

I'll look into the new Spring.Northwind, I think that the older version didn't contain
IInterceptor sample.

Anyway
Thanks a lot!

Regards,
Boris

Erich Eichinger
03-09-2007, 06:50 PM
Hi,

try the key

Spring.Data.NHibernate.Support.OpenSessionInViewMo dule.EntityInterceptorObjectName

(you wrote only ".EntityInterceptor" instead of ".EntityInterceptorObjectName")

this should do the job.

cheers,
Erich

boriska
03-12-2007, 11:25 AM
Hello,

cool ! It works now!


In order to outline how to enable IInterceptor I will repeat:

The config:


<object name="customerDAO" type="... DAO.NHibernate.NH_CustomerDAO,DAL">
<!-- <property name="sessionFactory" ref="sessionFactory" /> -->
<property name="HibernateTemplate" ref="HibernateTemplate" />
</object>

<object id="entityInterceptor" type="... Domain.AuditInterceptor,DAL">
</object>

<object id="HibernateTemplate" type="Spring.Data.NHibernate.HibernateTemplate, Spring.Data.NHibernate">
<property name="SessionFactory" ref="sessionFactory"/>
<property name="EntityInterceptor" ref="entityInterceptor"/>
</object>


And additionally (in case of OSIV)

<appSettings>
<add key="Spring.Data.NHibernate.Support.OpenSessionInViewMo dule.SessionFactoryObjectName" value="sessionFactory"/>
<add key="Spring.Data.NHibernate.Support.OpenSessionInViewMo dule.EntityInterceptorObjectName" value="entityInterceptor"/>
</appSettings>

does all necessary work.

So we can write all relevant data to a log file this way. But I assume that if I will write a log record into DB, injecting of an IInterceptor with Open-Session-In-View could be complicated. In the book “Hibernate in Action” in chapter 8 there is a sample where authors recommend using new session for the sole purpose of saving a single AuditLogRecord object.
In the comments to the article of Billy McCafferty http://www.codeproject.com/aspnet/NHibernateBestPractices.asp?msg=1447731#xx1447731x x the author submitted 2 options to solve a infinite-loop issue.
Can anybody suggest a way to do db-log-writing within Spring.NET properly?
Should I only inject a new session in a AuditInterceptor?

Thanks in advance,
Boris

e4s3
11-11-2007, 12:40 AM
The current design of SessionScope, specifically SessionScopeSettings class, hold the reference of the NHibernate IInterceptor..., which violates the IInterceptor object flow/design. The IInterceptor object should be one per ISession object, it means that every new open session should pass the new IInterceptor object.

By holding IInterceptor reference the SessionScope attemps to reuse the IInterceptor object for another ISession object.

I personally encounter the problem recently because of I'm trying to use the SearchInterceptor from NHibernate.Search and realize it.

To overcome this .... the SessionScopeSettings class instead holding IInterceptor reference, it better to just hold the name of the IInterceptor prototype, and in the SessionScope class to get the IInterceptor object by name just before the Open execution.

Rodrigo C. A.
11-29-2007, 12:59 PM
I have updated to Spring.NET-1.1-RC2 and it stopped working.

The documentation (http://www.springframework.net/doc-latest/reference/html/orm.html#d0e12478) says that this configuration will change in the future, already has changed in Spring.NET-1.1-RC2?

Looking at the source I saw the comment at the SessionScope class:

/// It is assumed that the session factory object name is called "SessionFactory". In case that you named the object
/// in different way you can specify your can specify it in the application settings using the key
/// Spring.Data.NHibernate.Support.SessionScope.Sessio nFactoryObjectName. Values for EntityInterceptorObjectName
/// and SingleSessionMode can be specified similarly.


But even changing fails.

Someone had this problem?

[]'s

Erich Eichinger
11-29-2007, 01:31 PM
Hi,

please update to the latest nightly build and double-check this. I remember that there has been a problem but it should already be fixed and work as described in the docs.

cheers,
Erich

Erich Eichinger
11-29-2007, 01:38 PM
Hi,

The current design of SessionScope, specifically SessionScopeSettings class, hold the reference of the NHibernate IInterceptor..., which violates the IInterceptor object flow/design.

I entered a Jira issue SPRNET-785 (http://opensource.atlassian.com/projects/spring/browse/SPRNET-785) to remind us fixing this.

cheers,
Erich

Rodrigo C. A.
12-26-2007, 07:37 PM
This works in Asp.Net 1.1? I am trying to do this in a project in Asp.Net 1.1 and is not working.

In Asp.Net 2.0 I made it work.

[]'s

Rodrigo Auler

gator3033
01-06-2008, 11:56 PM
I've followed the steps above but my interceptor is not being executed. We are using transactional proxy's for our service classes. I've include our spring config below. When I debug to my customer dao which simply contains
HibernateTemplate.Get<Domain.Customer.Customer>(customerId);
And I put into the immediate window
HibernateTemplate.SessionFactory.GetCurrentSession ().GetSessionImplementation().Interceptor
{NHibernate.EmptyInterceptor}
[NHibernate.EmptyInterceptor]: {NHibernate.EmptyInterceptor}
For some reason the interceptor on the session implementor is the EmptyInterceptor and not my Audit Interceptor. I should also note that the EntityInterceptor property on the HibernateTemplate is set to my AuditInterceptor.

My CustomerService class that is proxied only call the dao.
public Customer CustomerGetById(int id)
{
return CustomerDao.CustomerFetch(id);
}

From BusinessServices.xml
<object id="txProxyConfigurationTemplate" abstract="true" type="Spring.Transaction.Interceptor.TransactionProxyFac toryObject, Spring.Data">
<property name="PlatformTransactionManager" ref="hibernateTransactionManager"/>
<property name="TransactionAttributes">
<name-values>
<add key="*Create" value="PROPAGATION_REQUIRED"/>
<add key="*Save" value="PROPAGATION_REQUIRED"/>
<add key="*Update" value="PROPAGATION_REQUIRED"/>
<add key="*Modify" value="PROPAGATION_REQUIRED"/>
<add key="*Deactivate" value="PROPAGATION_REQUIRED"/>
<add key="*Delete" value="PROPAGATION_REQUIRED"/>
<add key="*Remove" value="PROPAGATION_REQUIRED"/>
<add key="ExecuteMethod" value="PROPAGATION_REQUIRED"/>
</name-values>
</property>
</object>

<object id="customerService" parent="txProxyConfigurationTemplate">
<property name="Target">
<object id="customerServiceTarget" type="...CustomerService, BusinessServices" singleton="true" parent="BusinessServiceBase">
<property name="customerDao" ref="customerDao"/>
</object>
</property>
</object>


From Dao.xml
<object id="hibernateTemplate" type="Spring.Data.NHibernate.Generic.HibernateTemplate">
<property name="SessionFactory" ref="sessionFactory"/>
<property name="TemplateFlushMode" value="Commit"/>
<property name="QueryCacheRegion" value="Hibernate.Query"/>
<property name="CacheQueries" value="true"/>
<property name="EntityInterceptor" ref="entityInterceptor"/>
</object>

<alias alias="EntityInterceptor" name="entityInterceptor"/>
<object id="entityInterceptor" type="...AuditInterceptor">
</object>

<!-- Hibernate Transaction Manager -->
<object id="hibernateTransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager , Spring.Data.NHibernate12">
<property name="DbProvider" ref="dbProvider"/>
<property name="SessionFactory" ref="sessionFactory"/>
<property name="EntityInterceptor" ref="entityInterceptor"/>
</object>

<alias alias="SessionFactory" name="sessionFactory"/>

<object id="sessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate12" singleton="true">
<property name="DbProvider" ref="dbProvider"/>
<property name="MappingAssemblies">
<list>
<value>HibernateDao</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="dialect" value="NHibernate.Dialect.MsSql2005Dialect"/>
</dictionary>
</property>
</object>


From Web.Config
<appSettings>
<add key="Spring.Data.NHibernate.Support.OpenSessionInViewMo dule.SessionFactoryObjectName" value="SessionFactory"/>
<add key="Spring.Data.NHibernate.Support.OpenSessionInViewMo dule.EntityInterceptorObjectName" value="EntityInterceptor"/>
</appSettings>

<httpModules>
<add name="Spring" type="Spring.Context.Support.WebSupportModule, Spring.Web" />
</httpModule>

Mark Pollack
01-07-2008, 04:19 AM
Hi,

Thanks for the update. We plan to look into it this week, sorry, I don't know workaround or root cause at this point. It will certainly be fixed and included by 1.1.1 for the end of month.

Mark

gator3033
01-07-2008, 06:05 PM
Does this mean it is a known issue or you were able to determine from my post that we had everything properly setup and if it isn't working it is a new issue? I'm just curious to know if anyone else is experiencing the same problem.

Mark Pollack
01-07-2008, 06:07 PM
I haven't duplicated the issue myself, but based on what I'm seeing in this thread, I believe there is one. I didn't see anything out of place in your example config. Sorry I can't be more specific at this time.
Mark

gator3033
01-17-2008, 04:34 PM
I'm checking in to see if there are any updates with this issue?

steinard
01-18-2008, 10:00 PM
Hi!

I just created an extremely simple interceptor and tried to apply it in my configuration. It did not work for me and the EntityInterceptor is always null in my case. Here is a small sample of my persistenceConfig.xml:

...........

<object id="EntityInterceptor" type="Viz.Core.Persistence.nHibernate.Interceptors.Entit yInterceptor, Viz.Core.Persistence"/>

<!-- Hibernate Transaction Manager-->
<object id="hibernateTransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager , Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProviderRef"/>
<property name="SessionFactory" ref="SessionFactory"/>
<property name="EntityInterceptor" ref="EntityInterceptor"/>
</object>

<!-- DAO with NHibernate support by adding the SessionFactory -->
<object id="UserDAO" type="Viz.Core.Persistence.nHibernate.Dao.UserDAO, Viz.Core.Persistence">
<property name="SessionFactory" ref="SessionFactory"/>
</object>
.....................
Since my DAOs are inheriting from HibernateDaoSupport I thought that I could skip the hibernateTemplate configuration. Just to try, I included one and set the interceptor there as well to no avail.

Documentation states:

/// Will get applied to any <b>new</b> ISession created by this object.
/// Such an interceptor can either be set at the ISessionFactory level,
/// i.e. on LocalSessionFactoryObject, or at the ISession level, i.e. on
/// HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager.
/// It's preferable to set it on LocalSessionFactoryObject or HibernateTransactionManager
/// to avoid repeated configuration and guarantee consistent behavior in transactions.
Anyone got this working by setting the EntityInterceptor on LocalSessionFactoryObject or HibernateTransactionManager? Any help is appreciated.

Cheers,
Steinar.

Mark Pollack
01-18-2008, 10:23 PM
Hi,
Just to update, this isn't forgotten, just didn't get to it yet.
Mark

steinard
01-20-2008, 11:57 PM
Hi!

I just looked into this today, and I wonder why we should set the EntityInterceptor on LocalSessionFactoryObject. Sure you can specify an interceptor to the underlying Configuration instance in LocalSessionFactoryObject which produces the session factory, but the produced session factory never exposes the interceptor. So I guess we could just forget LocalSessionFactoryObject then.

In my case, my DAOs inherit from HibernateDaoSupport and the default usage is to provide the DAOs with the session factory. Then, HibernateDaoSupport will always create a default HibernateTemplate, even if one already exists, the existing hibernate template will be replaced by the new default one! This means that the created HibernateTemplate will never reference my entity interceptor. My option is then to create my own HibernateTemplate pass it to my DAOs along with the session factory and at the moment I have to make sure that the template is passed in to HibernateDaoSupport after the ISessionFactory instance has been set so that I can replace the auto-produced template.

Okay, I guess I could do that. To make things easier, I could then just make my DAOs take the HibernateTemplate as ctor arg and then pass it to the base class (HibernateDaoSupport):


/// <summary>
/// An abstract class implementing generic DAO interface which will be used as an ancestor for all
/// DAO objects.
/// </summary>
public abstract class GenericDAO<KEY> : HibernateDaoSupport
{

/// <summary>
/// Constructor initializing the DAO with the configured template holding the
/// session factory object and entity interceptor.
/// </summary>
/// <param name="hibernateTemplate">The configured hibernate template</param>
protected GenericDAO(HibernateTemplate hibernateTemplate)
{
SessionFactory = hibernateTemplate.SessionFactory;
HibernateTemplate = hibernateTemplate;
}
The relevant parts of the configuration would then look like:

....

<object id="EntityInterceptor" type="Viz.Core.Persistence.nHibernate.Interceptors.Entit yInterceptor, Viz.Core.Persistence"/>

<!-- Hibernate Transaction Manager-->
<object id="hibernateTransactionManager" type="Spring.Data.NHibernate.HibernateTransactionManager , Spring.Data.NHibernate12">
<property name="DbProvider" ref="DbProviderRef"/>
<property name="SessionFactory" ref="SessionFactory"/>
<property name="EntityInterceptor" ref="EntityInterceptor"/>
</object>

<object id="hibernateTemplate" type="Spring.Data.NHibernate.HibernateTemplate, Spring.Data.NHibernate12">
<property name="SessionFactory" ref="SessionFactory"/>
<property name="QueryCacheRegion" value="Hibernate.Query"/>
<property name="CacheQueries" value="true"/>
<property name="EntityInterceptor" ref="EntityInterceptor"/>
</object>

<!-- DAO with NHibernate support by adding the SessionFactory -->
<object id="UserDAO" type="Viz.Core.Persistence.nHibernate.Dao.UserDAO, Viz.Core.Persistence">
<constructor-arg name="hibernateTemplate" ref="hibernateTemplate" />
</object>

....
Okay, if you are in a hurry (as I am) then this will solve your problem and make your interceptor do some interception! At least it worked for me ;)

For a more robust fix, I guess we could make some minor changes to the HibernateDaoSupport class. For instance, HibernateDaoSupport could take HibernateTemplate as input instead of an instance of ISessionFactory. This would make it explicit that the HibernateDaoSupprt class actually needs a hibernate template instance, and that you as a consumer of that class have to provide it! The session factory instance can always be derived from the hibernate template, so we do not worry about that (actually, that is what the current HibernateDaoSupport does once the session factory object has been passed into the dao support class, for details see the code listing below).
Other changes that must be done is to fix the SessionFactory property (or decide if it should be removed altogether) on HibernateDaoSupport, so that it never creates it's own HibernateTemplate, or at least not if an instance of the template already exists:

HibernateDaoSupport's SessionFactory property:

....
/// <summary>
/// Gets or sets the session factory to be used by this DAO.
/// Will automatically create a HibernateTemplate for the given SessionFactory.
/// </summary>
/// <value>The session factory.</value>
public ISessionFactory SessionFactory
{
get
{
return (this.hibernateTemplate != null ? this.hibernateTemplate.SessionFactory : null);
}
set
{
// The commented out lines below should be included:
// if(null == hibernateTemplate)
hibernateTemplate = CreateHibernateTemplate(value);
// else hibernateTemplate.SessionFactory = value;
}
}
...
I can see the usage where you just want to use a plain HibernateTemplate and the convenience of just getting it without any configuration by just setting the SessionFactory. In that case, at least the SessionFactory setter should be changed. What do you think?

Cheers,
Steinar.

Erich Eichinger
01-21-2008, 06:12 AM
Hi Steinar,

why don't you just do not set the SessionFactory on your DAO? The HibernateDaoSupport.SessionFactory property is only meant as a convenience if you are happy with the default HibernateTemplate. Otherwise just set the HibernateTemplate as you do in your configuration and leave the HibernateDaoSupport.SessionFactory property untouched.

@all:
Karl Chu entered the JIRA issue SPRNET-845 (http://jira.springframework.org/browse/SPRNET-845), noting that this has been my fault. OpenSessionInViewModule uses the wrong key to lookup the SessionFactory and EntityInterceptor names in the <appSettings> section. I just committed the fix for this.

cheers,
Erich

Erich Eichinger
01-21-2008, 06:27 AM
Hi gator3033,

try making your CustomerGetByID() method virtual or part of an interface e.g. ICustomerDao. It seems, that the HibernateTransactionManager does not intercept your method call.
Since Spring.AOP is based on dynamically creating proxies, your methods can only be intercepted if they are virtual or part of an interface.

cheers,
Erich

steinard
01-21-2008, 08:39 AM
Hi Erich!

Sure I could do that. I guess I misunderstood the class conventions there. Also, all examples I've seen passes in the SessionFactory. It got me confused ;)

Cheers,
Steinar.

gator3033
01-21-2008, 05:01 PM
Erich,
I'm doing exactly this in my example. I only omitted this from my post. CustomerService actually has a reference to ICustomerDao, it's implementation is CustomerDao which uses the HibernateTemplate. This is being injected by Spring.

If I understand the previous posts there is a solution out there that addresses this issue in JIRA SPRNET-845. Can you tell me which build number I can get to test this out and where do I get the nightly builds from?

Thanks,

JR

Erich Eichinger
01-21-2008, 08:09 PM
Hi,

sure, for SPRNET-845 you just need to get the latest nightly build (http://www.springframework.net/downloads/nightly/). But I'm still not sure if it applies to your pb. Can you maybe post some sample code illustrating the issue?

tx,
Erich

gator3033
01-22-2008, 04:33 PM
Erich,
Can you review my post above (post #12) and tell me what additional information you might need?

JR

gator3033
01-29-2008, 11:01 PM
Mark and Erich,
I have put together my own workaround by extending LocalSessionFactoryObject and implementing the NewSessionFactory(Configuration) method. In this method I'm setting my Interceptor in the Confiugration and then calling base.NewSessionFactory(Configuration) and returning the ISessionFactory. This seems to put my interceptor on every session that the ISessionFactory produces.

Please let me know if I am going down a dangerous path with this work around.

Thanks,

JR