用动态代理实现连接池
1、用javax.sql.DataSource实现连接池。
DataSource是标准的数据源。所在连接数据库的工具类,都应该实现这个接口。
此接口中,只有二个方法:
这两个方法 ,都是用来获取连接的:
getConnection() |
|
getConnection(String username, String password) |
4.1、三种实现
DataSource 接口由驱动程序供应商实现。共有三种类型的实现:
- 基本实现 - 生成标准的 Connection 对象
- 即这个DataSource中只有一个连接。
- 连接池实现 - 生成自动参与连接池的 Connection 对象。此实现与中间层连接池管理器一起使用。
- 还需要使用第三方jar包。
- 分布式事务实现 - 生成一个 Connection 对象,该对象可用于分布式事务,大多数情况下总是参与连接池。此实现与中间层事务管理器一起使用,大多数情况下总是与连接池管理器一起使用。
- 当你连接多个数据库时。要求也可以实现事务。
- JTA – Java Transaction Arichi..标准。
- javax.sql.XADataSource (接口)
找一下,哪些类,实现了javax.sql.DataSource接口
4.1、如何实现
测试使用mysql连接的ds类:
@Test
public void test1() throws Exception{
Class.forName("com.mysql.jdbc.Driver");
MysqlDataSource ds =
new MysqlDataSource();
ds.setUrl("jdbc:mysql:///oracle?chracterEncoding=UTF8");
ds.setUser("root");
ds.setPassword("1234");
Connection con = ds.getConnection();
System.err.println("这是第1个:"+con);
//再测试获取第二个
con.close();
Connection con2 = ds.getConnection();
System.err.println("这是第二个:"+con2);
System.err.println("--------------------------");
}
上面的代码不能使用:
1:依赖于mysql这个jar包的。
2:mysq中没有实现池管理。只是每次调用getConnection获取一个新的连接。
4.3、自己实现标准的连接池
1:必须要实现javax.sql.DataSource接口。
2:必须在实现类中,实现池管理.
3:保证可以回收连接。我就是动态代理实现。
4:一个项目中,保证只有一个DataSource实例就可以了
有了ds对象以后,ds就是一个数据源了.
第一步:开发一个资源文件
第二步:开发MyDataSource类
package cn.oracle.utils;
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.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;
import javax.sql.DataSource;
/**
* 不写static
* @author Administrator 应该让用户提供连接数据库需要信息
*/
public class MyDataSource implements DataSource {
// 非静态的
private LinkedList<Connection> pool = new LinkedList<Connection>();
// 提供一个构造方法
// 在构造中,让用提供四个
public MyDataSource(String driver, String url, String user, String pwd)
throws Exception {
this(driver, url, user, pwd, 1);
}
public MyDataSource(String driver, String url, String user, String pwd,
int pooSize) throws Exception {
Class.forName(driver);
for(int i=0;i<pooSize;i++){
Connection con = DriverManager.getConnection(url,user,pwd);
//将三个原生的conn放到
pool.addLast(con);
}
}
@Override
public Connection getConnection() throws SQLException {
synchronized (pool) {
if(pool.size()==0){
try {
pool.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
return getConnection();
}else{
final Connection con = pool.removeFirst();
Object obj =
Proxy.newProxyInstance(MyDataSource.class.getClassLoader(),
new Class[]{Connection.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getName().equals("close")){
synchronized (pool) {
pool.add(con);
pool.notify();
return null;
}
}else{
//放行,调用原生的
return method.invoke(con, args);
}
}
});
System.err.println("size si:"+pool.size());
return (Connection) obj;
}
}
}
//还有些方法就不用实现了
}
实现一个工具类,对MyDataSource实现工厂方式获取:
package cn.oracle.utils;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
/**
*/
public class DataSourceUtils {
private static DataSource ds;
static {
try {
Properties p = new Properties();
p.load(ClassLoader.getSystemResourceAsStream("jdbc.properties"));
ds = new MyDataSource(p.getProperty("driver"),
p.getProperty("url"), p.getProperty("user"),
p.getProperty("password"), Integer.parseInt(p
.getProperty("size")));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
//提供一个返回一个ds的方法
public static DataSource getDatasSource(){
return ds;
}
//还可以提供一个方法
public static Connection getCon() throws Exception{
return ds.getConnection();
}
}
代码结构如下:
测试如下:
5、使用第三方连接池
DBCP
C3p0
研究它们是用什么实现回收连接的?是包装还是代理。
5.1、DBCP
DataBase Connection Pool
使用dbcp连接数据库
创建dbcp数据源:
\
第一步:导入dbcpjar包及它的依赖包
第二步:dbcp核心
必须是DataSource的子类
用BasicDataSouce连接数据库:
直接在类中声明:
BasicDataSource ds =
new BasicDataSource();
//找set
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUsername("root");
ds.setPassword("1234");
ds.setUrl("jdbc:mysql:///oracle?characterEncoding=UTF8");
//看有几个连接
ds.setInitialSize(3);//设置开始时有几个连接
ds.setMaxActive(3);//设置最多有几个连接
或是通过配置文件读取:
、
、
还是像
开发获取dbcp数据源的工具类:
package cn.oracle.utils;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
public class DBCPUtils {
private static DataSource ds;
static {
try {
Properties p = new Properties();
p.load(ClassLoader.getSystemResourceAsStream("bb.properties"));
ds = BasicDataSourceFactory.createDataSource(p);
} catch (Exception e) {
}
}
public static DataSource getDs(){
return ds;
}
public static Connection getCon() throws Exception{
return ds.getConnection();
}
}
包装与代理:
由于包装就只两个实例类在进行交互。
反射比较慢。
6、元数据分析
只通过一个connection可以分析,目前数据库的所有信息:
版本
连接是什么数据库
驱动器版本
获取这个数据库中有多少表.
6.1、用DataBaseMetaData分析数据库的连接
/**
* 用元数据信息分析某个连接的数据库中有多少表
*/
@Test
public void getTables() throws Exception{
Connection con = DBCPUtils.getConnection();
DatabaseMetaData db = con.getMetaData();
//获取表的所有表名
//第一个参数与第二个参数是是指数据库是什么
//第三个参数你查找表名,
//找什么对象
ResultSet rs = db.getTables("mysql","mysql",null,new String[]{"TABLE"});
//显示表名
while(rs.next()){
String tname = rs.getString("TABLE_NAME");
System.err.println(tname);
}
}
6.2、分析结果集的信息
通过一个查询获取表中列,字段名类型 信息
请显示出mysql库中的user表的所有信息
@Test
public void testTable() throws Exception{
String sql = "select host,db,user from mysql.db";
//获取连接
Connection con = DBCPUtils.getConnection();
Statement st = con.createStatement();
//先获取到结果集
ResultSet rs = st.executeQuery(sql);
//获取结果集的元信息
ResultSetMetaData rsmd = rs.getMetaData();
//获取这个结果一共有几列
int cols = rsmd.getColumnCount();//5[微软用户1]
//获取列名
for(int i=1;i<=cols;i++){
String cName = rsmd.getColumnName(i);[微软用户2]
System.err.print(cName+"\t\t");
}
System.err.println();
while(rs.next()){
//一行数据
for(int i=1;i<=cols;i++){
String data = rs.getString(i);
System.err.print(data+"\t\t");
}
System.err.println();
}
}