【问题标题】:Am I Using JDBC Connection Pooling?我在使用 JDBC 连接池吗?
【发布时间】:2011-11-27 08:51:33
【问题描述】:

我正在尝试确定我是否真的在使用 JDBC 连接池。在做了一些研究之后,实施似乎太容易了。实际上比常规连接更容易,所以我想验证一下。

这是我的连接类:

public class DatabaseConnection {

Connection conn = null;

public Connection getConnection() {

    BasicDataSource bds = new BasicDataSource();
    bds.setDriverClassName("com.mysql.jdbc.Driver");
    bds.setUrl("jdbc:mysql://localhost:3306/data");
    bds.setUsername("USERNAME");
    bds.setPassword("PASSWORD");

    try{
        System.out.println("Attempting Database Connection");
        conn = bds.getConnection();
        System.out.println("Connected Successfully");
    }catch(SQLException e){
        System.out.println("Caught SQL Exception: " + e);
    }
    return conn;
}

public void closeConnection() throws SQLException {
    conn.close();
}

}

这是真正的连接池吗?我在另一个类中使用连接:

        //Check data against database.
    DatabaseConnection dbConn = new DatabaseConnection();
    Connection conn;
    ResultSet rs;
    PreparedStatement prepStmt;

    //Query database and check username/pass against table.
    try{
        conn = dbConn.getConnection();
        String sql = "SELECT * FROM users WHERE username=? AND password=?";
        prepStmt = conn.prepareStatement(sql);
        prepStmt.setString(1, user.getUsername());
        prepStmt.setString(2, user.getPassword());
        rs = prepStmt.executeQuery();

        if(rs.next()){ //Found Match.
            do{
                out.println("UserName = " + rs.getObject("username") + " Password = " + rs.getObject("password"));
                out.println("<br>");
            } while(rs.next());
        } else {
            out.println("Sorry, you are not in my database."); //No Match.
        }

        dbConn.closeConnection(); //Close db connection.

    }catch(SQLException e){
        System.out.println("Caught SQL Exception: " + e);
    }

【问题讨论】:

    标签: mysql jakarta-ee jdbc connection-pooling


    【解决方案1】:

    假设BasicDataSource 来自DBCP,那么是的,您正在使用连接池。但是,您在每次连接获取时都重新创建另一个连接池。您并没有真正从同一个池中汇集连接。您只需在应用程序启动时创建一次连接池并从中获取每个连接。您也不应该将连接保留为实例变量。您还应该关闭连接、语句和结果集,以确保正确关闭资源,即使出现异常也是如此。 Java 7 的try-with-resources statement 对此很有帮助,它会在try 块完成时自动关闭资源。

    这是一个小的重写:

    public final class Database {
    
        private static final BasicDataSource dataSource = new BasicDataSource();
    
        static {
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/data");
            dataSource.setUsername("USERNAME");
            dataSource.setPassword("PASSWORD");
        }
    
        private Database() {
            //
        }
    
        public static Connection getConnection() throws SQLException {
            return dataSource.getConnection();
        }
    
    }
    

    (如有必要,可以将其重构为抽象工厂以提高可插拔性)

    private static final String SQL_EXIST = "SELECT * FROM users WHERE username=? AND password=?";
    
    public boolean exist(User user) throws SQLException {
        boolean exist = false;
    
        try (
            Connection connection = Database.getConnection();
            PreparedStatement statement = connection.prepareStatement(SQL_EXIST);
        ) {
            statement.setString(1, user.getUsername());
            statement.setString(2, user.getPassword());
    
            try (ResultSet resultSet = preparedStatement.executeQuery()) {
                exist = resultSet.next();
            }
        }       
    
        return exist;
    }
    

    按如下方式使用:

    try {
        if (!userDAO.exist(username, password)) {
            request.setAttribute("message", "Unknown login. Try again.");
            request.getRequestDispatcher("/WEB-INF/login.jsp").forward(request, response);
        } else {
            request.getSession().setAttribute("user", username);
            response.sendRedirect("userhome");
        }
    } catch (SQLException e) {
        throw new ServletException("DB error", e);
    }
    

    在真正的 Java EE 环境中,您应该将 DataSource 的创建委托给容器/应用程序服务器并从 JNDI 获取。在 Tomcat 的情况下,另请参阅此文档:http://tomcat.apache.org/tomcat-6.0-doc/jndi-resources-howto.html

    【讨论】:

    • 哇,也感谢您的重写。对像我这样的新人的完美帮助。
    • 这个解决方案是线程安全的吗?我需要调用 connection.close();
    • @swapyonubuntu: 使用新的 Java7 try-with-resources 语句 docs.oracle.com/javase/tutorial/essential/exceptions/… 自动完成关闭
    • @BalusC 为什么原始示例是“在每次连接获取时重新创建另一个连接池”?因为每次调用getConnection(),都会调用setDriver、setUrl……等等?这些调用会重新创建另一个连接池吗?正如我看到您的示例,您将这些 setDriver.... 放入静态初始化程序
    • @user1169587:不,因为它每次都调用new BasicDataSource()
    【解决方案2】:

    似乎没有合并。您应该将 DataSource 存储在 DatabaseConnection 中,而不是在每次调用 getConnection() 时创建一个新的。 getConnection() 应该返回 datasource.getConnection()。

    【讨论】:

      【解决方案3】:

      看起来像 DBCP 用法。如果是这样,那么是的。已经集齐了。这是 DBCP 的默认池属性值。

      /**
      * The default cap on the number of "sleeping" instances in the pool.
      * @see #getMaxIdle
      * @see #setMaxIdle
      */
      public static final int DEFAULT_MAX_IDLE  = 8;
      /**
      * The default minimum number of "sleeping" instances in the pool
      * before before the evictor thread (if active) spawns new objects.
      * @see #getMinIdle
      * @see #setMinIdle
      */
      public static final int DEFAULT_MIN_IDLE = 0;
      /**
      * The default cap on the total number of active instances from the pool.
      * @see #getMaxActive
      */
      public static final int DEFAULT_MAX_ACTIVE  = 8;
      

      【讨论】:

        【解决方案4】:

        作为 BalusC 解决方案的后续,下面是一个实现,我可以在需要多个连接的应用程序中使用它,或者在事先不知道连接属性的公共库中使用它......

        import org.apache.commons.dbcp.BasicDataSource;
        
        import java.sql.Connection;
        import java.sql.SQLException;
        import java.util.concurrent.ConcurrentHashMap;
        
        public final class Database {
        
            private static final ConcurrentHashMap<String, BasicDataSource> dataSources = new ConcurrentHashMap();
        
            private Database() {
                //
            }
        
            public static Connection getConnection(String connectionString, String username, String password) throws SQLException {
        
                BasicDataSource dataSource;
        
                if (dataSources.containsKey(connectionString)) {
                    dataSource = dataSources.get(connectionString);
                } else {
                    dataSource = new BasicDataSource();
                    dataSource.setDriverClassName("com.mysql.jdbc.Driver");
                    dataSource.setUrl(connectionString);
                    dataSource.setUsername(username);
                    dataSource.setPassword(password);
                    dataSources.put(connectionString, dataSource);
                }
        
                return dataSource.getConnection();
        
            }
        
        }
        

        【讨论】:

        • 此解决方案并不总是有效。尽管使用了 ConcurrentHashMap,但它仍会受到竞争条件的影响。
        猜你喜欢
        • 2016-07-06
        • 2011-03-06
        • 2013-02-18
        • 2013-10-12
        • 2010-11-30
        • 2011-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多