Question

I need to inject EntityManager in EntityListener class so that I can perform CRUD operation on it.

POJO:

@Entity
@EntityListner(AuditLogging.class)
class User
{
      //Getter / setter of properties
}

AuditLogging (Listner class)

public class AuditInterceptor
{

  @PersistenceContext
  EntityManager entityManager;

  public void setEntityManager(EntityManager entityManager)
  {
    this.entityManager = entityManager;
  }

  @PrePersist
  public void prePersist(Object obj)
  {
     // Here I want to use ENTITY manager object so that I can perform CRUD operation
     // with prePersist coming object.

      entityManager.unwrap(Session.class).save(obj);

     // But I am getting NULL POINTER EXCEPTION for entity manager object 
   }

}

JDBC-CONFIg.xml

<bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
        <property name="packagesToScan" value="com.XXXXX.entity" />
        <property name="jpaProperties">
    </bean>

<!-- Datasource -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="${jdbc.driver.classname}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
    </bean>


<!-- transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

EntityListener is not managed by any of the container.EntityListeners are instanciated by JPA, so Spring does not have an opportunity to inject EntityManager. My question is, how we can inject inject EntityManager in EntityListener class so that I can perform CRUD operation on it ???

Was it helpful?

Solution 2

Anyways, I got this done by getting entityManager reference from EntityManagerFactory bean which is configured in my jdbc-config.xml. But again this is not what I wanted. I wanted to work around with @PersistenceContext.

  @Autowired
  EntityManagerFactory entityManagerFactory;

  private static EntityManager entityManager;

  public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
    entityManager=entityManagerFactory.createEntityManager();
    this.entityManagerFactory = entityManagerFactory;
  }

Here are few notes that we need to keep in mind:

  1. We can't inject an EntityManager into an EntityListener (through @PersistenceContext). EntityListener is not managed by any of the containers
  2. @PersistenceContext class cannot be static. So we cant attain the instance while class loading.
  3. EntityListeners are instantiated by JPA, so Spring does not have an opportunity to inject EntityManager

OTHER TIPS

I have faced a similar problem where I was trying to create history records for an entity using EntityListeners.

In order to resolve this problem, I have created utility class BeanUtil with a static method to get the bean and used this util class to get bean inside my Entitylistener class

@Service
public class BeanUtil implements ApplicationContextAware {

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static <T> T getBean(Class<T> beanClass) {
        return context.getBean(beanClass);
    }

}

Now we can call BeanUtil.getBean() to get the bean of any type

public class FileEntityListener {

    @PrePersist
    public void prePersist(File target) {
        perform(target, INSERTED);
    }

    @Transactional(MANDATORY)
    private void perform(File target, Action action) {
        EntityManager entityManager = BeanUtil.getBean(EntityManager.class);
        entityManager.persist(new FileHistory(target, action));
    }

}

We can use this BeanUtil class to get any spring managed bean from anywhere, To know more you can read my article JPA Auditing: Persisting Audit Logs Automatically using EntityListeners.

Well, the first solution which came into my mind is a little "hack", but should work.

    public class AuditInterceptor {

        static setEntityManager emf; 

        @Autowired
        public void setEntityManagerFactory(EntityManager emf) {
            AuditInterceptor.emf = emf;
        }

        @PrePersist
        public void prePersist(Object obj) { 
            EntityManager entityManager = emf.getEntityManager();
            // Here I want to use ENTITY manager object so that I can perform CRUD operation
            // with prePersist coming object.

            entityManager.unwrap(Session.class).save(obj);

            // But I am getting NULL POINTER EXCEPTION for entity manager object 
       }
   }

Inside of your code use EntityManager entityManager = emf.getEntityManager()

Declare your AuditInterceptor as a spring bean (@Component with component-scan or define AuditorInterceptor as a bean)

I used a ThreadLocal to pass the Spring Application Context which contains EntityManager around. Though I am not sure if it is safe Is it safe to pass in the Spring Application Context into a ThreadLocal associated with a request? but so far it is working for me.

The listener can be modified to have autowiring like this. However this needs to be done on on the handlers and not the constructor (doing it on the constructor seems less predictable). You are also not limited to the EntityManager but you have access to the whole context.

@Autowired
private EntityManager entityManager;

@Autowired
private MyDao myDao;

@PrePersist
public void pre() {
  SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
  Objects.requireNotNull(myDao);
  myDao.doSomething();
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top