Java 使用 EntityManager 关闭 JPA 连接

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19848794/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 20:40:38  来源:igfitidea点击:

JPA connection closed with EntityManager

javaspringhibernatejpaentitymanager

提问by user2966960

Please excuse the possible improper formatting of this post as I have never posted here before. I have a Spring MVC web service running that calls into several DAO's that use JPA/hibernate to interact with a SQL Server database. The connection to the database seems to get closed after the web service has been running for a while.

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

An example of one of the DAO classes is the following:

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

The entityManagerFactory is injected via spring in the following way:

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>

This all works fine when I first bring up the application. The application server is Tomcat. But, after the application is running for a while, requests to the server start receiving this error:

当我第一次启动应用程序时,这一切正常。应用服务器是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.SQLServerConnectionConnectionCommand.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.SQLServerConnectionConnectionCommand.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)

So, the connection is being closed. Obviously something is wrong with the implementation whether it be the injection method or the method for handling errors, etc. So, the question is - What is the proper way to do this? How can I avoid the connection closed error? Thanks much in advance.

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

回答by Angular University

I think what could be happening is that some of the DAO's are leaving database transactions open when an error occurs.

我认为可能发生的情况是,某些 DAO 在发生错误时将数据库事务保持打开状态。

The example DAO above is doing programmatic management of the database transaction, but does not handle the rollback scenario. This is an example of a DAO implementation that would do so:

上面的示例 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();
       }
    }
}

All DAOs that do programmatic transaction management should use this pattern, otherwise sooner or later a transaction will not be rolled back and this could cause problems.

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

A much more frequent and less error prone way of implementing DAOs is to inject directly only an entity manager:

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

MyService {

   @PersistenceContext
   private EntityManager em;

   @Autowired
   private MyDaoA myDaoA;

   @Autowired
   private MyDaoB myDaoB;

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

Injecting the EntityManagerFactory and doing manual transaction management should be reserved for exceptional cases, and the use of @Transactional be used by default.

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

回答by Jayasagar

I would say problem is with Connection pooling, so you can use c3p0connection pooling library for fixing your problem, but you should be very carefull with connection pool properties.

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

Actually lot of ways you can do, I am giving you same code for reference. It might help.

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

Step 1: persistence.xml

第 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" />

Step 2: And then we create c3p0.propertiesunder src/main/resources/META-INF with following properties

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

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

Step 3: You need to configure data source

第三步:需要配置数据源

<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/>