九、数据库连接池

1. 基本概念

  1. 数据库连接池:负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。这项技术能明显节约资源和提高对数据库操作的性能。
  2. 原理:是一个存放数据库连接的容器(集合)。当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
    在这里插入图片描述

2. javax.sql.DataSource接口

负责与数据库建立连接,返回一个连接对象Connection。

  • Connection getConnection() :直接获取连接;
  • Connection getConnection(String username, String password) :根据登录信息获取连接。
  • DataSource接口由数据库供应商实现(C3P0、Druid):
    • Connection.close() :归还连接。
      数据库连接池实现类会重写该方法,使得从连接池中获取的连接对象Connection调用close()方法时不会再关闭连接,而是归还连接。

3. C3P0

开源数据库连接池。实现了DataSource接口,支持JDBC2、JDBC3标准规范,易于扩展性能优越,Hibernate、Spring使用的都是该数据源。

  1. 步骤:
    1. 导入2个jar包 :
      c3p0-0.9.5.2.jar
      mchange-commons-java-0.2.12.jar

      注:不要忘记导入数据库驱动jar包

    2. 定义配置文件:

      • 名称:c3p0.propertiesc3p0-config.xml
      • 路径:根目录下。
      <c3p0-config>
      	<!-- 使用默认的配置读取连接池对象 -->
      	<default-config>
      	    <!--  连接参数 -->
      	    <property name="driverClass">com.mysql.jdbc.Driver</property>
      	    <property name="jdbcUrl">jdbc:mysql://localhost:3306/db4</property>
      	    <property name="user">root</property>
      	    <property name="password">root</property>
      	
      	    <!-- 连接池参数 -->
      	    <!--初始化申请的连接数量-->
      	    <property name="initialPoolSize">5</property>
      	    <!--最大的连接数量-->
      	    <property name="maxPoolSize">10</property>
      	    <!--超时时间-->
      	    <property name="checkoutTimeout">3000</property>
      	</default-config>
      	
      	<!-- 使用其他连接池对象 -->
      	<named-config name="otherc3p0">
      	    <!--  连接参数 -->
      	    <property name="driverClass">com.mysql.jdbc.Driver</property>
      	    <property name="jdbcUrl">jdbc:mysql://localhost:3306/db3</property>
      	    <property name="user">root</property>
      	    <property name="password">root</property>
      	
      	    <!-- 连接池参数 -->
      	    <property name="initialPoolSize">5</property>
      	    <property name="maxPoolSize">8</property>
      	    <property name="checkoutTimeout">1000</property>
      	</named-config>
      </c3p0-config>
      
    3. 创建数据库连接池对象ComboPooledDataSource:

      //获取默认配置的连接池对象
      DataSource ds  = new ComboPooledDataSource();
      //获取指定的连接池对象
      DataSource ds  = new ComboPooledDataSource("otherc3p0");
      
    4. 获取连接getConnection:

      Connection conn = ds.getConnection();
      
  2. 配置:
    1. 最大连接数maxPoolSize:
      import com.mchange.v2.c3p0.ComboPooledDataSource;
      
      import javax.sql.DataSource;
      import java.sql.Connection;
      import java.sql.SQLException;
      
      public class C3P0Demo {
          public static void main(String[] args) throws SQLException {
              //1.获取DataSource,使用默认配置
              DataSource ds = new ComboPooledDataSource();
      
              //2.获取连接
              for (int i = 1; i <= 11; i++) {
                  Connection conn = ds.getConnection();
                  System.out.println(i + ":" + conn);
      
                  if (i == 5) {
                      conn.close();//归还连接到连接池中
                  }
              }
          }
      }
      
      输出结果:
      1:com.mchange.v2.c3p0.impl.NewProxyConnection@6156496 [wrapping: com.mysql.jdbc.JDBC4Connection@3c153a1]
      2:com.mchange.v2.c3p0.impl.NewProxyConnection@13acb0d1 [wrapping: com.mysql.jdbc.JDBC4Connection@3e3047e6]
      3:com.mchange.v2.c3p0.impl.NewProxyConnection@2b6856dd [wrapping: com.mysql.jdbc.JDBC4Connection@5db45159]
      4:com.mchange.v2.c3p0.impl.NewProxyConnection@7c417213 [wrapping: com.mysql.jdbc.JDBC4Connection@15761df8]
      5:com.mchange.v2.c3p0.impl.NewProxyConnection@327b636c [wrapping: com.mysql.jdbc.JDBC4Connection@45dd4eda]
      6:com.mchange.v2.c3p0.impl.NewProxyConnection@19d37183 [wrapping: com.mysql.jdbc.JDBC4Connection@1a0dcaa]
      7:com.mchange.v2.c3p0.impl.NewProxyConnection@fdefd3f [wrapping: com.mysql.jdbc.JDBC4Connection@45dd4eda]
      8:com.mchange.v2.c3p0.impl.NewProxyConnection@a4102b8 [wrapping: com.mysql.jdbc.JDBC4Connection@11dc3715]
      9:com.mchange.v2.c3p0.impl.NewProxyConnection@7a52f2a2 [wrapping: com.mysql.jdbc.JDBC4Connection@78047b92]
      10:com.mchange.v2.c3p0.impl.NewProxyConnection@79ca92b9 [wrapping: com.mysql.jdbc.JDBC4Connection@1460a8c0]
      11:com.mchange.v2.c3p0.impl.NewProxyConnection@4387b79e [wrapping: com.mysql.jdbc.JDBC4Connection@6e75aa0d]
      
      以上连接5与连接7为同一连接 @45dd4eda ,说明连接被成功回收归还到连接池中。若不归还则会超出最大连接数,第11个连接在等待 checkoutTimeout 长的时间后会报错:
      1:com.mchange.v2.c3p0.impl.NewProxyConnection@27082746 [wrapping: com.mysql.jdbc.JDBC4Connection@66133adc]
      2:com.mchange.v2.c3p0.impl.NewProxyConnection@42f30e0a [wrapping: com.mysql.jdbc.JDBC4Connection@24273305]
      3:com.mchange.v2.c3p0.impl.NewProxyConnection@46f5f779 [wrapping: com.mysql.jdbc.JDBC4Connection@1c2c22f3]
      4:com.mchange.v2.c3p0.impl.NewProxyConnection@33e5ccce [wrapping: com.mysql.jdbc.JDBC4Connection@5a42bbf4]
      5:com.mchange.v2.c3p0.impl.NewProxyConnection@52d455b8 [wrapping: com.mysql.jdbc.JDBC4Connection@4f4a7090]
      6:com.mchange.v2.c3p0.impl.NewProxyConnection@6956de9 [wrapping: com.mysql.jdbc.JDBC4Connection@769c9116]
      7:com.mchange.v2.c3p0.impl.NewProxyConnection@2d6d8735 [wrapping: com.mysql.jdbc.JDBC4Connection@ba4d54]
      8:com.mchange.v2.c3p0.impl.NewProxyConnection@de0a01f [wrapping: com.mysql.jdbc.JDBC4Connection@4c75cab9]
      9:com.mchange.v2.c3p0.impl.NewProxyConnection@6f79caec [wrapping: com.mysql.jdbc.JDBC4Connection@67117f44]
      10:com.mchange.v2.c3p0.impl.NewProxyConnection@2471cca7 [wrapping: com.mysql.jdbc.JDBC4Connection@5fe5c6f]
      Exception in thread "main" java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
      	at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118)
      	at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:77)
      	at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:690)
      	at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140)
      	at xxx.xxx.C3P0Demo.main(C3P0Demo.java:14)
      Caused by: com.mchange.v2.resourcepool.TimeoutException: A client timed out while waiting to acquire a resource from com.mchange.v2.resourcepool.BasicResourcePool@490d6c15 -- timeout at awaitAvailable()
      	at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1467)
      	at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:644)
      	at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:554)
      	at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:758)
      	at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:685)
      	... 2 more
      
    2. 使用指定连接池对象:此时最大连接数为8,第9个连接在等待 1秒后会报错:
      import com.mchange.v2.c3p0.ComboPooledDataSource;
      
      import javax.sql.DataSource;
      import java.sql.Connection;
      import java.sql.SQLException;
      
      public class C3P0Demo {
          public static void main(String[] args) throws SQLException {
              //1.获取DataSource,使用指定名称配置
              DataSource ds = new ComboPooledDataSource("otherc3p0");
              //2.获取连接
              for (int i = 1; i <= 10; i++) {
                  Connection conn = ds.getConnection();
                  System.out.println(i + ":" + conn);
              }
          }
      }
      
      输出结果:
      1:com.mchange.v2.c3p0.impl.NewProxyConnection@27082746 [wrapping: com.mysql.jdbc.JDBC4Connection@66133adc]
      2:com.mchange.v2.c3p0.impl.NewProxyConnection@42f30e0a [wrapping: com.mysql.jdbc.JDBC4Connection@24273305]
      3:com.mchange.v2.c3p0.impl.NewProxyConnection@46f5f779 [wrapping: com.mysql.jdbc.JDBC4Connection@1c2c22f3]
      4:com.mchange.v2.c3p0.impl.NewProxyConnection@33e5ccce [wrapping: com.mysql.jdbc.JDBC4Connection@5a42bbf4]
      5:com.mchange.v2.c3p0.impl.NewProxyConnection@52d455b8 [wrapping: com.mysql.jdbc.JDBC4Connection@4f4a7090]
      6:com.mchange.v2.c3p0.impl.NewProxyConnection@6956de9 [wrapping: com.mysql.jdbc.JDBC4Connection@769c9116]
      7:com.mchange.v2.c3p0.impl.NewProxyConnection@2d6d8735 [wrapping: com.mysql.jdbc.JDBC4Connection@ba4d54]
      8:com.mchange.v2.c3p0.impl.NewProxyConnection@de0a01f [wrapping: com.mysql.jdbc.JDBC4Connection@4c75cab9]
      Exception in thread "main" java.sql.SQLException: An attempt by a client to checkout a Connection has timed out.
      

