代码之家  ›  专栏  ›  技术社区  ›  slartidan

短时间断开网络连接后无法恢复连接

  •  2
  • slartidan  · 技术社区  · 9 年前

    我想在我的tomcat Web应用程序中使用JPA。因此,我创造了自己的 Filter (在web.xml中提到)。

    过滤器有三种方法:

    • 这个 init 方法创建 EntityManagerFactory 并将其存储在实例字段中(在应用程序部署时执行)
    • 这个 doFilter 方法使用存储的EntityManagerFactory创建entityManager,启动事务并在处理请求后关闭事务(和entityManager)
    • 这个 destroy 方法关闭存储的EntityManagerFactory

    该机制工作良好,速度相当快,但 一旦数据库连接丢失,应用程序就无法恢复 .

    如何刷新EntityManagerFactory,以便它创建新的数据库连接?还是应该销毁并重新创建EntityManagerFactory?我是否必须运行数据库查询来检查连接是否有效?

    我当前的筛选器是这样实现的:

    package entityManagerTest;
    
    import java.io.IOException;
    
    import javax.persistence.*;
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    
    @WebFilter(urlPatterns = { "/JpaServlet" })
    public class JpaFilter implements Filter {
    
        private EntityManagerFactory entityManagerFactory;
    
        @Override
        public void init(FilterConfig config) throws ServletException {
            entityManagerFactory = Persistence.createEntityManagerFactory("test");
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            EntityManager entityManager = entityManagerFactory .createEntityManager();
            try {
                EntityTransaction transaction = entityManager.getTransaction();
                transaction.begin();
                try {
                    request.setAttribute("entityManager", entityManager);
                    chain.doFilter(request, response);
                } finally {
                    transaction.commit();
                }
            } finally {
                entityManager.close();
            }
        }
    
        @Override
        public void destroy() {
            entityManagerFactory.close();
        }
    }
    

    servlet如下所示:

    package entityManagerTest;
    
    import java.io.*;
    import javax.persistence.*;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.*;
    
    @WebServlet(urlPatterns={"/JpaServlet"})
    public class JpaServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            EntityManager entityManager = (EntityManager) req.getAttribute("entityManager");
    
            Query query = entityManager.createNativeQuery("SELECT 42");
            Object singleResult = query.getSingleResult();
    
            PrintWriter writer = resp.getWriter();
            writer.write("DB:"+singleResult);
            writer.close();
        }
    }
    

    我的persistence.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
        <persistence-unit name="test">
            <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
            <properties>
                <property name="javax.persistence.jdbc.driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
                <property name="javax.persistence.jdbc.url" value="jdbc:sqlserver:..." />
                <property name="javax.persistence.jdbc.user" value="x" />
                <property name="javax.persistence.jdbc.password" value="y" />
            </properties>
        </persistence-unit>
    </persistence>
    

    我尝试添加 验证查询 ,但未发现任何行为变化:

    <property name="hibernate.connection.validationQuery" value="select 1"/>
    <property name="connection.validationQuery" value="select 1"/>
    

    当连接丢失时,我会得到一个回滚异常(不是意外):

    Aug 18, 2015 11:41:30 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
    WARN: SQL Error: 0, SQLState: 08S01
    Aug 18, 2015 11:41:30 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
    ERROR: Software caused connection abort: recv failed
    Aug 18, 2015 11:41:30 AM org.apache.catalina.core.StandardWrapperValve invoke
    SCHWERWIEGEND: Servlet.service() for servlet [entityManagerTest.JpaServlet] in context with path [/EntityManagerTests] threw exception
    org.hibernate.TransactionException: rollback failed
        at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.rollback(AbstractTransactionImpl.java:217)
        at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:73)
        at entityManagerTest.JpaFilter.doFilter(JpaFilter.java:29)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
        at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1517)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1474)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)
    Caused by: org.hibernate.TransactionException: unable to rollback against JDBC connection
        at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doRollback(JdbcTransaction.java:167)
        at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.rollback(AbstractTransactionImpl.java:211)
        ... 21 more
    Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Die Verbindung wurde geschlossen.
        at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:190)
        at com.microsoft.sqlserver.jdbc.SQLServerConnection.checkClosed(SQLServerConnection.java:389)
        at com.microsoft.sqlserver.jdbc.SQLServerConnection.rollback(SQLServerConnection.java:1955)
        at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doRollback(JdbcTransaction.java:163)
        ... 22 more
    

    但在网络连接再次恢复后,代码仍然失败,出现异常( 即使现在可以恢复连接 ):

    Aug 18, 2015 11:42:54 AM org.apache.catalina.core.StandardWrapperValve invoke
    SCHWERWIEGEND: Servlet.service() for servlet [entityManagerTest.JpaServlet] in context with path [/EntityManagerTests] threw exception
    javax.persistence.PersistenceException: org.hibernate.TransactionException: JDBC begin transaction failed: 
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1763)
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
        at org.hibernate.jpa.spi.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:1771)
        at org.hibernate.jpa.internal.TransactionImpl.begin(TransactionImpl.java:64)
        at entityManagerTest.JpaFilter.doFilter(JpaFilter.java:24)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
        at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:518)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1091)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:668)
        at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1517)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1474)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)
    Caused by: org.hibernate.TransactionException: JDBC begin transaction failed: 
        at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:76)
        at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.begin(AbstractTransactionImpl.java:162)
        at org.hibernate.internal.SessionImpl.beginTransaction(SessionImpl.java:1471)
        at org.hibernate.jpa.internal.TransactionImpl.begin(TransactionImpl.java:61)
        ... 20 more
    Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: Die Verbindung wurde geschlossen.
        at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:190)
        at com.microsoft.sqlserver.jdbc.SQLServerConnection.checkClosed(SQLServerConnection.java:389)
        at com.microsoft.sqlserver.jdbc.SQLServerConnection.getAutoCommit(SQLServerConnection.java:1910)
        at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doBegin(JdbcTransaction.java:68)
        ... 23 more
    
    1 回复  |  直到 9 年前
        1
  •  4
  •   slartidan    9 年前

    摘录自 the official hibernate documentation :

    然而,Hibernate自己的连接池算法 初级的它旨在帮助您入门 不 用于生产系统 ,甚至是性能 测试。[…]例如,您可能希望使用c3p0。

    必须指定连接池数据源。连接池通常在使用连接之前检查连接的有效性。下面是连接池的C3P0(连接池数据源)和Hibernate配置的示例。我希望这对你有用。

    <?xml version="1.0" encoding="UTF-8"?>
    
    <persistence xmlns="http://java.sun.com/xml/ns/persistence"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
        version="2.0">
        <persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
            <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    
            <properties>
                <property name="hibernate.dialect" value="org.hibernate.dialect.SQLServerDialect" />
                <property name="hibernate.show_sql" value="true" />
                <property name="hibernate.format_sql" value="true" />
                <property name="hibernate.hbm2ddl.auto" value="update"/>
    
                <!-- I am not exactly sure that which one is correct -->
                <!-- javax.persistence.* or hibernate.connection.* properties -->
                <property name="hibernate.connection.driver_class" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
                <property name="hibernate.connection.url" value="jdbc:sqlserver:..."/>
                <property name="hibernate.connection.username" value="x"/>
                <property name="hibernate.connection.password" value="y"/>
    
    
                <!-- Connection Pooling settings -->
                <property name="hibernate.connection.provider_class" value="org.hibernate.service.jdbc.connections.internal.C3P0ConnectionProvider" />
                <property name="hibernate.c3p0.max_size" value="100" />
                <property name="hibernate.c3p0.min_size" value="0" />
                <property name="hibernate.c3p0.acquire_increment" value="1" />
                <property name="hibernate.c3p0.idle_test_period" value="300" />
                <property name="hibernate.c3p0.max_statements" value="0" />
                <property name="hibernate.c3p0.timeout" value="100" />
                <property name="hibernate.c3p0.preferredTestQuery">SELECT 1;</property>
    
            </properties>
        </persistence-unit>
    </persistence>
    

    此外,还需要将hibernate-c3p0添加到类路径中。

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>${hibernate.version}</version>
        </dependency>