如何在 EntityListeners 中注入 EntityManager

Posted

技术标签:

【中文标题】如何在 EntityListeners 中注入 EntityManager【英文标题】:How to inject EntityManager in EntityListeners 【发布时间】:2014-04-05 22:40:35 【问题描述】:

我需要在 EntityListener 类中注入 EntityManager,以便我可以对其执行 CRUD 操作。

POJO:

@Entity
@EntityListner(AuditLogging.class)
class User

      //Getter / setter of properties

AuditLogging(Listner 类)

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 不由任何容器管理。EntityListener 由 JPA 实例化,因此 Spring 没有机会注入 EntityManager。 我的问题是,我们如何在 EntityListener 类中注入 EntityManager 以便我可以对其执行 CRUD 操作???

【问题讨论】:

【参考方案1】:

我在尝试使用 EntityListeners 为实体创建历史记录时遇到了类似的问题。

为了解决这个问题,我用静态方法创建了实用程序类BeanUtil 来获取 bean,并使用这个实用程序类在我的 Entitylistener 类中获取 bean

@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);
    


现在我们可以调用 BeanUtil.getBean() 来获取任意类型的 bean

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));
    


我们可以使用这个BeanUtil 类从任何地方获取任何spring 托管bean,要了解更多信息,您可以阅读我的文章JPA Auditing: Persisting Audit Logs Automatically using EntityListeners。

【讨论】:

【参考方案2】:

无论如何,我通过从 EntityManagerFactory bean 中获取 entityManager 引用来完成此操作,该 bean 在我的 jdbc-config.xml 中配置。但这又不是我想要的。我想和@PersistenceContext一起工作。

  @Autowired
  EntityManagerFactory entityManagerFactory;

  private static EntityManager entityManager;

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

我们需要牢记以下几点:

    我们不能将EntityManager 注入EntityListener(通过 @PersistenceContext)。 EntityListener 不受任何 容器 @PersistenceContext 类不能是静态的。所以我们不能 在类加载时获取实例。 EntityListener 是 由 JPA 实例化,因此 Spring 没有机会 注入EntityManager

【讨论】:

【参考方案3】:

嗯,我想到的第一个解决方案有点“hack”,但应该可以。

    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 
       
   

在您的代码内部使用EntityManager entityManager = emf.getEntityManager()

将您的 AuditInterceptor 声明为 spring bean(@Component 使用组件扫描或将 AuditorInterceptor 定义为 bean)

【讨论】:

@PersistenceContext 不能变成静态的。 我的示例不再使用@PersistenceContext。谷歌也把我带到了这个post【参考方案4】:

我使用 ThreadLocal 来传递包含 EntityManager 的 Spring 应用程序上下文。虽然我不确定它是否安全 Is it safe to pass in the Spring Application Context into a ThreadLocal associated with a request? 但到目前为止它对我有用。

【讨论】:

以上是关于如何在 EntityListeners 中注入 EntityManager的主要内容,如果未能解决你的问题,请参考以下文章

JPA实体监听器-@EntityListeners

JPA实体监听器-@EntityListeners

JPA实体监听器-@EntityListeners

java.lang.ClassNotFoundException: javax.persistence.EntityListeners

如何将 DLL 注入 Adob​​e Reader X

Caused by: java.lang.ClassNotFoundException: javax.persistence.EntityListeners