【问题标题】:JPA connection closed with EntityManager使用 EntityManager 关闭 JPA 连接
【发布时间】:2013-11-19 20:34:17
【问题描述】:

请原谅我之前从未在此发布过的帖子格式可能不正确。我有一个运行的 Spring MVC Web 服务,它调用几个使用 JPA/hibernate 与 SQL Server 数据库交互的 DAO。 Web 服务运行一段时间后,与数据库的连接似乎已关闭。

DAO 类之一的示例如下:

@Repository
public class OceanClientDaoImpl {

// Create entity manager objects that will talk to the database
private EntityManagerFactory entityManagerFactory;

/**
 * Load all clients in the database. Return as ClientLight objects.
 * 
 * @return List of ClientLight objects.
 */
public List<Client> loadClients() 
{
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    entityManager.getTransaction().begin();
    List<?> clients = 
            entityManager.createNativeQuery("select id, clientname, industry, logo, slug from client", 
                    Client.class).getResultList();
    entityManager.getTransaction().commit();
    entityManager.close();
    return Client.getCheckedList(clients);
}
}

entityManagerFactory 通过以下方式通过 spring 注入:

<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
    <property name="driverClassName"     value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
    <property name="url" value="url" />
    <property name="username" value="user" />
    <property name="password" value="pw" />
</bean>

<bean
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    id="entityManagerFactory">
    <property name="dataSource" ref="dataSource" />
</bean>

<context:component-scan base-package="com.theocean.dao">
    <context:include-filter type="annotation"
        expression="org.springframework.stereotype.Repository" />
</context:component-scan>

<bean class="org.springframework.orm.jpa.JpaTransactionManager"
    id="transactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<bean id="oceanClientDaoImpl" class="com.theocean.dao.OceanClientDaoImpl">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

当我第一次启动应用程序时,这一切都很好。应用服务器是Tomcat。但是,应用程序运行一段时间后,对服务器的请求开始收到此错误:

> SEVERE: Servlet.service() for servlet [spring] in context with path [/OceanWebService] threw exception [Request processing failed; nested exception is javax.persistence.PersistenceException: org.hibernate.TransactionException: JDBC begin failed: ] with root cause
com.microsoft.sqlserver.jdbc.SQLServerException: SQL Server did not return a response. The connection has been closed.
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1667)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1654)
    at com.microsoft.sqlserver.jdbc.TDSReader.readPacket(IOBuffer.java:4844)
    at com.microsoft.sqlserver.jdbc.TDSCommand.startResponse(IOBuffer.java:6154)
    at com.microsoft.sqlserver.jdbc.TDSCommand.startResponse(IOBuffer.java:6106)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection$1ConnectionCommand.doExecute(SQLServerConnection.java:1756)
    at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:5696)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:1715)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectionCommand(SQLServerConnection.java:1761)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.setAutoCommit(SQLServerConnection.java:1901)
    at sun.reflect.GeneratedMethodAccessor32.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:126)
    at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:99)
    at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:58)
    at com.sun.proxy.$Proxy32.setAutoCommit(Unknown Source)
    at org.hibernate.transaction.JDBCTransaction.begin(JDBCTransaction.java:87)
    at org.hibernate.impl.SessionImpl.beginTransaction(SessionImpl.java:1473)
    at org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:60)
    at com.theocean.dao.OceanClientDaoImpl.loadClients(OceanClientDaoImpl.java:52)
    at com.theocean.service.ExperienceClientService.loadClientsLight(ExperienceClientService.java:30)
    at com.theocean.web.ExperienceController.loadClientsLight(ExperienceController.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:436)
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:424)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:669)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:574)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:947)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1009)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
SEVERE: Servlet.service() for servlet [spring] in context with path [/OceanWebService]     threw exception [Request processing failed; nested exception is javax.persistence.PersistenceException: org.hibernate.TransactionException: JDBC begin failed: ] with root cause
com.microsoft.sqlserver.jdbc.SQLServerException: SQL Server did not return a response. The connection has been closed.
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1667)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1654)
    at com.microsoft.sqlserver.jdbc.TDSReader.readPacket(IOBuffer.java:4844)
    at com.microsoft.sqlserver.jdbc.TDSCommand.startResponse(IOBuffer.java:6154)
    at com.microsoft.sqlserver.jdbc.TDSCommand.startResponse(IOBuffer.java:6106)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection$1ConnectionCommand.doExecute(SQLServerConnection.java:1756)
    at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:5696)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:1715)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.connectionCommand(SQLServerConnection.java:1761)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.setAutoCommit(SQLServerConnection.java:1901)
    at sun.reflect.GeneratedMethodAccessor32.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:126)
    at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:99)
    at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:58)
    at com.sun.proxy.$Proxy32.setAutoCommit(Unknown Source)
    at org.hibernate.transaction.JDBCTransaction.begin(JDBCTransaction.java:87)
    at org.hibernate.impl.SessionImpl.beginTransaction(SessionImpl.java:1473)
    at org.hibernate.ejb.TransactionImpl.begin(TransactionImpl.java:60)
    at com.theocean.dao.OceanClientDaoImpl.loadClients(OceanClientDaoImpl.java:52)
    at com.theocean.service.ExperienceClientService.loadClientsLight(ExperienceClientService.java:30)
    at com.theocean.web.ExperienceController.loadClientsLight(ExperienceController.java:30)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:176)
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:436)
    at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:424)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:669)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:574)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:947)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1009)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)

