Tomcat + Jersey + MySQL连接关闭后不允许任何操作

时间:2015-11-21 19:56:34

标签: java mysql tomcat jdbc

我正在实现一个部署在Tomcat 8.0.28上的aplication,它连接到MySQL数据库。此应用程序充当REST API,并且每次必须在数据库中存储或检索新数据时都会调用该API。我使用泽西进行数据库处理。

问题是,过了一段时间没有打电话给这个应用程序(让我们说一天),我不能再打电话给我的终点了。出现一条消息,指示连接已过期。

这些是我的server.xml和我的数据库连接(我有两个与DB连接的端点:一个用于检索数据,另一个用于插入):

DatabaseConnection.java

@Singleton
public class DatabaseConnection {
private DataSource dataSource;
private Connection conn;
private static final Logger LOGGER = Logger.getLogger(DatabaseConnection.class);

private static final DatabaseConnection INSTANCE = new DatabaseConnection();

private DatabaseConnection() {
    if (INSTANCE != null) {
        throw new IllegalStateException("Already instantiated");
    }
}

public static DatabaseConnection getInstance() {
    return INSTANCE;
}

private void prepareConnection() {
    try {
        Context initialContext = new InitialContext();
        Context environmentContext = (Context) initialContext
                .lookup("java:comp/env");
        String dataResourceName = "jdbc/DBNAME";
        dataSource = (DataSource) environmentContext
                .lookup(dataResourceName);
    } catch (NamingException e) {
        LOGGER.error("Error in DatabaseConnection ", e);
        e.printStackTrace();
    }
}

public Object[][] executeQuery(String query) {
    prepareConnection();
    try (Connection connection = dataSource.getConnection();
            Statement stmt = connection.createStatement();
            ResultSet rs = stmt.executeQuery(query)) {
            return getResult(rs);               
    } catch (SQLException e) {
        LOGGER.error("Error in DatabaseConnection", e);
        e.printStackTrace();
    }
    return null;
}

public Object[][] getResult(ResultSet rs) throws SQLException{
      //Transforms the resultset into a bidimiensional array
}

public Integer executeUpdate(String query) {
    return executeUpdate(query, false);
}

public Integer executeUpdate(String query, boolean returnKeys) {
    Integer result = null;
    prepareConnection();
    try (Connection connection = dataSource.getConnection();
            Statement stmt = connection.createStatement()) {
        if (returnKeys) {
            stmt.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
            try (ResultSet rs = stmt.getGeneratedKeys()) {
                rs.next();
                result = rs.getInt(1);
            }
        } else {
            stmt.executeUpdate(query);
        }
    } catch (SQLException e) {
        LOGGER.error("Error in DatabaseConnection", e);
        e.printStackTrace();
    }
    return result;
}

}

server.xml中

 <Resource name="jdbc/DBNAME" 
          global="jdbc/DBNAME"
          factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
          auth="Container"
          type="javax.sql.DataSource"              
          username=USER
          password=PASSWORD
          driverClassName="com.mysql.jdbc.Driver"
          description="MYAPP Database"
          url="jdbc:mysql://localhost:3306/DBNAME"
          maxTotal="10"
          maxIdle="10"
          maxWaitMillis="10000"

          autoReconnect="true"    
          defaultAutoCommit="true" />
        <!--removeAbandonedTimeout="300"
          removeAbandoned="true"    -->

在server.xml中注释掉最后两行没有区别(我已经尝试删除所有已放弃的连接)。我得到的例外情况如下。

2015-11-21 18:10:00,362 [http-nio-3303-exec-38] ERROR com.myapp.server.persistence.DatabaseConnection - Error in DatabaseConnection
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
at com.mysql.jdbc.Util.getInstance(Util.java:387)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:919)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:898)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:887)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:862)
at com.mysql.jdbc.ConnectionImpl.throwConnectionClosedException(ConnectionImpl.java:1237)
at com.mysql.jdbc.ConnectionImpl.checkClosed(ConnectionImpl.java:1232)
at com.mysql.jdbc.ConnectionImpl.createStatement(ConnectionImpl.java:2437)
at com.mysql.jdbc.ConnectionImpl.createStatement(ConnectionImpl.java:2421)
at sun.reflect.GeneratedMethodAccessor56.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:126)
at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:108)
at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:81)
at com.sun.proxy.$Proxy58.createStatement(Unknown Source)
at com.myapp.server.persistence.DatabaseConnection.executeQuery(DatabaseConnection.java:56)

Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 90,165,721 milliseconds ago.  The last packet sent successfully to the server was 90,165,721 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:983)
at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3644)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2452)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2617)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2546)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2504)
at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1370)
at com.myapp.server.persistence.DatabaseConnection.executeQuery(DatabaseConnection.java:57)
... 39 more

Caused by: java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:113)
at java.net.SocketOutputStream.write(SocketOutputStream.java:159)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3626)
... 50 more

我在互联网上搜索,在使用try-with-paramters构造调用DB后,我创建的连接已正确关闭。为什么我在一段时间后无法再次呼叫连接?我怎么能跳过这个问题?

1 个答案:

答案 0 :(得分:1)

您的连接在池中闲置的时间太长,并且在您使用它们时会被MySQL丢弃而导致错误

您可以告诉池在将它们提供给应用程序之前测试连接

<Resource name="jdbc/DBNAME" 
          global="jdbc/DBNAME"
          factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
          auth="Container"
          type="javax.sql.DataSource"              
          username=USER
          password=PASSWORD
          driverClassName="com.mysql.jdbc.Driver"
          description="MYAPP Database"
          url="jdbc:mysql://localhost:3306/DBNAME"
          maxTotal="10"
          maxIdle="10"
          maxWaitMillis="10000"

          autoReconnect="true"    
          defaultAutoCommit="true"
          testOnBorrow="true" 
          validationQuery="select 1" />