JavaWeb(八)JDBC——数据库连接池

上一章链接:JDBC——MySQL

数据库连接池是为了提高性能而出现的,先了解连接池的相关概念再往下深入。

目录

数据库连接池

    引语

    概念

    连接池的几个参数

    DBCP连接池

    C3P0连接池

JNDI

    概念

    作用

    配置格式

    代码用例

dbUtils

    原理

    两大核心类

    例子

后话


数据库连接池

    引语

在Java开发中,使用JDBC操作数据库无非就是那四个步骤:①加载驱动类、②连接数据库、③操作数据库、④关闭数据库。而在编写代码过后,发现步骤①②④是一样的,只有③操作数据库不一样,那么这样就会造成性能降低的问题出现。因此,数据库连接池就应势而生。

    概念

数据库连接池(Connection pooling)是程序启动时建立足够的数据库连接,并将这些连接组成一个连接池,由程序动态地对连接池中的连接进行申请,使用,释放。

上面的比较官方,先得提及池技术。池技术可以优化服务器应用程序的性能,提高执行效率和降低开销。池就类似于现实世界的工具箱,当要拆卸风扇时,需要用到工具箱内的螺丝刀,用完后放回工具箱内以下次使用。这就是池的作用。

因此数据库连接池的基本思想就是为数据库连接建立一个缓冲池,在缓冲池内预先存入一定数量的连接。当需要建立数据库连接时,只需要从缓冲池内拿出一个连接,使用后再放置回缓冲池里,这样就大大地减少了性能的损耗。

下面用图来附加理解连接池:

JavaWeb(八)JDBC——数据库连接池_第1张图片

    连接池的几个参数

initialSize:连接池初始的连接对象数。

minIdle:连接池中最小的空闲连接数,当连接数小于此值时,连接池会创建连接补充连接池。

maxIdle:连接池中最大的空闲连接数,当没有数据库连接时表示保持待命状态的连接数目。

maxActive:连接池支持的最大连接数,即最多只有N个连接对象(N为任意数)。

maxWait:连接池中的连接用完时,下一个请求的等待时间。

这里以工厂代池为例:工厂规模只能容纳40个工人(maxActive),初始只有4人(initialSize),而最小的空闲工人数(minIdle)为5人,此时就需要再去招收1个工人回来。当招募完毕后,为了保证工厂的收益,最多只能空闲20人(maxIdle),即当空闲人数大于20人时,要裁人至20个空闲工人。当40个工人都在工作时,突然增加了一个工作,由于工厂的规模限制,无法再招收工人,那么只能等待工厂中的工人完成手中工作,而此时等待的时间即为maxWait,如果超过等待时间则放弃这个工作。

    DBCP连接池

dbcp连接池是Java数据库连接池的其中一种,若要使用连接池需导入Apache开发的两个jar包:commons-dbcp和commons-pool。

以下为演示其用法:

    public void func1() throws SQLException {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/student");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        
        dataSource.setMaxActive(30);
        dataSource.setMinIdle(4);
        dataSource.setMaxWait(1000);
        
        Connection con = dataSource.getConnection();
        System.out.println(con.getClass().getName());
    }

结果如下所示:

在这之中需要说明的是,连接池内部使用了四大参数(即驱动类名称、路径、用户名以及密码)创建了MySQL驱动提供的连接对象Connection。而在连接池使用这个Connection对象进行了装饰,对其中的close()方法进行了更改,用于将当前连接归还给连接池。(这里涉及到了装饰者模式的概念)

    C3P0连接池

C3P0相比于DBCP连接池有自动回收空闲连接功能。若要使用C3P0连接池也需要导入jar包:c3p0-0.9.2.1。

