Priya
Priya

Reputation: 65

Spring JPA/Hibernate EmptyInterceptor not injecting Entitymanager/Spring beans

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.

Upvotes: 3

Views: 7628

Answers (1)

Ricardo Veguilla
Ricardo Veguilla

Reputation: 3155

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>

Upvotes: 6

Related Questions