How to inject EntityManager in EntityListeners
Asked Answered
T

5

8

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 ???

Tearing answered 4/3, 2014 at 11:53 Comment(0)
T
5

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
Tearing answered 5/3, 2014 at 8:0 Comment(0)
S
13

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.

Sanctuary answered 14/2, 2017 at 9:40 Comment(0)
T
5

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
Tearing answered 5/3, 2014 at 8:0 Comment(0)
R
0

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)

Rale answered 4/3, 2014 at 22:7 Comment(2)
@PersistenceContext CANNOT become STATIC.Tearing
My example does not use @PersistenceContext anymore. Google also leads me to this postRale
M
0

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.

Memoried answered 9/8, 2020 at 3:48 Comment(0)
M
0

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();
}
Memoried answered 2/2, 2023 at 14:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.