文档章节

JDBC学习笔记——简单的连接池

rakshasa
 rakshasa
发布于 2014/12/27 16:33
字数 1326
阅读 11
收藏 0

1、使用LinkedList保存连接                                                               

      即使是最简单的JDBC操作,也需要包含以下几步:建立连接、创建SQL语句、执行语句、处理执行结果、释放资源,其中建立连接步骤是很耗费计算机性能的,如果我们每次进行JDBC操作都创建新的JDBC连接,使用完后再立即释放连接,这样做会耗费大量性能。更合理的做法应该是:创建JDBC连接,使用JDBC连接,使用完后不是立刻释放JDBC连接,而是把连接缓存起来,当下一次操作JDBC时,我们可以直接使用缓存中已经连接上的JDBC连接。

      以下的代码通过一个LinkedList保存创建的JDBC连接,在刚创建的时候,我们会先建立10个连接并保存在list中,当客户代码需要使用Connection的时候,我们直接从list中取出第一个Connection返回,当客户代码使用完Connection,需要调用free()方法,把Connection再放回list中:

public class DataSource1 {
	LinkedList<Connection> connectionPool = new LinkedList<Connection>();

	public DataSource1() {
		try {
			for (int i = 0; i < 10; i++) {
				this.connectionPool.addLast(this.createConnection());
			}
		} catch (SQLException e) {
			throw new ExceptionInInitializerError(e);
		}
	}
	
	public Connection getConnection() {
		return connectionPool.removeFirst();
	}
	
	public void free(Connection conn) {
		connectionPool.addLast(conn);
	}
	
	private Connection createConnection() throws SQLException {
		return DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc",
				"root", "");
	}
}

2、控制连接数量                                                                             

      在上例的getConnection()方法中,我们没有检测connectionPool中是否包含Connection,直接返回connectionPool中的第一个Connection,这样做是不安全的,在从connectionPool中返回Connection之前,我们首先应该检测connectionPool中是否有Connection,若有,返回connectionPool中的第一个Connection,如果没有,我们可以新创建一个并返回。但是这样有一个问题,如果请求的线程很多,我们这样无限制地创建很多Connection可能导致数据库阻塞,因为数据库可以支持的连接数是有限的,所以我们应该控制新建Connection的上限,若没有达到上限,我们可以创建并返回,如果达到连接上限,那么我们就抛出异常。同时我们应该在getConnection()上加锁,保证多线程安全性,修改后代码如下,我们用initCount表示list初始化大小,maxCount表示list最大大小,currentCount表示现在存活的Connection数量:

public class DataSource2 {
	private static int initCount = 10;
	private static int maxCount = 30;
	private int currentCount = 0;

	LinkedList<Connection> connectionPool = new LinkedList<Connection>();

	public DataSource2() {
		try {
			for (int i = 0; i < initCount; i++) {
				this.connectionPool.addLast(this.createConnection());
				this.currentCount++;
			}
		} catch (SQLException e) {
			throw new ExceptionInInitializerError(e);
		}
	}

	public Connection getConnection() throws SQLException {
		synchronized (connectionPool) {
			if (connectionPool.size() > 0)
				return connectionPool.removeFirst();

			if (this.currentCount < maxCount) {
				this.currentCount++;
				return createConnection();
			}

			throw new SQLException("已没有链接");
		}
	}

	public void free(Connection conn) {
		connectionPool.addLast(conn);
	}

	private Connection createConnection() throws SQLException {
		return DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc",
				"root", "");
	}
}

3、动态代理拦截close方法                                                          

      上面的代码有个问题,那就是要关闭Connection必须调用我们的free()方法,不能直接调用Connection上的close()方法,这对于一些习惯使用完Connection就close()的用户并不是很友好,很容易导致它们忘记把用完的Connection还回connectionPool,为了让用户保持原有习惯,我们希望能够改写Connection的close()方法,让它不是直接关闭连接,而是把连接还回connectionPool中,其他方法保持原来不变,对于这种需求,我们可以使用动态代理实现:

public class DataSource3 {
	private static int initCount = 1;
	private static int maxCount = 1;
	int currentCount = 0;

	LinkedList<Connection> connectionsPool = new LinkedList<Connection>();

	public DataSource3() {
		try {
			for (int i = 0; i < initCount; i++) {
				this.connectionsPool.addLast(this.createConnection());
				this.currentCount++;
			}
		} catch (SQLException e) {
			throw new ExceptionInInitializerError(e);
		}
	}

	public Connection getConnection() throws SQLException {
		synchronized (connectionsPool) {
			if (this.connectionsPool.size() > 0)
				return this.connectionsPool.removeFirst();

			if (this.currentCount < maxCount) {
				this.currentCount++;
				return this.createConnection();
			}

			throw new SQLException("已没有链接");
		}
	}

	public void free(Connection conn) {
		this.connectionsPool.addLast(conn);
	}

	private Connection createConnection() throws SQLException {
		Connection realConn = DriverManager.getConnection(
				"jdbc:mysql://localhost:3306/jdbc", "root", "");
		MyConnectionHandler proxy = new MyConnectionHandler(this);
		return proxy.bind(realConn);
	}
}


class MyConnectionHandler implements InvocationHandler {
	private Connection realConnection;
	private Connection warpedConnection;
	private DataSource3 dataSource;