所以,连接正在关闭。显然,无论是注入方法还是处理错误的方法等,实现都有问题。所以,问题是 - 这样做的正确方法是什么?如何避免连接关闭错误?非常感谢。

【问题讨论】:

  • 我建议你应该使用 JPA 的连接池。您可以在 entityManagerFactory 中指定连接池。可能在您指定 entityManagerFactory 的 jpaProperties 属性时。您必须使用某种 JPA 供应商,如 hibernate、eclipselink 或 toplink 等。每个 jpa 供应商都有不同的方式/属性来使用连接池。你需要参考他们的文档来学习如何使用连接池。

标签: java spring hibernate jpa entitymanager


【解决方案1】:

我认为可能发生的情况是一些 DAO 在发生错误时使数据库事务处于打开状态。

上面的示例 DAO 正在对数据库事务进行编程管理,但不处理回滚场景。这是一个 DAO 实现的示例:

    MyService {

   private EntityManagerFactory emf;

   public void myMethod() {
       EntityManager em = emf.createEntityManager();
       EntityTransaction tx = em.getTransaction();
       MyDaoA myDaoA = new MyDaoA(em);
       MyDaoB myDaoB = new MyDaoB(em);
       try {
           tx.begin();
           myDaoA.doSomething();
           myDaoB.doSomething();
           tx.commit();
       } catch(Exception e) {
           tx.rollback();
       }
    }
}

所有进行程序化事务管理的 DAO 都应该使用这种模式,否则事务迟早不会回滚,这可能会导致问题。

实现 DAO 的一种更频繁且不易出错的方法是直接注入一个实体管理器:

MyService {

   @PersistenceContext
   private EntityManager em;

   @Autowired
   private MyDaoA myDaoA;

   @Autowired
   private MyDaoB myDaoB;

   @Transactional
   public void myMethod() {
      myDaoA.doSomething();
      myDaoB.doSomething();
    }
}

注入EntityManagerFactory并进行手动事务管理应保留在特殊情况下,默认使用@Transactional。

【讨论】:

  • 我认为 entityManager 应该只存在于单个请求的生命周期中。如果我以这种方式注入 entityManager,它不会在应用程序的整个生命周期内都存在吗?
  • spring 注入的 PersistenceContext 实际上不是实体管理器本身,而是实体管理器代理。如果代理配置了 PersistenceContext(type=PersistenceContextType.EXTENDED) 那么你有你提到的行为。但是正常的默认情况是 PersistenceContextType.TRANSACTION,这将为每个事务提供一个不同的实体管理器。这种机制是透明的,如果您使用@Transactional,您始终可以确保获得正确的实体管理器。
  • 我编辑了我的帖子,原因是我认为这是相关的,似乎发生的事情是由于 DAO 的编写方式(如果出错则不处理回滚,手动事务处理)一些的 DAO 使交易保持打开状态,并且连接被泄露。因此增加连接池大小或切换到另一个连接池(如 c3Po)只会掩盖问题的真正原因。希望这会有所帮助。
  • 我将其更改为使用 PersistenceContext 将 entityManager 注入 DAO,但不幸的是问题仍然发生。我启动服务,等待 20 分钟,发出请求并获得连接关闭异常。
  • 根据堆栈跟踪,有一个对 OceanClientDaoImpl 的调用,它触发了对 SessionImpl.beginTransaction 的调用,它调用驱动程序向 SQL 服务器发送命令并通过 TDSReader.readPacket 等待回复根本原因是“SQL Server 没有返回响应”。所以服务器端发生了一些事情,导致服务器没有回复。一种解释是,这是由于外部因素导致的 SQL 服务器上的操作问题。一个方向是与 DBA 核对是否确实在服务器上打开了许多连接以及由谁打开。
【解决方案2】:

我想说问题出在Connection pooling,所以你可以使用c3p0连接池库来解决你的问题,但是你应该非常小心连接池properties

其实你可以做很多方法,我给你同样的代码供你参考。它可能会有所帮助。

第 1 步:persistence.xml

<!-- c3p0 properties -->
        <!-- Determines how many connections at a time c3p0 will try to acquire when the pool is exhausted -->
        <property name="hibernate.c3p0.acquire_increment" value="1"/>
        <!-- If this is a number greater than 0, 
        c3p0 will test all idle, pooled but unchecked-out connections, every this number of seconds -->
        <property name="hibernate.c3p0.idle_test_period" value="60"/>

        <property name="hibernate.c3p0.max_size" value="20"/>
        <property name="hibernate.c3p0.max_statements" value="50"/>
        <property name="hibernate.c3p0.min_size" value="5"/>
        <property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider" />

第 2 步:然后我们在 src/main/resources/META-INF 下创建 c3p0.properties 并具有以下属性

c3p0.testConnectionOnCheckout=true
c3p0.preferredTestQuery=SELECT 1;

第三步:需要配置数据源

<bean id="myJdbcDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
  destroy-method="close">
 <!-- Connection properties -->
 <property name="driverClass" value="$DS{database.class}" />
 <property name="jdbcUrl" value="$DS{database.url}" />
 <property name="user" value="$DS{database.username}" />
 <property name="password" value="$DS{database.password}" />
<bean/>

【讨论】:

    猜你喜欢
    • 2012-04-12
    • 2012-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-21
    • 2015-09-24
    相关资源
    最近更新 更多