数据库连接在一段时间后停止,没有明显的原因

时间:2014-09-06 10:29:30

标签: java mysql database tomcat jdbc

我几天前部署了我的第一个Java Web应用程序,并意识到发生了一件奇怪的事情。一段时间后,所有依赖于我的数据库连接的动态内容和功能(推荐提交,管理员登录)都停止了工作。似乎这种情况每24小时左右就会发生一次。每天早上我都意识到它还没有再起作用。

我通过访问Tomcat Web应用程序管理器并单击“#34; reload"”来解决此问题。在有问题的网络应用程序上。网站的动态功能立即再次运作。

我的服务器正在运行Tomcat 7和MySQL,并且网络应用使用JDBC驱动程序建立与数据库的连接。我没有对Apache或Tomcat设置进行任何更改。

我还有其他用PHP编写的Web应用程序,这些应用程序可以持续运行而且没有错误,这似乎是这个Java Web应用程序存在这个问题。

导致这种情况发生的原因是什么?我怎样才能实现这一点,以便在再次建立数据库连接之前不需要重新加载Web应用程序?

编辑:附加一些数据库连接代码

数据库连接

public class DBConnection {
    private static Connection conn; 
    private static final Configuration conf     = new Configuration();
    private static final String dbDriver        = conf.getDbDriver();
    private static final String dbHostName      = conf.getDbHostname();
    private static final String dbDatabaseName  = conf.getDbDatabaseName();
    private static final String dbUsername      = conf.getDbUsername();
    private static final String dbPassword      = conf.getDbPassword();

    public Connection getConnection(){
        try{
            Class.forName(dbDriver);
            Connection conn = (Connection) DriverManager.getConnection(dbHostName + dbDatabaseName, dbUsername, dbPassword);
            return conn;
        } catch(Exception e){
            e.printStackTrace();
        }
        return conn;
    }
    public void disconnect(){

        try{
            conn.close();
        } catch (Exception e){}
    }
}

登录表单控制器:

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String form         = request.getParameter("form");
// check login details
if(form.equals("loginForm")){
    String username = request.getParameter("username").trim();
    String password = request.getParameter("password").trim();

    password = loginService.hashPassword(password);
    boolean isValidUser = loginService.checkUser(username, password);

    if(isValidUser){

        Cookie loggedIn = new Cookie("loggedIn", "true");
        loggedIn.setMaxAge(60*60*24);
        response.addCookie(loggedIn);

        out.print("success");

    }else{
        out.print("nope");
    }
}
}

登录服务检查登录详细信息是否正确:

public boolean checkUser(String username, String password){
    boolean isValid = false;
    try{
        sql = "SELECT username, password FROM morleys_user WHERE username=? AND password=? AND isActive=1 LIMIT 1";
        prep = conn.prepareStatement(sql);
        prep.setString(1, username);
        prep.setString(2, password);
        rs = prep.executeQuery();
        if(rs.next()){
            return true;
        }
    }catch(Exception e){
        e.printStackTrace();
    }finally{
        connection.disconnect();
        }
    return isValid;
}

更新

如果我理解正确,我不应该处理与数据库的直接连接,而是使用可以为我管理连接的服务。

这是我建立与MysQL数据库的DataSource连接的例子。

建立此类的新DataSource实例:

package uk.co.morleys;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

import javax.sql.DataSource;

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;

public class DataSourceFactory {

