0

我在一个使用 JavaEE 的项目中工作。我使用 Glassfish 服务器版本 3。我在使用 EntityManager 实例的单例 EJB 中经常遇到问题(并非总是如此)。很多时候,我得到这个错误:

[timestamp] [http-thread-pool-8080(49)] ERROR com.sun.xml.ws.server.sei.TieHandler.serializeResponse Attempting to execute an operation on a closed EntityManager.
java.lang.IllegalStateException: Attempting to execute an operation on a closed EntityManager.
 at org.eclipse.persistence.internal.jpa.EntityManagerImpl.verifyOpen(EntityManagerImpl.java:1662) ~[org.eclipse.persistence.jpa.jar:2.3.4.v20130626-0ab9c4c]
 at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:643) ~[org.eclipse.persistence.jpa.jar:2.3.4.v20130626-0ab9c4c]
 at org.eclipse.persistence.internal.jpa.EntityManagerImpl.find(EntityManagerImpl.java:532) ~[org.eclipse.persistence.jpa.jar:2.3.4.v20130626-0ab9c4c]
 at com.sun.enterprise.container.common.impl.EntityManagerWrapper.find(EntityManagerWrapper.java:320) ~[container-common.jar:3.1.2.1]

日志还在继续,但我只是展示了它的顶部。日志的下一行是对部署在同一服务器中的 WebService 的调用。这个错误发生时,它总是源于对部署在同一服务器中的 WebService 的调用,该服务器使用 entityManager 实例中的“find”方法在数据库中执行查找。

实体管理器被注入到 @WebService 注释类的 @PostConstruct 中的 bean 之外,使用行 '(EntityManager)new InitialContext().lookup("java:comp/env/persistence/etc");' 这是接收所有传入请求并根据请求决定应该调用哪个 bean 的类。

收到请求后,此类立即根据请求调用相应的单例 bean,将注入的 EntityManager 传递给相应的 bean。

我知道当我尝试执行操作时 EntityManager 已关闭,这确实是问题所在。但是,我认为 EntityManager 的这种打开和关闭是自动管理的。显然它不是那样工作的。我也没有直接在代码中的任何位置关闭 EntityManager。

我没有看到任何合理的解决方案来解决这个问题。我在在线资源中发现的只是它可能是一个 Glassfish 错误,并且重新启动服务器通常可以工作。没有什么具体可以解决问题。

我正在使用的 persistence.xml 文件中配置的 PersistenceUnit 中的一些信息如下所示。

<persistence-unit> name="XXX" transaction-type="JTA"
<provider>org.eclipse.persistence.jpa.PersistenceProvider></provider>
<jta-data-source>jdbc/YYY</jta-data-source>
<properties>
            <property name="eclipselink.target-database" value="Oracle"/>
            <property name="eclipselink.cache.shared.default" value="false"/>
            <property name="eclipselink.cache.size.default" value="0"/>
            <property name="eclipselink.cache.type.default" value="None"/>
            <property name="eclipselink.weaving.internal" value="false"/>
            <property name="toplink.target-database" value="Oracle"/>
            <property name="eclipselink.session.customizer"                       
            value="aaa.bbb.ccc.IsolateEmbeddablesCustomizer"/>
 </properties>
 <exclude-unlisted-classes>true</exclude-unlisted-classes>
 </persistence-unit>

您对如何解决此问题有任何想法吗?你们中的任何人是否也遇到过同样的错误?

谢谢你。

4

1 回答 1

0

我认为问题就在这里:

实体管理器被注入到 @WebService 注释类的 @PostConstruct 中的 bean 之外,使用行 '(EntityManager)new InitialContext().lookup("java:comp/env/persistence/etc");' ...

容器EntityManager从它维护的某个池中提供 s。EM 有一定的生命周期,它们可能会在某个时间点失效。通常(稍后解释)这不是问题,因为通常 EM 是以托管方式注入的。

但是您将其初始化@PostConstruct为某个变量,并且当该变量指向的 EM 无效时,它不会重新初始化,因为它不是由容器按预期方式管理的。

我认为您可以通过检查 EM 是否无效并在引用无效时再次查找来解决此问题。我强调:这不是正确的方法。

将注入的 EntityManager 传递给相应的 bean。

不要通过 EM。在使用它的 bean 中初始化 EM。通过唯一的实例,您将不会节省任何费用。相反,它会使性能变差。让容器处理创建 EM 的优化。

所以,通常你会在你的 bean 中做这样的事情(不是通过 EM,而是在 bean 上管理它):

@PersistenceContext(unitName="XXX") // "unitName" might not be needed
private EntityManeger em;           // if you use only one persistence unit

使 EM 得到正确管理。这样容器确保它始终有效。

如果您已经正确构建了让容器初始化的bean,例如将@Inject它们设置为您的,@WebService这应该是可能的。

如果由于某种原因不可能,那么您仍然可以在 bean 中进行 JNDI 查找,但是我对任何 JTA 事务的功能都持怀疑态度。

于 2018-11-09T14:33:24.833 回答