【问题标题】:Do this implimentation of C3P0 in my web application is correct? What are the pros and cons of this implimentation?我的 Web 应用程序中的 C3P0 实现是否正确?这种实施的优点和缺点是什么?
【发布时间】:2015-07-27 13:55:12
【问题描述】:

我需要实现使用 Servlet 和 JSP 页面的 Web 应用程序的连接池。我不想在服务器中的所有应用程序之间建立连接池,而是希望在所有 servlet 之间实现连接池。

我有一个 C3P0 的工作实现,但我不知道这是不是正确的方法,这样做会产生预期的结果。

以下是我的实现 有一个数据源文件 C3P0 实现来返回连接。 连接将在servelet init 方法上检索并在destroy 方法上关闭。

  • 这种类型的实现是否会产生预期的结果?
  • 我必须在销毁时关闭连接吗?
  • 还有哪些其他方法可以实现与 servlet 应用程序的连接池?
  • 我喜欢将所有与 db 相关的操作移动到单独的类中,在这种情况下,我需要处理哪些事情?在这种情况下,我是否必须在 servlet 中获得连接并传递给 db 方法?
  • 请给出当前实现的优缺点

请在下面找到我的连接池实现

返回连接的数据源文件和使用该连接的Servlet

数据源文件

import java.beans.PropertyVetoException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;

import com.mchange.v2.c3p0.ComboPooledDataSource;


public class DataSource {

    // JDBC driver name and database URL
    static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
    static final String DB_URL = "jdbc:mysql://dburl/dbName";

    // Database credentials
    static final String USER = "username";
    static final String PASS = "password";  

    private static DataSource     datasource;
    private ComboPooledDataSource cpds;

    private DataSource() throws IOException, SQLException, PropertyVetoException {
        cpds = new ComboPooledDataSource();
        cpds.setDriverClass("com.mysql.jdbc.Driver"); //loads the jdbc driver

        cpds.setJdbcUrl(DB_URL);
        cpds.setUser(USER);
        cpds.setPassword(PASS);

        // the settings below are optional -- c3p0 can work with defaults
        cpds.setMinPoolSize(5);
        cpds.setAcquireIncrement(5);
        cpds.setMaxPoolSize(20);
        cpds.setMaxStatements(180);

    }

    public static DataSource getInstance() throws IOException, SQLException, PropertyVetoException {
        if (datasource == null) {
            datasource = new DataSource();
            return datasource;
        } else {
            return datasource;
        }
    }

    public Connection getConnection() throws SQLException {
        return this.cpds.getConnection();
    }

}

Servlet

import java.beans.PropertyVetoException;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class ConnectionTest
 */
@WebServlet("/ConnectionTest")
public class ConnectionTest extends HttpServlet {


Connection connection = null;
Statement statement = null;

@Override
public void init() throws ServletException {
    super.init();

    try {
        connection = DataSource.getInstance().getConnection();

    } catch (PropertyVetoException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

/**
 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
 *      response)
 */
protected void doGet(HttpServletRequest request,
        HttpServletResponse response) throws ServletException, IOException {

    PrintWriter out = response.getWriter();
    out.println("<h1> Hi</h1>");

    ResultSet resultSet = null;
    try {

        statement = connection.createStatement();
        resultSet = statement
                .executeQuery("SELECT VehicleRegistration  FROM Registration");
        while (resultSet.next()) {

            String first = resultSet.getString("VehicleRegistration");

            // Display values
            out.println("<h1> " + first + "</h1>");

        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        if (resultSet != null)
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        if (statement != null)
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
    }
}

@Override
public void destroy() {
    if (connection != null) {
        try {

            connection.close();

        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    super.destroy();
}

}

我欢迎所有建议,即使它太小或太大,这有助于我改进此代码

先谢谢大家

【问题讨论】:

  • 为什么要投反对票,我喜欢改进给定的代码并需要专家建议,我认为这样做是为了“改进我们的编码方式”

标签: java servlets connection-pooling c3p0


【解决方案1】:

所以,这是可以改进的。

您的 Servlet 应该在 init(...) 中获取对 DataSource 的引用,并且在 Servlet 的生命周期内不持有未处理的 Connection 对象。 Connection 对象应根据需要从连接池数据源中获取,而不是长时间保持打开状态。您的 doGet(...) 方法应该获取 Connection(这会很快,因为它已经在池中!),然后注意在其 finally 块中调用 Connection 上的 close()

改变这一点非常重要。除了永久保持连接打开的麻烦和资源使用不佳之外,在您当前的架构下,对您的 Servlet 的同时请求将同时在同一个连接上运行,这可能导致意外和不正确的行为,特别是如果您的 web 应用程序中有 servlet使用事务(大多数重要的应用程序必须这样做)。

您的 DataSource 类似乎不是特别有用。为什么不直接构造 c3p0 数据源,而不是使用 getConnection() 方法定义一种新的对象。我认为您的动机可能只是让静态成员datasource 成为一个真正的数据源。但是,如果您想将 DataSource 存储为静态成员,您可以在任何地方执行此操作,无需包装实际的 DataSource。

不过,在 JavaEE Web 应用程序中保存连接池支持的数据源的最佳位置不是静态成员。更好的方法是在ServletContextListenercontextInitialized(...) 中构造您的DataSource,并将DataSource 存储在应用程序范围内。在contextDestroyed(...) 调用close() c3p0 ComboPooledDataSource,并将其从应用程序范围中删除。

【讨论】:

  • 谢谢史蒂夫,是的,我相信这是对我已经完成的工作的巨大改进。请根据您的建议查看修改后的文件数据源文件ctrlv.it/java/1811/4060924481ServletContextListnerctrlv.it/java/1812/1704021759Servletctrlv.it/java/1813/4206276490 请查看它们,是否正确?我已经修改了“数据源”,添加了新的 Servlet 上下文侦听器并在“contextInitialized()”中调用了“数据源”构造器,将它作为参数传递给了“ComboPooledDataSource”,并在 Servlet“doGet()”中使用了它
  • 嗨史蒂夫还有一些疑问,使用 c3p0 时,connection.close() 会关闭连接还是将连接返回到连接池?如果它关闭连接,那么在我的 servlet 服务方法中使用 mysql 存储过程和 c3p0 给我存储过程的好处。我发现这个“MySQL 为每个连接维护自己的存储过程缓存。如果应用程序在单个连接中多次使用存储过程,则使用编译版本,否则存储过程就像查询一样工作。”我们在每个请求中关闭它
  • 这样更好。但是我根本看不到您的 DataSource 类的意义。您可以直接在 ServletContextListener 中设置您的 ComboPooledDataSource。为什么要创建一个构造函数设置一个的类,只访问它并丢弃包装对象?这是不必要的间接和混乱。在 c3p0 PooledDataSource 发出的连接上调用 connection.close() 不会关闭()物理连接。物理连接被检入池中并再次可用。
  • 关于存储过程缓存,为了获得最大的性能优势,不要很快过期连接。默认情况下,c3p0 不会使连接过期,但您应该配置一些连接测试,以便从池中清除不良连接。无论如何,过度使用,所有池化连接的存储过程缓存都会被填满,您会看到存储过程性能提高,至少在理论上是这样。
  • 谢谢史蒂夫,非常感谢您对我重复的 cmets 的回复,您的回答和 cmets 给了我很好的见解并让我进步了很多......谢谢你..
猜你喜欢
  • 2010-09-10
  • 2012-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-08
  • 1970-01-01
  • 2011-01-07
  • 2012-02-07
相关资源
最近更新 更多