【问题标题】:How can I add an entity listener to a JPA (EclipseLink) entity without an active session?如何在没有活动会话的情况下将实体侦听器添加到 JPA (EclipseLink) 实体?
【发布时间】:2014-10-03 04:33:36
【问题描述】:

我正在尝试在 spring bean 中执行以下操作:

@PostConstruct
public void registerTorchEntityListeners()
{
    Session session = entityManager.unwrap(Session.class);
    for (EntityType<?> entity : entityManager.getMetamodel().getEntities())
    {
        if (entity.getJavaType().isAnnotationPresent(TorchEntityListeners.class))
        {
             TorchEntityListeners annotation = (TorchEntityListeners) entity.getJavaType().getAnnotation(TorchEntityListeners.class);
             for (Class listenerClass : annotation.value())
             {
                 Map<String, DescriptorEventListener> map = applicationContext.getBeansOfType(listenerClass);
                 for (DescriptorEventListener listenerBean : map.values())
                 {
                     session.getClassDescriptor(entity.getClass()).getEventManager().addListener(listenerBean);
                 }
             }
        }
    }

}

问题是我得到以下异常,因为(我认为)我不在事务中,因此没有可用于获取 ClassDescriptor 的会话,以便我可以将侦听器添加到特定实体:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'torchEntityListenerConfigurer': Invocation of init method failed; nested exception is java.lang.IllegalStateException: No transactional EntityManager available
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:133)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:396)

基本上我正在尝试做与此等效的 EclipseLink:http://invariantproperties.com/2013/09/29/spring-injected-beans-in-jpa-entitylisteners/。我宁愿用监听器来注释实体,而不是做这样的事情:Injecting a Spring dependency into a JPA EntityListener

想法?

【问题讨论】:

  • 你目前如何在你的 bean 中注入 EntityManager
  • 它与:@PersistenceContext 是我的问题......是的,我知道我没有展示那部分......但我没有意识到这是这个问题。

标签: spring eclipselink jpa-2.0


【解决方案1】:

事实上,你可以在不获取EntityManager的情况下进行注册,因为你只使用它来获取客户端会话,并且你只使用客户端会话向服务器会话注册侦听器无需与数据库进行任何直接交互 也不改变任何对象。

EntityManagerFactory实际上是一个EntityManagerFactoryImpl,可以直接暴露ServerSessionunwrap。我不得不深入研究明确标记为内部的类,才能找到这一点,ServerSession(也标记为内部)在他的 javadoc 中明确指出:对对象和数据库的所有更改都必须通过一个工作单元完成从客户端会话中获取,这允许更改发生在事务对象空间和独占数据库连接下

但是对于您的用例,我认为这样使用它是正确的,仅使用服务器会话来访问实际上包含类描述符并且是 EclipseLink 中的公共类的Project

public void registerTorchEntityListeners()
{
    // no entityManager here and Session is only used to get access to Project
    Project proj = entityManagerFactory.unwrap(Session.class).getProject();
    for (EntityType<?> entity : entityManagerFactory.getMetamodel().getEntities())
    {
        if (entity.getJavaType().isAnnotationPresent(SpringEntityListeners.class))
        {
             SpringEntityListeners annotation = (SpringEntityListeners) entity.getJavaType().getAnnotation(SpringEntityListeners.class);
             for (Class listenerClass : annotation.value())
             {
                 Map<String, DescriptorEventListener> map = applicationContext.getBeansOfType(listenerClass);
                 for (DescriptorEventListener listenerBean : map.values())
                 {
                     ClassDescriptor classDescriptor = proj.getClassDescriptor(entity.getJavaType());
                     if (null != classDescriptor)
                     {
                         classDescriptor.getEventManager().addListener(listenerBean);
                     }
                 }
             }
        }
    }
}

【讨论】:

  • 抱歉花了这么长时间才回复这个问题。感谢您的帮助。
【解决方案2】:

当然,我在添加赏金后 30 分钟就知道了 :)

我终于通过从 EntityManagerFactory 中的有线获取 entityManager 而不是使用:@PersistenceContext 将其注入 TorchEntityListenerConfigurer 来实现这一点

这是可行的解决方案……效果很好!

这里是配置:

<bean id="approvalEntityListener" class="com.prometheus.torchlms.core.activity.approval.ApprovalEntityListener">
    <property name="activityRepository" ref="activityRepository" />
    <property name="notificationFactory" ref="notificationFactory" />
    <property name="notificationService" ref="notificationService" />
</bean>
<bean id="springEntityListenerConfigurer" class="com.prometheus.torchlms.core.SpringEntityListenerConfigurer">
    <constructor-arg ref="entityManagerFactory" />
</bean>

这就是魔法发生的地方(如果这对某人有用):

public class SpringEntityListenerConfigurer implements ApplicationContextAware
{
    private ApplicationContext applicationContext;
    private EntityManagerFactory entityManagerFactory;

    public SpringEntityListenerConfigurer(EntityManagerFactory entityManagerFactory)
    {
        this.entityManagerFactory = entityManagerFactory;
    }

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

    @PostConstruct
    public void registerTorchEntityListeners()
    {
        //entityManager.
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        Session session = entityManager.unwrap(Session.class);
        for (EntityType<?> entity : entityManagerFactory.getMetamodel().getEntities())
        {
            if (entity.getJavaType().isAnnotationPresent(SpringEntityListeners.class))
            {
                 SpringEntityListeners annotation = (SpringEntityListeners) entity.getJavaType().getAnnotation(SpringEntityListeners.class);
                 for (Class listenerClass : annotation.value())
                 {
                     Map<String, DescriptorEventListener> map = applicationContext.getBeansOfType(listenerClass);
                     for (DescriptorEventListener listenerBean : map.values())
                     {
                         ClassDescriptor classDescriptor = session.getClassDescriptor(entity.getJavaType());
                         if (null != classDescriptor)
                         {
                             classDescriptor.getEventManager().addListener(listenerBean);
                         }
                     }
                 }
            }
        }

    }
}

所以现在我可以将我的@SpringEntityListeners({ApprovalEntityListener.class}) 添加到我想要的任何实体中,并使用我想要的任何侦听器,这些侦听器可以是 spring bean!

如果有帮助,这里的注释:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SpringEntityListeners
{
    Class<?>[] value();
}

【讨论】:

  • 只是一个补充。你不需要得到EntityManager:你可以简单地写for (EntityType&lt;?&gt; entity : entityManagerFactory.getMetamodel().getEntities())
  • @SergeBallesta 很好的反馈。我会更新答案。我仍然需要 EntityManager 来获取会话,以便获取 ClassDescriptor。你知道另一种方法来控制它吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-26
  • 1970-01-01
  • 2018-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多