以下为演示代码:

    public void func2() throws SQLException, PropertyVetoException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        ds.setDriverClass("com.mysql.jdbc.Driver");
        ds.setJdbcUrl("jdbc:mysql://localhost:3306/student");
        ds.setUser("root");
        ds.setPassword("123456");
		
        ds.setInitialPoolSize(10); // 初始化连接数
        ds.setAcquireIncrement(5); // 设置增量为5
        ds.setMinPoolSize(5); // 设置最小连接数
        ds.setMaxPoolSize(20); // 设置最大连接数
		
        Connection con = ds.getConnection();
        System.out.println(con);
        con.close(); // 归还连接到连接池
    }

C3P0亦可以指定使用配置文件,但是配置文件的名称必须是c3p0-config.xl,并必须放在类的路径下。其会寻找配置文件。

配置文件格式如下所示:


  

    
        com.mysql.jdbc.Driver
        jdbc:mysql://localhost:3306/student
        root
        123456
		
        10
        5
        4
        20
    

用例代码如下所示:

    @Test
    public void func3() throws SQLException {
        ComboPooledDataSource ds = new ComboPooledDataSource();
        Connection con = ds.getConnection();
        System.out.println(con);
    }

需要说明的一点:在配置文件中,除了默认配置default-config以外,还有一个自定义命名标签named-config。而在ComboPooledDataSource类的构造方法里有一个带参的构造方法来加载配置文件指定的配置。


JNDI

    概念

JNDI是 Java 命名与目录接口(Java Naming and Directory Interface),是J2EE非常重要的一个规范之一。

    作用

JDNI的作用是在服务器上配置资源,然后通过统一的方式来获取配置的资源。实际上,这个作用就降低了程序和数据库之间的耦合性。

    配置格式

配置JNDI资源需要到元素中配置子元素:

    name:指定资源的名称,该名称用于获取资源

    factory:用来创建资源的工厂,固定值

    type:资源的类型

    _+:表示资源的属性,资源存在什么名字的属性即配置什么名字的属性的值。如连接池需要配置driveClass等属性。

现在以配置连接池为例,如下所示:


    

该配置放置于tomcat根目录下的conf文件夹下的server.xml内,也可以单独创建一个和项目名一样的xml在conf下的Catalina的localhost文件夹下。

    代码用例

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws 
            ServletException, IOException {
        Context cxt;
        Connection con = null;
        try {
            cxt = new InitialContext();
            DataSource dataSource = (DataSource) cxt.lookup(
                "java:comp/env/jdbc/dataSource");
            con = dataSource.getConnection();
            System.out.println("JNDI:" + con);
        } catch (NamingException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(con != null)
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }			
        }
    }

Context是javax.naming.Context代表了资源的上下文环境,初始化上下文环境后,通过lookup()方法查找资源。其中,固定资源入口是java:comp/env,而后续的目录则是对应Resource中配置的name值,返回获取的是一个资源对象。


dbUtils

dbUitls是Java中对数据库操作的一个实用工具,其里面封装了对JDBC的操作,简化了大量重复使用的代码,以提高效率。在介绍以及讲述功能使用之前,先讲述一下原理。

    原理

为了便于理解以及记忆,需要知道dbUtils中内部是如何封装对JDBC的操作。

首先先准备好一个连接池的小工具,将其命名为jdbcUtils,代码如下所示:

    public class JdbcUtils {
        private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
        
        public static ComboPooledDataSource getDataSource() {
            return dataSource;
        }

        public static Connection getConnection() throws SQLException {
            return dataSource.getConnection();
        }
    }

接着再来创建一个类叫做QR,里面包含了对数据库的操作,代码如下所示:

public class QR {
    private DataSource dataSource;
	
    public QR(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public int update(String sql, Object... params) {
        Connection con = null;
        PreparedStatement pstmt = null;
        try {
            con = dataSource.getConnection();
            pstmt = con.prepareStatement(sql);
            initParams(pstmt, params); // 对模板的参数初始化
			
            return pstmt.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if(pstmt != null) pstmt.close();
                if(con != null) con.close();
            } catch (SQLException e1) {
                throw new RuntimeException(e1);
            }    
        }		
    }
	
