为什么要引入连接池

一、问题

当应用在执行持久化数据到数据库的时候,会使用jdbc连接数据库,并插入数据

我们都知道jdbc在连接数据库分成如下步骤

  • 建立数据库连接
  • 增删改查数据
  • 关闭数据库连接

如果有上万个请求同时进行持久化的操作,那就意味着开关数据库的次数达到上万次,这样会带来一些问题

1、如果持久化的时候出现异常,导致数据库没有正确的关闭连接,会造成大量的连接积压在数据库的会话列表中,出现数据库无法访问的情况。(专有名词:数据库连接泄漏)

2、假如每次插数开关操作整体耗费1秒的话,那么上万条请求同时执行,开关带来的系统负担是巨大的,而且是无意义的。

二、介绍连接池

什么是数据库连接池:听名字就知道,这是一个池子,里面装了很多连接,连接被池子进行管理。

数据库连接池的职责:添加连接,管理连接、释放连接

数据库连接池的连接数:分为最大连接数和最小连接数,旨在将数据库的连接维持在一定的范围内,应用程序可以使用已经打开的连接去访问数据库,相当于市面上投入最少五个最多十个共享单车,供一百个人使用,一个人用完后不会去解锁,而是转交给另外的人用,这就加快了使用效率。(第一个解锁的人是土豪= 。=)

三、创建连接池

编写连接池需实现 java.sql.DataSource 接口,并先初始化连接链表。

public class DataSourcetest implements DataSource{
	
	//定义一个连接池 用linkedlist是为了增加删除插入的速度
	private static List<Connection> connectionList = new LinkedList<Connection>();

	static{
		//获取资源文件中的数据库连接配置文件
//		jdbc.driverClassName=com.mysql.jdbc.Driver
//		jdbc.url=jdbc:mysql://127.0.0.1:3306/umbrelladatabase?useUnicode=true&characterEncoding=UTF-8
//		jdbc.host=127.0.0.1
//		jdbc.username=root
//		jdbc.password=root
		InputStream is = DataSourcetest.class.getClassLoader().getResourceAsStream("load.properties");
		Properties p = new Properties();
		try{
			p.load(is);
			//获取配置文件中的属性
			String driverClassName = p.getProperty("jdbc.driverClassName");
			String url = p.getProperty("jdbc.url");
			String host = p.getProperty("jdbc.host");
			String username = p.getProperty("jdbc.username");
			String password = p.getProperty("jdbc.password");
			String poolSize = p.getProperty("jdbc.poolSize");
			int jdbcPoolSize = Integer.parseInt(poolSize);
			//获取driver的名称
			Class.forName(driverClassName);
			//根据连接池大小,初始化连接并加入链表中
			for(int i=0;i<jdbcPoolSize;i++){
				Connection conn = DriverManager.getConnection(url, username, password);
				connectionList.add(conn);
			}
		}catch(Exception e){
			e.printStackTrace();
			for(int i=0;i<connectionList.size();i++){
				Connection conn = connectionList.get(i);
				try {
					conn.close();
				} catch (SQLException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		}
	}.........}

之后需要重写getConnection方法:

	@Override
	public Connection getConnection() throws SQLException {
		if(connectionList.size()>0){
			Connection conn = connectionList.get(0);connectionList.remove(0);
			return (Connection) Proxy.newProxyInstance(DataSourcetest.class.getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler(){
				@Override
				public Object invoke(Object proxy, Method method, Object[] args)
						throws Throwable {
					 if(!method.getName().equals("close")){
                                             return method.invoke(conn, args);
                                         }else{
                                             //如果调用的是Connection对象的close方法,就把conn还给数据库连接池
                                             connectionList.add(conn);
                                             System.out.println(conn + "被还给listConnections数据库连接池了!!");
                                             System.out.println("connectionList数据库连接池大小为" + connectionList.size());
                                             return null;
                                         }
				} 
			});
		}
		return null;
	}

这里额外说下Proxy.newProxyInstance 代理类的创建:

Proxy的模式最主要的目的,原有的类对象由于某种原因不能访问,需要通过一个新的类来间接地去实现,这个新的类就称为代理类

Proxy类的newInstance()方法有三个参数:

  • ClassLoader loader:它是类加载器类型,你不用去理睬它,你只需要知道怎么可以获得它就可以了:DataSourcetest.class.getClassLoader()就可以获取到ClassLoader对象
  • Class[] interfaces:指定newProxyInstance()方法返回的对象要实现哪些接口,没错,可以指定多个接口,例如上面例子只我们只指定了一个接口:Class[] cs = {Connection .class};
  • InvocationHandler h:它的名字叫调用处理器,其实无论你调用代理对象的什么方法,它都是在调用InvocationHandler的invoke()方法!
InvocationHandler的invoke()方法的参数有三个:
  • Object proxy:代理对象,也就是Proxy.newProxyInstance()方法返回的对象,通常我们用不上它;
  • Method method:表示当前被调用方法的反射对象,例如method.getName().equals("close"),那么method就是close()方法的反射对象;
  • Object[] args:表示当前被调用方法的参数,当然close()这个方法调用是没有参数的,所以args是一个零长数组。

四、开源连接池

    DBCP 数据库连接池

    C3P0 数据库连接池

    Druid连接池

    HikariCP连接池 

    BoneCP连接池


简单介绍一下Druid:

Druid是一个JDBC组件,它包括三部分: 

  • DruidDriver 代理Driver,能够提供基于Filter-Chain模式的插件体系。 
  • DruidDataSource 高效可管理的数据库连接池。
  • SQLParser 
Druid可以做什么? 

1) 可以监控数据库访问性能,Druid内置提供了一个功能强大的StatFilter插件,能够详细统计SQL的执行性能,这对于线上分析数据库访问性能有帮助。 

2) 替换DBCP和C3P0。Druid提供了一个高效、功能强大、可扩展性好的数据库连接池。 

3) 数据库密码加密。直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver和DruidDataSource都支持PasswordCallback。 

4) SQL执行日志,Druid提供了不同的LogFilter,能够支持Common-Logging、Log4j和JdkLog,你可以按需要选择相应的LogFilter,监控你应用的数据库访问情况。 
扩展JDBC,如果你要对JDBC层有编程的需求,可以通过Druid提供的Filter-Chain机制,很方便编写JDBC层的扩展插件。 

猜你喜欢

转载自blog.csdn.net/qq_31615049/article/details/80685691