📜  jdbc 中的连接池 - Java (1)

📅  最后修改于: 2023-12-03 15:02:07.349000             🧑  作者: Mango

JDBC 中的连接池 - Java

在 Java 应用程序中使用 JDBC 进行数据库操作时,连接池是一种改进的技术,它通过连接池管理连接对象,避免重复创建和释放连接对象带来的性能开销。本文将介绍在 Java 中使用连接池技术进行数据库操作的方法和优点。

连接池的作用

在传统的 JDBC 中,每次进行数据库操作时,都需要创建连接对象。这个过程包括加载驱动、建立连接、进行数据操作、关闭连接等步骤。然而,频繁地创建和销毁连接对象会造成很大的开销,降低运行效率。

连接池是一种解决此问题的方法。连接池允许应用程序在启动时,创建一定数量的连接对象,并将它们保存在连接池中。当应用程序需要进行数据库操作时,可以从连接池中获取一个连接对象,完成操作后将连接对象释放回连接池而不是销毁掉。这样可以达到多个应用程序共用少量的连接对象,提高了连接的利用率。

连接池的优点

连接池除了减少连接创建和销毁所带来的性能开销,还有以下几个优点:

  • 提升运行效率:由于连接池中的连接对象都是事先创建好的,并已经建立好连接,因此可以直接使用,而无需重复的创建和销毁连接对象,可以明显地提升程序的运行效率,特别是在高并发的情况下。

  • 控制连接对象的使用数量:连接池可以设置连接池的最大连接数,最小连接数,以及连接对象的最长使用时间等参数,这样可以合理地分配连接对象的使用,防止连接泄漏。

  • 提高系统稳定性:由于连接池中的连接对象都是定期检查使用状态并进行维护的,这些连接对象都是有效的,不会出现无效的连接对象,因此可以提高系统的稳定性和可靠性。

  • 简化编程:连接池可以提供简洁、统一的 API,避免了开发人员重复编写连接创建和销毁的代码,简化了编程。

连接池的实现

在 Java 中,可以使用一些开源的连接池框架或直接使用 Java 标准库自带的连接池来实现连接池功能。

使用 Apache Commons DBCP 实现连接池

Apache Commons DBCP(DataBase Connection Pool)是一个流行的用于 JDBC 连接池的开源框架。使用 DBCP 需要遵循以下步骤:

  1. 添加依赖

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
        <version>2.7.0</version>
    </dependency>
    
  2. 创建连接池对象

    import org.apache.commons.dbcp2.BasicDataSource;
    
    public class ConnectionPool {
        private static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
        private static final String URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8";
        private static final String USERNAME = "root";
        private static final String PASSWORD = "root";
        private static final int INITIAL_SIZE = 5;
        private static final int MAX_TOTAL = 20;
        private static final int MAX_IDLE = 10;
        private static final int MIN_IDLE = 5;
        private static final long MAX_WAIT_MILLIS = 5000;
    
        private static BasicDataSource dataSource;
    
        static {
            dataSource = new BasicDataSource();
            dataSource.setDriverClassName(DRIVER_CLASS_NAME);
            dataSource.setUrl(URL);
            dataSource.setUsername(USERNAME);
            dataSource.setPassword(PASSWORD);
            dataSource.setInitialSize(INITIAL_SIZE);
            dataSource.setMaxTotal(MAX_TOTAL);
            dataSource.setMaxIdle(MAX_IDLE);
            dataSource.setMinIdle(MIN_IDLE);
            dataSource.setMaxWaitMillis(MAX_WAIT_MILLIS);
        }
    
        public static Connection getConnection() throws SQLException {
            return dataSource.getConnection();
        }
    }
    
  3. 使用连接池

    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
        conn = ConnectionPool.getConnection();
        pstmt = conn.prepareStatement("SELECT * FROM user WHERE id = ?");
        pstmt.setLong(1, 1L);
        rs = pstmt.executeQuery();
        while (rs.next()) {
            System.out.println(rs.getString("username") + ", " + rs.getString("password"));
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        try {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
            if (conn != null) conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
使用 Java 标准库实现连接池

除了使用第三方框架实现连接池外,Java 标准库中也提供了连接池的实现。这里介绍如何使用 javax.sql.DataSource 接口和 java.sql.DriverManager 类的组合实现连接池。其中,javax.sql.DataSource 接口定义了数据库连接池的一些标准特性,java.sql.DriverManager 类则实现了获取 JDBC 连接的基本功能。使用 Java 标准库实现连接池需要遵循以下步骤:

  1. 创建连接池对象

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.ArrayList;
    import java.util.List;
    
    public class ConnectionPool {
        private static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
        private static final String URL = "jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=utf-8";
        private static final String USERNAME = "root";
        private static final String PASSWORD = "root";
        private static final int CONNECTION_NUM = 10;
    
        private static List<Connection> connections = new ArrayList<>(CONNECTION_NUM);
    
        static {
            try {
                Class.forName(DRIVER_CLASS_NAME);
                for (int i = 0; i < CONNECTION_NUM; i++) {
                    Connection conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
                    connections.add(conn);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    
        public static Connection getConnection() throws SQLException {
            if (connections.isEmpty()) {
                throw new SQLException("No available connection.");
            }
            Connection conn = connections.remove(0);
            if (conn.isClosed()) {
                return getConnection();
            }
            return conn;
        }
    
        public static void releaseConnection(Connection conn) {
            if (conn != null) {
                try {
                    if (!conn.isClosed() && connections.size() < CONNECTION_NUM) {
                        connections.add(conn);
                    } else {
                        conn.close();
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
  2. 使用连接池

    Connection conn = null;
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    try {
        conn = ConnectionPool.getConnection();
        pstmt = conn.prepareStatement("SELECT * FROM user WHERE id = ?");
        pstmt.setLong(1, 1L);
        rs = pstmt.executeQuery();
        while (rs.next()) {
            System.out.println(rs.getString("username") + ", " + rs.getString("password"));
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        try {
            if (rs != null) rs.close();
            if (pstmt != null) pstmt.close();
            if (conn != null) {
                ConnectionPool.releaseConnection(conn);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
总结

连接池是一种简单且常用的优化 Java 应用程序的方法。 使用连接池可以避免重复创建和销毁连接对象带来的性能影响,提升程序运行效率和可靠性。可以使用 Apache Commons DBCP 或者 Java 标准库中的 DataSource 接口和 DriverManager 类实现连接池,开发人员可以根据实现的不同要求选择合适的方式来实现数据库连接池。