    private void initParams(PreparedStatement pstmt, 
                    Object[] params) throws SQLException{
        for(int i = 0; i < params.length; i++)
            pstmt.setObject(i+1, params[i]);
    }

    public T query(String sql, RsHandler rh, Object... params) {
        Connection con = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            con = dataSource.getConnection();
            pstmt = con.prepareStatement(sql);
            initParams(pstmt, params);
            rs = pstmt.executeQuery();
            return (T) rh.handle(rs);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if(pstmt != null) pstmt.close();
                if(con != null) con.close();
            } catch (SQLException e1) {
                throw new RuntimeException(e1);
            }
        }
    }
}

再来定义一个接口,命名为RsHandler,其用于对返回的结果集的处理方式,代码如下所示:

    public interface RsHandler {
        public T handle(ResultSet rs) throws SQLException;
    }

最后写一个调用例子,测试其效果,代码如下所示:

    @Test
    public void fun1() throws SQLException {    
        Student stu = new Student("100", "zhangsan", 18);
        addStu(stu);
		
        System.out.println(loadStu("100"));
    }
	
    public void addStu(Student stu) {
        QR qr = new QR(JdbcUtils.getDataSource());
        String sql = "insert into message values(?, ?, ?)";
        Object[] params = {stu.getNumber(), stu.getName(), stu.getAge()};
        qr.update(sql, params);
    }
	
    public Student loadStu(String number) throws SQLException{
        QR qr = new QR(JdbcUtils.getDataSource());
        String sql = "select * from message where number = ?";
        Object[] params = {number};
		
        Student stu =  (Student) qr.query(sql, new RsHandler() {

            @Override
            public Student handle(ResultSet rs) throws SQLException {
                if( !rs.next() ) return null;
                Student stu = new Student();
                stu.setNumber(rs.getString("number"));
                stu.setName(rs.getString("name"));
                stu.setAge(rs.getInt("age"));
                return stu;
            }
			
        }, params);
		
        return stu;
    }

结果如下所示:

而dbutils内部即是这样实现的,只是名字 稍微有些不同。

    两大核心类

QueryRunner类:提供对SQL语句操作的API

ResultSetHandler类:接口类,需要实现将结果集转成对象模型的操作。

    例子

在DbUtils中分别实现了ResultSetHandler类来处理各种不同情况。第一种是将单行记录转换成Bean对象(即BeanHandler)

    // @Test
    public void fun2() throws SQLException {
        QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "select * from message where number = ?";
        Object[] params = {1001};
        Student stu = qr.query(sql, new BeanHandler(Student.class), params);
        System.out.println(stu);
    }

显示结果如下所示:

第二种是将多行记录转换成List对象(即BeanListHandler)

    @Test
    public void fun3() throws SQLException {
        QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "select * from message";
        List list = qr.query(sql, new BeanListHandler(Student.class));
        System.out.println(list);
    }

结果如下所示:

第三种是将一行记录转换成一个Map对象(即MapHandler)

    @Test
    public void fun4() throws SQLException {
        QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "select * from message where number = ?";
        Object[] params = {1001};
        Map map = qr.query(sql, new MapHandler(), params);
        System.out.println(map);
    }

结果如下所示:

第四种是将每行记录转换成Map然后保存到List里(即MapListHandler)

    @Test
    public void fun5() throws SQLException {
        QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());
		String sql = "select * from message";
		List> mapList = qr.query(sql, new MapListHandler());
		System.out.println(mapList);
    }

结果如下所示:

第五种则适合统计时或者单行单列(即ScalarHandler)

    @Test
    public void fun6() throws SQLException {
        QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());
        String sql = "select count(*) from message";
        Object obj = qr.query(sql, new ScalarHandler());
        System.out.println(obj);
    }

结果如下所示:

后话

        '
            JDBC前半部分到此为止,先停一会
        '

 

你可能感兴趣的:(JavaWeb,JavaWeb,数据库连接池,C3P0)