【问题标题】:How to inject EntityManager in EntityListeners如何在 EntityListeners 中注入 EntityManager
【发布时间】: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 操作???

【问题讨论】:

    标签: java spring hibernate spring-mvc spring-data-jpa


    【解决方案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;
        }
      

      我们需要牢记以下几点:

      1. 我们不能将EntityManager 注入EntityListener(通过 @PersistenceContext)。 EntityListener 不受任何 容器
      2. @PersistenceContext 类不能是静态的。所以我们不能 在类加载时获取实例。
      3. 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? 但到目前为止它对我有用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-05-15
          • 2012-05-24
          • 1970-01-01
          • 1970-01-01
          • 2015-04-17
          • 1970-01-01
          • 1970-01-01
          • 2011-05-07
          相关资源
          最近更新 更多