为什么用连接池
-我们最初jdbc连接数据库需要
1. 安装驱动
2. 取得连接
3. 打开窗口
4. 书写语句、执行语句和查看结果
5. 关闭到数据库的连接
其中1,2和5完全可以重用,这样就加快了执行。也即:
同一个连接可以打开多个窗口
同一个窗口可以多次执行语句
我们发现jdbc连接数据库时对于同一个项目来说,如果每次操作数据库的话,每一次查询添加…,使用上面的方法就是要建立多次数据库连接,而每一次获得连接对于Java代码来说都是重复动作,不仅占用了空间,而且在执行效率上也产生了浪费
- 解决连接重用的方案分析
我们想象火车站售票大厅的情况,平时客流比较少时,我们只开几个售票窗口,不管有没有顾客来买票:多时间处于空闲状态。在如节假日等高峰期,所有售票窗口都会启用。特别地,在春运这样的最高峰期,可能会有临时窗口。但临时窗口也不是无限制地多开,开窗口要耗费物力人力。这样在高峰期只能让客户等待长一点的时间。春运最高峰时排队,这个时间客户就需要等待。
我们在重用数据库连接时也可以采用类似的方案。我们先打开一批连接等待客户使用(如同非高峰期我们开几个窗口) ,不管用没有客户使用。在高峰期到来时,我们可以多开几个连接让浏览器客户使用。如同春运一样不可能为了让每个人都有一个窗口服务,我们的数据库连接也不能无限开启(数据库连接要占用内存等资源,同时每个数据库能支持的并发连接数也不是无限的).在不能为需要的数据的客户立马服务时,只能让客户排队等待。如果用户一直无法得到一个连接对象时,就不要让用户无限期等待。
从上面可以看出,我们重用连接的方案,需要提供以下信息:
- 预先开启的连接数
- 能开启的最大连接数
- 一个连接要被多个客户使用,因而连接用完不能关闭
- 一个超时时间,如果超过这个时间,用户就无法获取连接而得到数据。
在JDBC连接数据库的连接重用方案里,我们称为连接池。
连接池中可以重用的包括Connection对象和Statement对象
连接池的作用及原理
1. 预先开启的连接数
2. 能开启的最大连接数
3. 一个连接要被多个客户使用,因而连接用完不能关闭
4. 一个超时时间,如果超过这个时间,用户就无法获取连接而得到数据。
自定义连接池
原理
定义一个集合来保存一定数量的connection连接
重写Connection的close方法—不真正关闭而是把它放置到连接池中
- 导入相关jar包
-这里使用的是Oracle数据库我们就导入ojdbc6.jar - jdbc.properties配置文件
-url=jdbc:oracle:thin:@localhost:1521:orcl
user=lgh
password=lgh
className=oracle.jdbc.OracleDriver
- 编写数据库连接工具类
package com.jt.demo07;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* 数据库连接工具类
* 此类主要是加载驱动创建连接
* */
public class JDBCUnti {
private static String url = null;
private static String user = null;
private static String password = null;
private static String className = null;
private static Properties into = new Properties();
static {
try {
// 读取文件和类在一起就用该方法
into.load(JDBCUnti.class.getResourceAsStream("jdbc.properties"));
url = into.getProperty("url");
user = into.getProperty("user");
password = into.getProperty("password");
className = into.getProperty("className");
} catch (IOException e) {
e.printStackTrace();
}
}
static {
try {
// DriverManager.registerDriver(new OracleDriver());
// 这种注册方式实际上注册了两次驱动
Class.forName(className);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
public static void free(Statement stmt, Connection conn) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void free(ResultSet res, Statement stmt, Connection conn) {
if (res != null) {
try {
res.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
free(stmt, conn);
}
}
编写连接池需实现java.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:
Connection getConnection()
Connection getConnection(String username, String password)
实现DataSource接口,并实现连接池功能的步骤:
在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。
实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。
当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到List中,而不要把conn还给数据库。Collection保证将自己返回到List中是此处编程的难点。
package com.jt.demo07;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.sql.DataSource;
/**
* List 集合模拟一个池子 存放lianjie
* 静态代码块在类被加载时就获得执行
* 预期存入5个数据库连接
* */
public class MyDatasource implements DataSource {
public static List<Connection> pool = new ArrayList<Connection>();
static{
for(int i=0;i<5;i++){
try {
pool.add(JDBCUnti.getConnection());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public Connection getConnection() throws SQLException {
if(pool.size()>0){
final Connection conn = pool.remove(0);
System.out.println("conn de ++++"+conn.getClass());
return (Connection)Proxy.newProxyInstance(null, new Class[]{Connection.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这是一个方法内部类 通过动态代理的方法来实现对 Connection的close方法的//重写,以达到调用close方法不会关闭连接,而是把它放到连接池中
System.out.println("_____"+this.getClass());
if(method.getName().equals("close")){
System.out.println("aaaaaaaa"+this.getClass());
pool.add(conn);
}else{
return method.invoke(conn, args);
}
return null;
}
});
}
return null;
}
@Override
public Connection getConnection(String username, String password)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
编写Java程序测试
编写查询数据库操作代码
package com.jt.demo07;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import oracle.jdbc.OracleDriver;
public class JdbcSelect {
/**
* 测试MyDatasource连接池
*
* */
public static void main(String[] args) throws SQLException {
Connection conn = null;
Statement stmt = null;
ResultSet res = null;
MyDatasource md = new MyDatasource();
try {
conn = md.getConnection();
stmt = conn.createStatement();
String sql = "select * from t_user";
res = stmt.executeQuery(sql);
System.out.println("id\tname\tpwd\tage\tbirthday");
while (res.next()) {
System.out.println(res.getInt("id") + "\t"
+ res.getString("name") + "\t" + res.getString("pwd")
+ "\t" + res.getInt("age") + "\t"
+ res.getDate("birthday"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUnti.free(res, stmt, conn);
}
}
}
执行程序
第一次写博客,没什么经验,主要是想把学习的内容写出来,做个总结,有什么不足之处还请谅解