Announcement Announcement Module
Collapse
No announcement yet.
Hibernate Interceptor and Audit Logging with Spring Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Hibernate Interceptor and Audit Logging with Spring

    I want to create an audit log for any class that implements my Auditable interface. In Hibernate, you can write an Interceptor that extends their Interceptor interface to inject logging. Problem is that you need to attach the Interceptor to the session via "interceptor.setSession(session)" when the session is first opened. I'm using Spring for session management so I was wondering if I could somehow nest this Interceptor into a Spring interceptor and just use the application context to add it dynamically.

    If there are other approaches to audit logging with Spring, I'd be interested to hear them.

    Thanks,
    Lou

  • #2
    It looks like there's a setter for adding Hibernate Interceptors (getHibernateTemplate().setEntityInterceptor(inter ceptor)), but the apidoc states that it has to be on a *new* session. Since I'm telling Spring to use the Spring HibernateInterceptor, the session is automatically created for me. How can I add my AuditLog Interceptor to Spring's version before teh session is created? Is this something I could do in the applicationContext somehow?

    I tried adding it my interceptor and using the HibernateInterceptor as the "parent" but that didn't work. I also tried adding it as its own interceptor and then adding it above the Hibernate interceptor in my list of interceptors that use it.

    Here's my current save method:

    Code:
        
    public Serializable save(Object entity) {
            Monitor mon = MonitorFactory.start(this.getClass().getName() + ":save");
            try {
    //            Session session = SessionFactoryUtils.getSession(getSessionFactory(),
    //                    false);
                AuditLogInterceptor interceptor = new AuditLogInterceptor();
    //            interceptor.setSession(session);
                getHibernateTemplate().setEntityInterceptor(interceptor);
                return getHibernateTemplate().save(entity);
            } finally {
                mon.stop();
            }
        }
    Here's my current applicationContext:

    Code:
    	<bean id="myHibernateInterceptor" 
    		class="org.springframework.orm.hibernate.HibernateInterceptor">
    		<property name="sessionFactory">
    			<ref bean="mySessionFactory"/>
    		</property>
    	</bean>
    	
    	<!-- AOP interceptor for trapping Spring runtime exceptions and to rethrow that
    	   - as a checked user-defined exceptions. -->
    	<bean id="exceptionInterceptor"  class="com.mitchell.services.technical.claim.exception.ExceptionInterceptor" /> 
    	
    
    	<bean id="clmActivityLogDaoTarget" 
    		class="com.mitchell.services.technical.claim.dao.spring.ClmActivityLogHibernateDao">
    		<property name="sessionFactory">
    			<ref local="mySessionFactory"/>
    		</property>
    	</bean>
    	<bean id="clmActivityLogDao" 
    		class="org.springframework.aop.framework.ProxyFactoryBean">
    		<property name="proxyInterfaces">
    			<value>com.mitchell.services.technical.claim.dao.ClmActivityLogDao</value>
    		</property>
    		<property name="interceptorNames">
    			<list>
    				<value>exceptionInterceptor</value>
    				<value>myHibernateInterceptor</value>
    				<value>clmActivityLogDaoTarget</value>
    			</list>
    		</property>
    	</bean>
    Thanks,
    Lou

    Comment


    • #3
      Lou, you can configure your entityInterceptor in your applicationContext.xml. org.springframework.orm.hibernate.HibernateTransac tionManager has a property entityInterceptor that holds a Hibernate Entity Interceptor.

      HTH

      Comment


      • #4
        I tried doing this, but it complains about the property setting. Is this documented somewhere? I couldn't find anything in the specification for entityInterceptor. Also, would this work if I'm using CMT?

        Thanks again Omar,
        Lou

        Code:
        	
        	<bean id="auditLogInterceptor"  class="com.mitchell.services.technical.claim.util.AuditLogInterceptor" /> 
        	
        	<bean id="myTransactionManager" 
        		class="org.springframework.orm.hibernate.HibernateTransactionManager">
        		<property name="sessionFactory">
        			<ref local="mySessionFactory"/>
        		</property>
        		<property name="entityInterceptor">
        			<value>auditLogInterceptor</value>
        		</property>
        	</bean>

        Comment


        • #5
          entityInterceptor is of type net.sf.hibernate.Interceptor, so you should use a <ref ...> here:
          Code:
             <bean id="auditLogInterceptor"  class="com.mitchell.services.technical.claim.util.AuditLogInterceptor" /> 
              
             <bean id="myTransactionManager" 
                class="org.springframework.orm.hibernate.HibernateTransactionManager"> 
                <property name="sessionFactory"> 
                   <ref local="mySessionFactory"/> 
                </property> 
                <property name="entityInterceptor"> 
                   <ref local="auditLogInterceptor"/> 
                </property> 
             </bean>
          HTH

          Comment


          • #6
            Ok, I was able to do this, but I had to use two SessionFactory's. At first, I thought I would be able to just create a new session, but since the entityInterceptor is registered with the SessionFactory, the postFlush() gets called again when I try to flush the log message. Thus causing an infinite loop. I really need to scope the entityInterceptor with the session...is there some way to accomplish this? I can't use the HibernateTransaction manager as you pointed out because I am using CMT.

            Thanks,
            Lou

            Here's the way my application context ended up looking:
            Code:
            	<bean id="mySessionFactory" 
            		class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
            		<property name="dataSource">
            			<ref local="clmDataSource"/>
            		</property>
            		<property name="configLocation">
            			<value>classpath&#58;hibernate.cfg.xml</value>
            		</property>
                    <property name="entityInterceptor">
                       <ref local="auditLogInterceptor"/>
                    </property>
            	</bean>
            	
            	<bean id="mySessionFactory2" 
            		class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
            		<property name="dataSource">
            			<ref local="clmDataSource"/>
            		</property>
            		<property name="configLocation">
            			<value>classpath&#58;hibernate.cfg.xml</value>
            		</property>
            	</bean>
            
            	<bean id="auditLogInterceptor"  class="com.mitchell.services.technical.claim.util.AuditLogInterceptor" />
            	
            
            	<bean id="auditLog" 
            		class="com.mitchell.services.technical.claim.util.AuditLog">
            		<property name="sessionFactory">
            			<ref local="mySessionFactory2"/>
            		</property>
            	</bean>

            Comment


            • #7
              I can't use the HibernateTransaction manager as you pointed out because I am using CMT
              EntityInterceptor can also be set at the HibernateTemplate and HibernateInterceptor levels.
              but I had to use two SessionFactory's
              AFAIK, an entityInterceptor should not manipulate the associated Hibernate Session. That is why you need to use two SessionFactories. You can also implement auditing using database triggers; did you consider this alternative?

              Comment


              • #8
                Originally posted by irbouho
                EntityInterceptor can also be set at the HibernateTemplate and HibernateInterceptor levels.
                I tried experimenting at this level as well. If I set the entityInterceptor on anything but the SessionFactory, my interceptor does not get called.

                Originally posted by irbouho
                AFAIK, an entityInterceptor should not manipulate the associated Hibernate Session. That is why you need to use two SessionFactories.
                According to Hibernate in Action, you can just create a new Session based off the JDBC connection. I was thinking of using SessionFactoryUtils.getSession(getSessionFactory() , true), thereby with the "true" parameter a new session is forcibly created.

                Originally posted by irbouho
                You can also implement auditing using database triggers; did you consider this alternative?
                Yes, but the DBAs do not want to implement triggers. Trust me, I'd prefer doing this really.

                Lou

                Comment


                • #9
                  I tried experimenting at this level as well. If I set the entityInterceptor on anything but the SessionFactory, my interceptor does not get called.
                  If a Hibernate Session is already bound to the thread, and has no entityInterceptor configured, applying your interceptor to HibernateTemplate will not work for you since SessionFactoryUtils always returns the pre-bound HibernateSession.

                  SessionFactoryUtils.getSession(getSessionFactory() , true), thereby with the "true" parameter a new session is forcibly created
                  SessionFactoryUtils.getSession returns the thread bound Hibernate Session if one is found, if no session is bound to the current thread SessionFactoryUtils creates a new one if allowCreate is true. So, allowCreate has no effect if a Session is already bound to the thread.

                  Comment


                  • #10
                    Originally posted by irbouho
                    If a Hibernate Session is already bound to the thread, and has no entityInterceptor configured, applying your interceptor to HibernateTemplate will not work for you since SessionFactoryUtils always returns the pre-bound HibernateSession.
                    Thanks for increasing my understanding in this area. It sounds like using two SessionFactories (one with the interceptor, one without) seems like the best solution for me. This solution does work for me fine, however are they any downsides to doing this? CMT (both appear to still fall under my transaction), DB Connections, etc?

                    Lou

                    Comment


                    • #11
                      ,

                      I've the same requirement that i would like to record some history information when the a record in the table is inserted/updated/deleted (CRUD). I'm trying to implement as discussed here....the spring way.

                      I've the AuditLogInterceptor class that implements Interceptor as follows:



                      Code:


                      public class AuditLogRecordInterceptor implements Interceptor {
                      .....
                      ......

                      * *public void postFlush(Iterator arg0) throws CallbackException {
                      * ** *try {
                      * ** ** *for(Iterator itr = inserts.iterator(); itr.hasNext(); ) {
                      * ** ** ** *Auditable entity = (Auditable)itr.next();
                      * ** ** ** *AuditLog.logEvent("create", entity, "testuser");
                      * ** ** *}
                      * ** ** *for(Iterator itr = updates.iterator(); itr.hasNext(); ) {
                      * ** ** ** *Auditable entity = (Auditable)itr.next();
                      * ** ** ** *AuditLog.logEvent("update", entity, "testuser");
                      * ** ** *}
                      * ** *} catch(HibernateException e) {
                      * ** ** *
                      * ** *} finally {
                      * ** ** *inserts.clear();
                      * ** ** *updates.clear();
                      * ** *}
                      * ** *
                      * *}
                      }



                      In this class i'm implementing the required methods such as OnSave, OnPostFlush etc as per the hibernate requirement.

                      I also have a class called AuditLog an utility class as follows:



                      Code:



                      public class AuditLog {
                      * *private SessionFactory sessionFactory;
                      * *
                      * *public void setSessionFactory(SessionFactory sessionFactory) {
                      * ** *this.sessionFactory = sessionFactory;
                      * *}
                      * *
                      * *public SessionFactory getSessionFactory() {
                      * ** *return sessionFactory;
                      * *}
                      * *
                      * *public void logEvent(String message, Auditable entity,
                      * ** ** ** ** ** ** *Long userId)
                      * ** ** ** ** ** ** ** *throws CallbackException {
                      * ** *Session session = null;
                      * ** *
                      * ** *try {
                      * ** ** *AuditLogRecord record = new AuditLogRecord(message, entity.getId(),
                      * ** ** ** ** ** ** ** *entity.getClass(), userId);
                      * ** ** * session = sessionFactory.openSession();
                      * ** ** *session.save(record);
                      * ** ** *session.flush();
                      * ** *} catch(Exception ex) {
                      * ** ** *throw new CallbackException(ex);
                      * ** *} finally {
                      * ** ** *try {
                      * ** ** ** *session.close();
                      * ** ** *} catch(HibernateException ex) {
                      * ** ** ** *throw new CallbackException(ex);* ** ** ** *
                      * ** ** *}
                      * ** *}
                      * ** *
                      * *}
                      }




                      Here is my applicationContext.xml file (partial)



                      Code:



                      * ** * <!-- Hibernate SessionFactory 2 used for audit log-->
                      * * <bean id="sessionFactory2" class="org.springframework.orm.hibernate.LocalSess ionFactoryBean">
                      * * * * <property name="dataSource"><ref local="dataSource"/></property>
                      ** ** *<property name="mappingResources">
                      * ** ** *<list>
                      * ** ** ** *<value>com/apple/ermt/model/Project.hbm.xml</value>
                      * ** ** ** *<value>com/apple/ermt/model/ProjectResource.hbm.xml</value>
                      * ** ** ** *<value>com/apple/ermt/model/Resource.hbm.xml</value>
                      * ** ** ** *<value>com/apple/ermt/model/Requirement.hbm.xml</value>* *
                      * ** ** ** *<value>com/apple/ermt/model/TestCase.hbm.xml</value>* *
                      * ** ** ** *<value>com/apple/ermt/model/UseCase.hbm.xml</value>* ** ** ** ** ** *
                      * ** ** *</list>
                      * ** *</property>
                      * * </bean>
                      *
                      * *<bean id="auditLogInterceptor" class="com.apple.ermt.persistence.audit.AuditLogIn terceptor" singleton="false"/>
                      * *
                      * *<bean id="auditLog" class="com.apple.ermt.persistence.audit.AuditLog">
                      * ** *<property name="sessionFactory"><ref local="sessionFactory2"/></property>
                      * *</bean>
                      * *
                      * * <!-- Hibernate SessionFactory -->
                      * * <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSess ionFactoryBean">
                      * * * * <property name="dataSource"><ref local="dataSource"/></property>
                      ** ** *<property name="mappingResources">
                      * ** ** *<list>
                      * ** ** ** *<value>com/apple/ermt/model/Project.hbm.xml</value>
                      * ** ** ** *<value>com/apple/ermt/model/ProjectResource.hbm.xml</value>
                      * ** ** ** *<value>com/apple/ermt/model/Resource.hbm.xml</value>
                      * ** ** ** *<value>com/apple/ermt/model/Requirement.hbm.xml</value>* *
                      * ** ** ** *<value>com/apple/ermt/model/TestCase.hbm.xml</value>* *
                      * ** ** ** *<value>com/apple/ermt/model/UseCase.hbm.xml</value>* ** ** ** ** ** *
                      * ** ** *</list>
                      * ** *</property>
                      * * * * <property name="hibernateProperties">
                      * * * * <props>
                      * * * * * * <prop key="hibernate.dialect">net.sf.hibernate.dialect.M ySQLDialect</prop>
                      * * * * * * <prop key="hibernate.hbm2ddl.auto">update</prop>
                      * * * * </props>
                      * * * * </property>*
                      * * </bean>
                      ........
                      ......
                      <!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
                      <bean id="transactionManager" class="org.springframework.orm.hibernate.Hibernate TransactionManager">
                      <property name="sessionFactory"><ref local="sessionFactory"/></property>
                      <property name="entityInterceptor"><ref local="auditLogInterceptor"/></property>
                      </bean>



                      My problem is that per the Hibernate in Action book, the AuditLog can have a static method called logEvent which can be used from the AuditLogRecord class to actually record the entry in the table. If i make this method STATIC, it can't access the instance variable sessionFactory being injected by Spring. The solution is to make this variable also STATIC. But i feel that this is WRONG. So do i need to have the logEvent method to be STATIC? If not, how else should i implement this?

                      Could someone please share your experience?


                      Thanks very much in advan

                      Comment


                      • #12
                        I suggest you use database triggers. Why would you ask the wolf to keep track of how many chickens he took... :wink:

                        Comment


                        • #13
                          http://forum.springframework.org/vie...670&highlight=

                          I can log the changes by interceptor for hibernate


                          but , I 've still a question: how to set username using httpsession?

                          http://forum.springframework.org/vie...og+interceptor

                          Comment


                          • #14
                            Originally posted by steven.warren
                            I suggest you use database triggers. Why would you ask the wolf to keep track of how many chickens he took... :wink:
                            Unfortunately in some cases the DB doesn't have all the information necessary to log the level of detail required by Audit. For example, the user's ID, type and status.

                            Scott

                            Comment


                            • #15
                              Hi ,

                              l use two SessionFactory's ,

                              once l got DataIntegrityViolationException (and l catched it) because of the entity l add to the database is not unique for some field , and re-enter the entity again by correcting the mistake , will l get double entry for the auditlog ,

                              Code:
                              Hibernate: insert into database.ENTITY (ENTITY_NAME) values (?)
                              Hibernate: insert into database.AUDITLOG (MESSAGE, ENTITY_ID, ENTITY_CLASS, USERNAME, CREATED) values (?, ?, ?, ?, ?)
                              Hibernate: insert into database.AUDITLOG (MESSAGE, ENTITY_ID, ENTITY_CLASS, USERNAME, CREATED) values (?, ?, ?, ?, ?)
                              what's happening ?

                              yatgor
                              Last edited by morning; Apr 25th, 2008, 05:19 AM.

                              Comment

                              Working...
                              X