(二十)自定义数据库连接池

目录


使用数据库连接池优化数据库性能

之前我们对数据库的操作,其实是有很大问题的;

因为我们是每次操作数据库之前,都会问数据库要一个连接,用完之后,就把这个链接还给了数据库;

其实数据库连接是重量级的东西,数据库每次创建一个连接出来,都要花老大力气了;

因此,我们应该固定的保存着一些连接,这些连接用完以后,就放在那里,等下次再用,避免每次都问数据库要 ;

连接池

程序一启动,就问数据库要一定数量的连接,维护在一个地方(连接池);

这样每次,需要连接,就从池中获取,用完之后也不返回给数据库,而是继续保持在池中;起到复用连接的作用;

这种变化是很惊人的;以前10000次访问,就需要创建一千次连接。现在无论多少次访问,都只需要创建连接池中的连接数 ;

编写自定义数据库连接池

编写连接池需要实现 javax.sql.DataSource 接口。DateSource 接口中定义了 2 个重载 getConnection 的方法 :


    Connection getConnection() ;

    Connection getConnection(String username,String password) ;

  • 实现 DateSource 接口,并实现连接池功能的步骤

    a、在DateSource构造器中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中 ;
    
    b、实现getConnection方法,让getConnection方法每次调用时,从Linkedlist中取出一个连接返回给用户 ;
    
    c、当用户使用完Connection以后,调用Connection.close()方法时,Collection对象保证将自己返回到LinkedList中,而不是把conn返给数据库 ;
    
    其中Collection保证将自己返回给LinkedList中是此处编程的难点!!
    

在实现 Connection getConnection() 的时候,有个问题,就是我们不能简单的从LinkedList 里面获取 Connection 返回,而要是确保 linkedList 里面将按个Connection 对象移除了,否则会造成连接冲突;

还有一个问题,就是用户在用完 Connection 的时候,会习惯调用 close()方法,这样就导致了连接放给了数据库,而不是返给了我们连接池;


close()方法加强

现在就有一个问题,就是如何保证调用 Connection的close()方法,是将连接还给连接池而不是数据库 ?

这就涉及到,我们对一个类功能进行加强的问题;

在java里面,通常有三种方法;



a、写一个子类,覆写父类的方法

        这个做法有个致命的缺点:就是必须获得父类的所有信息,向我们这里,继承是基本不可能的!

    因为,真正返回给我们的连接是,mysql的驱动类生成的连接,这个类里面,封装了太多的信息:比如:连接的数据库地址、数据库名字、用户名、连接密码。。这些信息 ;

    我们要真的想继承它,我们就要在子类中得到这些信息,可惜人家都说private字段,子类也拿不着;子类拿不着,那么子类就连不上数据库。。

    除非你去看去mysql的驱动源码,自己把信息拿出来,写在你创建的子类里面,但这跟重写mysql驱动没哈区别了,工程量吓人!

    因此,这里放弃这种做法 ;




b、用包装模式

    写法:

        第一步:定义一个类实现与被增强类相同的接口

        第二步:在类中定义一个变量,记住被增强对象

        第三步:定义一个构造器,参数接受一个被增强对象的类 

        第四步:覆盖我们向增强的方法 ;

        第五步:对于不想增强的方法,直接调用被增强对象的方法 ;


        这样就完美的增强了我们想要的方法;但是也有缺点,就是接口中的每个方法,都要我们去调用被增强对象的方法 ;

    接口中的方法少还行,一旦很多,这种代码,我们就要写很多次,,,,,很恶心!

    因此,应该考虑动态代理 ;       


c、动态代理(aop编程,面向切面编程)

    主要是利用拦截;(暂时,我也不会,以后会跟,最多20天以内更新。写于2018年6月12日22:07:53)

代码:

自定义连接池代码。。


public class MyDataSource implements javax.sql.DataSource {

    private static LinkedList<Connection> connections = new LinkedList<>();

    static {
        try {
            //        初始化10个连接出来
            for (int i = 0; i < 10; i++) {
                connections.add(new MyConnection(JdbcUtils.getConnection()));
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new ExceptionInInitializerError(e);
        }
    }

    private static MyDataSource myDataSource = new MyDataSource();

    public static MyDataSource getDataSource() {
        return myDataSource;
    }

    private MyDataSource() {
    }

    @Override
    public Connection getConnection() throws SQLException {

        if (connections.size() < 1) {
            throw new SQLException("数据库连接正忙,请稍后再试...");
        }

        return connections.removeFirst();
    }


    ....

    ....

    ....

    // 包装器类 ,对MyDataSource类 进行增强
    static class MyConnection implements Connection {
        private Connection connection;


        public MyConnection(Connection connection) {
            this.connection = connection;
        }

    // 其他方法,直接调用 connection的方法。。
        @Override
        public Statement createStatement() throws SQLException {
            return connection.createStatement();
        }

        @Override
        public PreparedStatement prepareStatement(String sql) throws SQLException {
            return connection.prepareStatement(sql);
        }

    ....

    //  重点关注 close方法,对其进行增强,不将连接返回给数据库
    @Override
        public void close() throws SQLException {
            System.out.println("连接池中的剩余连接量:"+connections.size());
            connections.add(this) ;
            System.out.println("成功拦截关闭!将它们返给连接池。。");

            System.out.println("连接池中的剩余连接量:"+connections.size());
        }


    ....

    // 还有许多其他方法,不一一列举了,太多了,这也是包装模式的缺点 。。。



posted @ 2018-06-12 22:18  Yiaz  阅读(103)  评论(0编辑  收藏  举报