MySQL 与 JERSEY 的连接池

MySQL connection pooling with JERSEY

我正在开发 RESTful API 和 MySQL。

我实际上是在使用 JDBC 驱动程序连接到数据库,每次我想访问它时我都会创建一个新连接。由于它显然是内存泄漏,我开始实现 ServletContextClassclass 但是当我需要获取 SQL 查询的结果时,我不知道如何调用该方法。

这是我做错的方式:

DbConnection.java public class DbConnection {

    public Connection getConnection() throws Exception {
        try {
            String connectionURL = "jdbc:mysql://root:port/path";
            Connection connection = null;
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            connection = DriverManager.getConnection(connectionURL, "root", "password");
            return connection;
        }

        catch (SQLException e) {
            throw e;
        }
    }
}

DbData.java

public ArrayList<Product> getAllProducts(Connection connection) throws Exception {
    ArrayList<Product> productList = new ArrayList<Product>();
    try {
        PreparedStatement ps = connection.prepareStatement("SELECT id, name FROM product");
        ResultSet rs = ps.executeQuery();
        while (rs.next()) {
            Product product = new Product();
            product.setId(rs.getInt("id"));
            product.setName(rs.getString("name"));
            productList.add(product);
        }
        return productList;
    } catch (Exception e) {
            throw e;
    }
}

Resource.java

@GET
@Path("task/{taskId}")
@Consumes(MediaType.APPLICATION_JSON)
public Response getInfos(@PathParam("taskId") int taskId) throws Exception {
    try {
        DbConnection database= new DbConnection();
        Connection connection = database.getConnection();
        Task task = new Task();
        DbData dbData = new DbData();
        task = dbData.getTask(connection, taskId);

        return Response.status(200).entity(task).build();
    } catch (Exception e) {
        throw e;
    }
}

这是我最终尝试实施新 class:

的地方

ServletContextClass.java

public class ServletContextClass implements ServletContextListener {

    public Connection getConnection() throws Exception {
        try {
            String connectionURL = "jdbc:mysql://root:port/path";
            Connection connection = null;
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            connection = DriverManager.getConnection(connectionURL, "root", "password");
            return connection;
        } catch (SQLException e) {
            throw e;
        }
    }

    public void contextInitialized(ServletContextEvent arg0) {
        System.out.println("ServletContextListener started");
        DbConnection database = new DbConnection();
        try {
            Connection connection = database.getConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void contextDestroyed(ServletContextEvent arg0) {
        System.out.println("ServletContextListener destroyed");
        //con.close ();       
    }

}

但问题是,我不知道下一步该做什么。有什么帮助吗?谢谢

您需要将 Connection 变量设置为 ServletContextattribute。另外,我建议使用 connection 作为静态 class 变量,这样您就可以在 contextDestroyed 方法中关闭它。 您可以稍后在任何 servlet 中检索 connection 属性以进行数据库操作。

public class ServletContextClass implements ServletContextListener {

    public static Connection connection;

    public Connection getConnection(){
        try {
            String connectionURL = "jdbc:mysql://root:port/path";
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            connection = DriverManager.getConnection(connectionURL, "root", "password");
        } catch (SQLException e) {
            // Do something
        }
    }

    public void contextInitialized(ServletContextEvent arg0) {
        System.out.println("ServletContextListener started");
        getConnection();
        arg0.getServletContext().setAttribute("connection", connection);
    }

    public void contextDestroyed(ServletContextEvent arg0) {
        System.out.println("ServletContextListener destroyed");
        try{
            if(connection != null){
                connection.close();
            }
        }catch(SQLException se){
            // Do something
        } 
    }

}

最后访问您的 Servlet(资源)中的 connection 属性。确保将 @Context ServletContext 传递给 Response 方法,以便可以访问上下文属性。

@GET
@Path("task/{taskId}")
@Consumes(MediaType.APPLICATION_JSON)
public Response getInfos(@PathParam("taskId") int taskId, @Context ServletContext context) throws Exception {
    try {
        Connection connection = (Connection) context.getAttribute("connection");
        Task task = new Task();
        DbData dbData = new DbData();
        task = dbData.getTask(connection, taskId);

        return Response.status(200).entity(task).build();
    } catch (Exception e) {
        throw e;
    }
}

既然我们已经解决了您当前的问题,我们需要知道这种方法会出现什么问题。

首先,您只创建了一个 connection 对象,它将在任何地方使用。想象一下多个用户同时访问您的 API,单个 connection 将在所有用户之间共享,这会减慢您的响应时间。

其次,你的 connection 到数据库会在闲置一段时间后死掉(除非你配置 MySql 服务器不要终止空闲连接,这不是一个好主意),当你尝试要访问它,你会得到 SQLExceptions 到处都是。这可以在你的 servlet 内部解决,你可以检查你的连接是否已死,重新创建它,然后更新上下文属性。

处理 Mysql 连接池的最佳方法是使用 JNDI 资源。您可以创建一个 pool of connections,它将由您的 servlet container 管理。您可以将池配置为在闲置后死机时重新创建连接。如果您使用 Tomcat 作为您的 Servlet 容器,您可以查看 this 简短教程以开始了解 JNDI 连接池。