4. Druid

开源数据库连接池,由阿里巴巴提供。能够提供强大的监控和扩展功能。

  1. 步骤:

    • 导入jar包:druid-1.0.9.jar
    • 定义配置文件:任意名称的properties文件,可以放在任意目录下。
      driverClassName=com.mysql.jdbc.Driver
      url=jdbc:mysql:///db3
      username=root
      password=root
      # 初始化连接数量
      initialSize=5
      # 最大连接数
      maxActive=10
      # 最大等待时间
      maxWait=3000
      
    • 加载配置文件Properties;
    • 通过工厂DruidDataSourceFactory来获取数据库连接池对象;
    • 获取连接getConnection。
      import com.alibaba.druid.pool.DruidDataSourceFactory;
      
      import javax.sql.DataSource;
      import java.io.IOException;
      import java.io.InputStream;
      import java.sql.Connection;
      import java.util.Properties;
      
      public class DruidDemo {
          public static void main(String[] args) throws Exception {
              //1.加载配置文件
              Properties pro = new Properties();
              InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
              pro.load(is);
              //2.获取连接池对象
              DataSource ds = DruidDataSourceFactory.createDataSource(pro);
              //3.获取连接
              Connection conn = ds.getConnection();
              System.out.println(conn);
          }
      }
      
      输出结果:
      com.mysql.jdbc.JDBC4Connection@5d740a0f
      
  2. 定义工具类JDBCUtils:

    1. 提供静态代码块加载配置文件,初始化连接池对象。
    2. 提供方法:
      1. 获取连接方法:通过数据库连接池获取连接;
      2. 释放资源;
      3. 获取连接池的方法。
    package com.ernest.utils;
    
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    
    import javax.sql.DataSource;
    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    
    public class JDBCUtils {
        //1.定义成员变量 DataSource
        private static DataSource ds;
    
        static {
            try {
                //1.加载配置文件
                Properties pro = new Properties();
                pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
                //2.获取DataSource
                ds = DruidDataSourceFactory.createDataSource(pro);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取连接池方法
         */
        public static DataSource getDataSource() {
            return ds;
        }
    
        /**
         * 获取连接
         */
        public static Connection getConnection() throws SQLException {
            return ds.getConnection();
        }
    
        /**
         * 释放资源
         */
        public static void close(Statement stmt, Connection conn) {
            close(null, stmt, conn);
        }
        
        public static void close(ResultSet rs, Statement stmt, Connection conn) {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
    
            if (conn != null) {
                try {
                    conn.close();//归还连接
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    测试类:

    import com.ernest.utils.JDBCUtils;
    
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    
    public class DruidDemo02 {
        public static void main(String[] args) {
            //完成添加操作:给account表添加一条记录
            Connection conn = null;
            PreparedStatement pstmt = null;
            try {
                //1.获取连接
                conn = JDBCUtils.getConnection();
                //2.定义sql
                String sql = "insert into account values(null,?,?)";
                //3.获取pstmt对象
                pstmt = conn.prepareStatement(sql);
                //4.给?赋值
                pstmt.setString(1, "王五");
                pstmt.setDouble(2, 3000);
                //5.执行sql
                int count = pstmt.executeUpdate();
                System.out.println(count);
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                //6.释放资源
                JDBCUtils.close(pstmt, conn);
            }
        }
    }
    
    输出结果:
    1
    

5. JdbcTemplate

  1. 概念:
    Spring框架对JDBC做的封装,通过模板设计模式帮助我们消除了冗长的代码,在尽可能保留灵活性的情况下,简化了JDBC操作。可以看作是一个小型的轻量级持久化层框架,和DBUtils风格非常接近。
  2. 步骤:
    1. 导入jar包:
      spring-tx-5.0.0.RELEASE.jar
      spring-beans-5.0.0.RELEASE.jar
      spring-core-5.0.0.RELEASE.jar
      spring-jdbc-5.0.0.RELEASE.jar
      commons-logging-1.2.jar
    2. 创建JdbcTemplate对象,依赖于数据源DataSource:
      JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
      
    3. 调用JdbcTemplate的方法来完成CRUD的操作:
      • update(...) :执行DML语句(增、删、改);

        // 增
        String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
        int count = template.update(sql, 1015, "郭靖", 10);
        System.out.println(count);
        
        // 删
        String sql = "delete from emp where id = ?";
        int count = template.update(sql, 1015);
        System.out.println(count);
        
        // 改
        String sql = "update emp set salary = 10000 where id = 1001";
        int count = template.update(sql);
        System.out.println(count);
        
      • queryForMap(...) :查询结果将结果集封装为Map集合,将列名作为key,将值作为value 将这条记录封装为一个Map集合

          String sql = "select * from emp where id = ? or id = ?";
          Map<String, Object> map = template.queryForMap(sql, 1001,1002);
          System.out.println(map);
        

        注意:这个方法查询的结果集长度只能是1

      • queryForList(...) :查询结果将结果集封装为list集合

        String sql = "select * from emp";
        List<Map<String, Object>> list = template.queryForList(sql);
        
        for (Map<String, Object> stringObjectMap : list) {
        	System.out.println(stringObjectMap);
        }
        

        注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中

      • query(...) :查询结果,将结果封装为JavaBean对象,参数为RowMapper

        String sql = "select * from emp";
        List<Emp> list = template.query(sql, new RowMapper<Emp>() {
        	@Override
        	public Emp mapRow(ResultSet rs, int i) throws SQLException {
        		Emp emp = new Emp();
        		int id = rs.getInt("id");
        		String ename = rs.getString("ename");
        		int job_id = rs.getInt("job_id");
        		int mgr = rs.getInt("mgr");
        		Date joindate = rs.getDate("joindate");
        		double salary = rs.getDouble("salary");
        		double bonus = rs.getDouble("bonus");
        		int dept_id = rs.getInt("dept_id");
        		
        		emp.setId(id);
        		emp.setEname(ename);
        		emp.setJob_id(job_id);
        		emp.setMgr(mgr);
        		emp.setJoindate(joindate);
        		emp.setSalary(salary);
        		emp.setBonus(bonus);
        		emp.setDept_id(dept_id);
        		
        		return emp;
        	}
        });
        
        for (Emp emp : list) {
        	System.out.println(emp);
        }
        

        一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装,参数为new BeanPropertyRowMapper<类型>(类型.class)

        String sql = "select * from emp";
        List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
        for (Emp emp : list) {
        	System.out.println(emp);
        }
        
      • queryForObject :查询结果,将结果封装为对象,一般用于聚合函数的查询

        String sql = "select count(id) from emp";
        Long total = template.queryForObject(sql, Long.class);
        System.out.println(total);
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值