    public static DataSource getMySQLDataSource() {
        Properties props = new Properties();
        FileInputStream fis = null;
        MysqlDataSource mysqlDS = null;
        try {
            fis = new FileInputStream("db.properties");
            props.load(fis);
            mysqlDS = new MysqlDataSource();
            mysqlDS.setURL(props.getProperty("MYSQL_DB_URL"));
            mysqlDS.setUser(props.getProperty("MYSQL_DB_USERNAME"));
            mysqlDS.setPassword(props.getProperty("MYSQL_DB_PASSWORD"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return mysqlDS;
    }

}

实例化一个新的DataSource以检查用户登录详细信息

public boolean checkUser(String username, String password){
    boolean isValid = false;
    DataSource ds = DataSourceFactory.getMySQLDataSource();
    Connection con = null;
    ResultSet rs = null;
    PreparedStatement ps = null;

    try{
        con = ds.getConnection();
        sql = "SELECT username, password FROM morleys_user WHERE username=? AND password=? AND isActive=1 LIMIT ";
        ps = con.prepareStatement(sql);
        ps.setString(1, username);
        ps.setString(2, password);
        rs = ps.executeQuery();
        if(rs.next()){
            return true;
        }

    }catch(SQLException e){
        e.printStackTrace();
    }finally{
        try {
            if(rs != null) rs.close();
            if(ps != null) ps.close();
            if(con != null) con.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
}
    return isValid;
}

2 个答案:

答案 0 :(得分:5)

在我假设您没有非常有效地管理数据库资源之前,您从未听说过连接池。 访问数据库的最基本方法是获取连接,执行一些语句和关闭连接。 在您提供的代码中,我没有看到您获取或关闭连接,因此我假设您在启动应用程序时创建单个连接并保持连接“永久”打开。经过一段时间后,MySql服务器决定终止连接,因为它已经打开了很长时间。

每次需要创建和关闭连接时,通常不会遇到任何连接超时,但每次应用程序需要时,您可能会遇到很多创建连接的开销。

这是连接池的用武之地;连接池管理许多数据库连接,每次需要时,应用程序都会借用一个。通过正确配置连接池,池通常会透明地处理断开的连接(例如,您可以将池配置为在x分钟/小时之后续订连接。)

您还需要注意资源管理;例如一旦你不再需要它就立即结束声明。

以下代码演示了如何改进“检查用户”方法:

public boolean checkUser(String username, String password) throws SQLException {
    //acquire a java.sql.DataSource; the DataSource is typically a connection pool that's set-up in the application of obtained via jndi
    DataSource dataSource = acquireDataSource();
    //java 7 try-with-resources statement is used to make sure that resources are properly closed
    //obtain a connection from the pool. Upon closing the connection we return it to the pool
    try (Connection connection = dataSource.getConnection()) {
        //release resources associated with the PreparedStatement as soon as we no longer need it.
        try(PreparedStatement ps = connection.prepareStatement("SELECT username, password FROM morleys_user WHERE username=? AND password=? AND isActive=1 LIMIT 1");){
            ps.setString(1, username);
            ps.setString(2, password);
            ResultSet resultSet = ps.executeQuery();
            return resultSet.next();
        }
    }
}

公共连接池为Apache Commons-DBCPC3P0

由于管理sql资源可能非常重复和繁琐,您可能需要考虑使用模板:例如Spring's JdbcTemplate

示例C3p0配置:

public ComboPooledDataSource dataSource(String driver, String url, String username,String password) throws PropertyVetoException {
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setDriverClass(driver);
    dataSource.setJdbcUrl(url);
    dataSource.setUser(username);
    dataSource.setPassword(password);

    dataSource.setAcquireIncrement(1);
    dataSource.setMaxPoolSize(100);
    dataSource.setMinPoolSize(1);
    dataSource.setInitialPoolSize(1);
    dataSource.setMaxIdleTime(300);
    dataSource.setMaxConnectionAge(36000);

    dataSource.setAcquireRetryAttempts(5);
    dataSource.setAcquireRetryDelay(2000);
    dataSource.setBreakAfterAcquireFailure(false);

    dataSource.setCheckoutTimeout(30000);
    dataSource.setPreferredTestQuery("SELECT 1");
    dataSource.setIdleConnectionTestPeriod(60);
    return dataSource;
}//in order to do a "clean" shutdown you should call datasource.close() when shutting down your web app.

答案 1 :(得分:4)

MySQL在一段时间后超时连接。处理此问题的标准方法是使用正确配置的连接池(使用已配置的DataSource),而不是直接使用DriverManager。

连接池将检查并丢弃“陈旧”连接。