3

Experts/Gurus/Friends

We are working with Spring 3.2, JPA 2, Hibernate 4.2 combo and facing this weird null pointer issue while trying to inject any spring annotated beans into EmtyInterceptor implemented as shown below. We have tried annotating this bean as well as a spring bean but no luck.

Any help to solve this puzzle here is highly appreciated.

import javax.inject.Inject;
import javax.inject.Named;
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import org.springframework.transaction.annotation.Transactional;
...

@Named
@Transactional
public class AuditEmptyInterceptor extends EmptyInterceptor {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    // Didnt inject - Null
    @PersistenceContext
    private EntityManager entityManager;

    // Didnt inject - Null
    //@PersistenceUnit
    //private EntityManagerFactory entityManagerFactory;

    // Didnt inject - Null
    //@Inject
    //private AuditHelper auditHelper;

    @Override
    public boolean onSave(Object entity, Serializable id, Object[] currentState,
            String[] propertyNames, Type[] types) {

        System.out.println("**********inside OnSave() in Audit Empty Interceptor******************");
        if(entity instanceof xxAuditInterface || entity instanceof xxxCompBranchInterface){
            for (int i = 0; i < propertyNames.length; i++) {
         ...
         ...
         // Null entityManager - NPE here
        javax.persistence.Query query = entityManager.createQuery("Select c From CompanyDO c Where c.companyName =:companyName");
        query.setParameter("companyName", xxx);
        CompanyMasterDO companyMasterDO = (CompanyMasterDO) query.getSingleResult();
         ...
         ...
          }
         }
        }

 }

Everywhere else in the application the injection works like a charm without any issues. Here is our applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans default-lazy-init="true" 
xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" 
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" 
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:sec="http://www.springframework.org/schema/security"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">


<context:annotation-config></context:annotation-config> 

<context:component-scan base-package="com" />

<context:property-placeholder location="classpath*:hibernate.properties" />
<tx:annotation-driven />

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan" value="com"/>
 </bean>

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource"
    p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost/rcent_rel_2"
    p:username="root" p:password="root" />

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaAdapter">
    <property name="loadTimeWeaver">
        <bean
            class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
    </property>
    <property name="persistenceXmlLocation" value="classpath*:META-INF/spring-persistence.xml" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
    p:entityManagerFactory-ref="entityManagerFactory" />

<bean id="jpaAdapter"
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
    p:database="MYSQL" 
    p:showSql="false" 
    p:databasePlatform="org.hibernate.dialect.MySQL5Dialect"/>

<\beans>

And our spring-persistence.xml is below. Please note that I have added Emptyinceptor property here.

<?xml version="1.0" encoding="UTF-8"?>

<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" version="2.0">
<persistence-unit name="xxx" transaction-type="RESOURCE_LOCAL">
    <class>com.xxx</class>
         ...
         ...
    <properties>
           <property name="hibernate.ejb.interceptor"
                       value="com.company.demo.audit.AuditEmptyInterceptor" />           
    </properties>
    </persistence-unit>
</persistence>

Let me know your valuable thoughts/tips on this. Once again thanks for your time to read this post.

Also I have read the post Injecting JPA's Entity Manager in Hibernate's EmptyInterceptor. But looks like they are manually trying to find the bean with name to resolve and I feel there could be some other way.