	MyConnectionHandler(DataSource3 dataSource) {
		this.dataSource = dataSource;
	}

	Connection bind(Connection realConn) {
		this.realConnection = realConn;
		this.warpedConnection = (Connection) Proxy.newProxyInstance(this
				.getClass().getClassLoader(), new Class[] { Connection.class },
				this);
		return warpedConnection;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		if ("close".equals(method.getName())) {
			this.dataSource.connectionsPool.addLast(this.warpedConnection);
		}
		return method.invoke(this.realConnection, args);
	}
}

  上述代码的关键点是MyConnectionHandler类,在该类的bind()方法中返回了一个wrapedConnection,wrapedConnection的创建方法如下:

this.warpedConnection = (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), 
        new Class[] { Connection.class },this);

  Proxy.newProxyInstance()方法是Java动态代理的关键方法,该方法会在运行时在内存中动态创建一个类,该方法的第一个参数是指定一个ClassLoader,第二个参数指定动态创建类实现的接口,第三个参数指定在该类上调用的方法应该转给哪个类,在这里指定了this,所以所有方法都会转给MyConnectionHandler,准确的说是MyConnectionHandler的invoke方法:

public Object invoke(Object proxy, Method method, Object[] args)
		throws Throwable {
	if ("close".equals(method.getName())) {
		this.dataSource.connectionsPool.addLast(this.warpedConnection);
	}
	return method.invoke(this.realConnection, args);
}

  在invoke()方法中,我们可以看到第二个参数传递了调用的Method,我们根据传递的Method对象判断,用户调用的是否是close方法,如果是close方法,那么我们就把这个Connection重新加入list,如果不是,那么我们就执行真正Connection上的相应方法。

      还需要注意的是,现在调用createConnection()产生的Connection对象已经不是原始的Connection对象,而是调用MyConnectionHandler类上bind()方法动态产生的代理类:

private Connection createConnection() throws SQLException {
	Connection realConn = DriverManager.getConnection(
			"jdbc:mysql://localhost:3306/jdbc", "root", "");
	MyConnectionHandler proxy = new MyConnectionHandler(this);
	return proxy.bind(realConn);
}

 

本文转载自:http://www.cnblogs.com/timlearn/p/4080169.html

rakshasa
粉丝 34
博文 142
码字总数 16820
作品 0
昆明
高级程序员
私信 提问
【Java学习路线】新手该如何一步步的学习 Java

新手该如何一步步的学习 Java? 如果真的想学Java,最好要循序渐进,有章有法的学习它! 今天小慕就不说一些学习方法和技巧了,直接来谈每个阶段要学习的内容。 首先,给大家分享一张以 企业...

Eddie_yang
2018/11/15
1K
0
jvm内存模型-深入理解jvm

  最近学习了周志明老师的《深入理解Java虚拟机》,收获颇多,留下一些学习笔记,供以后复习用。   JVM定义了若干个程序执行期间使用的数据区域。这个区域里的一些数据在JVM启动的时候创...

Swen_9826
2018/08/09
0
0
java 私塾随堂笔记——Hibernate(上)

java 私塾随堂笔记——Hibernate(上) 第一章 Hibernate介绍 一.是什么: Hibernate是轻量级的ORM框架。 1.ORM(ORMapping) Object——Mapping——→Relation DB Object←——JDBC———Rel...

luodis
2011/03/21
444
0
析评数据库连接池(创世纪新篇)

学习Java语言,对于数据库连接池不陌生啦。例如,一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不...

crossmix
2015/08/21
31
0
Spring笔记8---数据库持久化

聊聊持久化。 ------------------------Spring的平台无关持久化异常 下面是Spring提供的数据访问模板,分别适用于不同的持久化机制 模板类org.springframework.* 用途 jca.cci.core.CciTemp...

强子哥哥
2015/01/12
64
0

没有更多内容

加载失败,请刷新页面

加载更多

哪些情况下适合使用云服务器?

我们一直在说云服务器价格适中,具备弹性扩展机制,适合部署中小规模的网站或应用。那么云服务器到底适用于哪些情况呢?如果您需要经常原始计算能力,那么使用独立服务器就能满足需求,因为他...

云漫网络Ruan
今天
5
0
Java 中的 String 有没有长度限制

转载: https://juejin.im/post/5d53653f5188257315539f9a String是Java中很重要的一个数据类型,除了基本数据类型以外,String是被使用的最广泛的了,但是,关于String,其实还是有很多东西...

低至一折起
今天
17
0
OpenStack 简介和几种安装方式总结

OpenStack :是一个由NASA和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目。项目目标是提供实施简单、可大规模扩展、丰富、标准统一的云计算管理平台。OpenSta...

小海bug
昨天
11
0
DDD(五)

1、引言 之前学习了解了DDD中实体这一概念,那么接下来需要了解的就是值对象、唯一标识。值对象,值就是数字1、2、3,字符串“1”,“2”,“3”,值时对象的特征,对象是一个事物的具体描述...

MrYuZixian
昨天
9
0
解决Mac下VSCode打开zsh乱码

1.乱码问题 iTerm2终端使用Zsh,并且配置Zsh主题,该主题主题需要安装字体来支持箭头效果,在iTerm2中设置这个字体,但是VSCode里这个箭头还是显示乱码。 iTerm2展示如下: VSCode展示如下: 2...

HelloDeveloper
昨天
9
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部