views:

855

answers:

2

I am trying to use Envers on a project that also uses Hibernate and Spring - and I appreciate a lot the code reduction offered by HibernateTemplate.

I configured Envers under JPA, and after a few tweaks I was able to have the schema generated by the EnversHibernateToolTask Ant task (including the auditing tables). However, when I write code such as:

    hibernateTemplate.saveOrUpdate(f);

the data is persisted, but nothing goes to the auditing tables. Conversely, if I write:

    EntityManager em = emf.createEntityManager();
    em.getTransaction().begin();
    em.persist(f);
    em.getTransaction().commit();

then data goest to the audit tables (but I'd rather use the former syntax - I know using JPA's EntityManager decouples that code from Hibernate, but it simple does not pay off the hassle - changing ORM engine is not in my wildest dreams for this project.)

It may help to check my applicationContext.xml configuration:

<bean id="entityManagerFactory"
 class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
 <property name="persistenceUnitName" value="projetox" />
 <property name="dataSource" ref="dataSource" />
 <property name="jpaVendorAdapter">
  <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
   <property name="showSql" value="true" />
  </bean>
 </property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
 <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="sessionFactory"
 class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
 <property name="dataSource" ref="dataSource" />
 <property name="packagesToScan" value="com.w2it.projetox.model" />
 <property name="hibernateProperties">
  <props>
   <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
  </props>
 </property>
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
 destroy-method="close">
 ...
</bean>

and here is my persistence.xml setup:

<persistence-unit name="projetox" transaction-type="RESOURCE_LOCAL">
 <jta-data-source>java:/DefaultDS</jta-data-source>
 <properties>
  <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
  <!--  Hibernate Envers -->
  <property name="hibernate.ejb.event.post-insert"
   value="org.hibernate.envers.event.AuditEventListener" />
  <property name="hibernate.ejb.event.post-update"
   value="org.hibernate.envers.event.AuditEventListener" />
  <property name="hibernate.ejb.event.post-delete"
   value="org.hibernate.envers.event.AuditEventListener" />
  <property name="hibernate.ejb.event.pre-collection-update"
   value="org.hibernate.envers.event.AuditEventListener" />
  <property name="hibernate.ejb.event.pre-collection-remove"
   value="org.hibernate.envers.event.AuditEventListener" />
  <property name="hibernate.ejb.event.post-collection-recreate"
   value="org.hibernate.envers.event.AuditEventListener" />
 </properties>
</persistence-unit>

Does anyone have a hint on what is going on here? Thank you!

+2  A: 

HibernateTemplate has its JPA counterpart, JpaTemplate which provides a fairly similar functionality.

The reason Envers doesn't work with HibernateTemplate is because it relies on JPA events (you can see the listeners declared in your persistence.xml above) triggered when EntityManager is used. It's possible in theory to write code to trigger those events from Hibernate session when HibernateTemplate is used, but it's rather involved.

ChssPly76
Excellent - using JpaTemplate should do the trick then. It does not have all the bells and whistles that I want (in particular, I had some code that needs to do pagination, and did that via findByCriteria, which allows one to inject limits on the call), but at least I have a starting point. Thank you!
chester
This is somewhat of a hack, but if you set JpaTemplate's `exposeNativeEntityManager` to true, you can use its `executeFind()` method to cast `EntityManager` passed to `JpaCallback` as `HibernateEntityManager` and use `getSession()` of the latter to do criteria-based queries.
ChssPly76
Also, in Envers 1.2.1 (the latest release) `AuditEventListener` implements regular Hibernate's event listeners which means it actually should be possible to configure it for plain Hibernate (no JPA). You'd need to get rid of the `entityManagerFactory` and your `persistence.xml` and instead configure all the listeners as part of sessionFactory using `eventListeners` map - look at `LocalSessionFactoryBean` for details; you'd then be able use `HibernateTemplate`. Note that I have not tested this.
ChssPly76
Hmmm.... gotta check both approaches. I think that if I really need to use Hibernate funcionality, I'd rather drop JPA altogether, but I'll try first to do it all without either exposing the HibernateEntityManager or trying such a new funcionality. Thanks.
chester
+1  A: 

All u needed to do was put @Transactional in your Dao or services which call the dao.save()/ update methods.

Even if you register your eventlistener these events are not fired unless you use transcational of the Spring FW. Spring has to know and tell hibernate that these events are fired.

Syed Mahdi