Priya
  • 65
  • 1
  • 2
  • 7
  • Where do you use that `AuditEmptyInterceptor` class? Can you post the source where you (maybe) inject that interceptor? – Andrei Stefan Aug 12 '14 at 21:43
  • The AuditEmptyInterceptor is a EmptyInterceptor which is used to get a centralised call back from Hibernate for any save or update. Please see [this](http://duckranger.com/2012/12/capturing-property-changes-with-spring-jpa-and-hibernate/) and [this](http://blog.octo.com/en/audit-with-jpa-creation-and-update-date/) to understand. The source is mentioned just in the snippet above and also this gets a call back from Hibernate directly. – Priya Aug 12 '14 at 22:24
  • This is the documentation from Jboss [Hibernate] (http://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/events.html). EmptyInterceptor implements Interceptor. – Priya Aug 12 '14 at 22:44
  • For Spring database class configuration see my suggestion here: http://stackoverflow.com/questions/21801605/how-do-i-hook-up-my-hibernate-4-interceptor-in-java-configuration-for-spring-3 – Roman Yakimenko Jan 22 '15 at 11:59

1 Answers1

6

The AuditEmptyInterceptor is not a bean managed by Spring, it is instantiated by Hibernate, so you cannot inject dependencies into it.

You can employ a static delegate instead:

public class StaticDelegateInterceptor extends EmptyInterceptor {

    private static Interceptor interceptor; 

    public static void setInterceptor(Interceptor interceptor) {
        StaticDelegate.interceptor = interceptor;
    }

    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
      return StaticDelegate.interceptor.onSave(entity, id, state, propertyNames, types);
    }
    ...
}

Register the StaticDelegateInterceptor on the persistence.xml

<persistence>
    <persistence-unit name="xxx" transaction-type="RESOURCE_LOCAL">
       <class>com.xxx</class>
       ...
       ...
       <properties>
          <property name="hibernate.ejb.interceptor"
                   value="com.company.demo.audit.StaticDelegateInterceptor" />           
       </properties>
   </persistence-unit>
</persistence>

Modify your current AuditEmptyInterceptor so that it register itself with the StaticDelegateInterceptor:

@Named
@Transactional
public class AuditEmptyInterceptor extends EmptyInterceptor {  

     @PostConstruct
     public void init() {
          StaticDelagateInterceptor.setInterceptor(this);
     }
     ...
}

And finally make sure your entityManagerFactory bean depends on your auditEmptyInterceptor by setting the depends-on attribute:

<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaAdapter"
depends-on="auditEmptyInterceptor" >
...
</bean>
Ricardo Veguilla
  • 3,107
  • 1
  • 18
  • 17
  • Wow.. Thanks Ricardo.. That worked.. Thank you so much for quick help.. But it opened up another weird exception.. – Priya Aug 13 '14 at 01:00
  • Glad I could help. If the weird exception you are seeing now is related to Spring, please let me know in a comment so I can try to correct the answer, if possible. – Ricardo Veguilla Aug 13 '14 at 01:29
  • Thanks again Ricardo. The following is the exception that I am getting .. org.hibernate.AssertionFailure: null id in com.rcent.domain.worklist.WorklistStatusDO entry (don't flush the Session after an exception occurs) at org.hibernate.event.internal.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:79) at org.hibernate.event.internal.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:194) at – Priya Aug 13 '14 at 01:36
  • I got this bug before also while trying to set the audit info using PreInsertEventListener. Hence I explored this EmptyInterceptor. But looks like I am hitting this HIbernateAssertionfailure again. The exception that I am getting is similar to [this post](http://stackoverflow.com/questions/10855542/org-hibernate-assertionfailure-null-id-in-entry-dont-flush-the-session-after) – Priya Aug 13 '14 at 01:42
  • I guess hibernate does an auto flush before reading and an exception thrown while flushing. How do I know what exception is occurring ?? I even removed all the not null constrains in entity and in table. So dont know why it is complaining.. – Priya Aug 13 '14 at 01:53
  • Hmmm, I haven't seen that exception before. If I understand correctly you are trying to implement some type of auditing when an Entity is persisted, right? If that is the case, I think you should create a new separate question specifically about that, since it will probably attract more relevant answers. – Ricardo Veguilla Aug 13 '14 at 02:09
  • In any case, if you feel this answer solved the problem Spring injection issue, please mark it as 'accepted'. – Ricardo Veguilla Aug 13 '14 at 02:11
  • Thanks Ricardo. I have created another [post in stackoverflow here](https://stackoverflow.com/questions/25278583/spring-jpa-hibernate-org-hibernate-assertionfailure-null-id-in-entity-dont-fl). Please have a look and let me know your thoughts. – Priya Aug 13 '14 at 05:24
  • 1
    Can this be done wtihout the definition of the entityManagerFactory bean? I dont have it defined explicitly in my application. Thank you in advance. – radusezciuc Aug 11 '15 at 07:31
  • PS: I use the HibernateJpaAutoConfiguration from spring boot. – radusezciuc Aug 11 '15 at 08:01
  • It is possible to create an Hibernate Interceptor managed as an Spring @Bean - http://stackoverflow.com/questions/32123108/why-hibernatetransactionmanager-doesnt-autowire-spring-injections/32123517#32123517 – Alex Aug 21 '15 at 14:09