您的位置:首页 > 编程语言 > Java开发

JavaWeb-JDBC连接池、JDBC框架

2013-06-27 20:24 393 查看
一、连接池概念

应用程序直接获取连接的缺点



出现的问题

用户请求Servlet,Servlet请求Service,Service调用Dao。
用户请求多次,Dao需要打开关闭connection多次。
频繁的开关connection很多次,浪费时间和消耗资源,并且容易造成数据库服务器内存溢出、宕机。

连接池图示



解决方法-连接池
解决方法:连接资源有限,需要重复利用,而不是关闭。怎么重复利用,连接池。
连接池:连接池是一个pool(缓存),里面封装了一个数据或者集合,这里使用List集合。
过程:每次应用启动的时候,创建一片缓存,即连接池,连接池中创建一定的connection对象。用户访问的时候从连接池取出一个connection连接,当使用结束后需要还回这个connection。

二、编写连接池遇到的问题

要对connection进行归还,需要重写connection的close方法。调用close的时候,归还这个conenction到连接池中。
对一个已有类com.mysql.Connection(不能修改)中的某个方法进行增强或者更改它的行为,直接继承可以吗?
直接继承在这里不可以,因为close方法中,归还的时候,父类的一些参数(URL、username、password)不能一起归还。
com.mysql.Connection这个接口中的方法,都没有实现。(这里看一下Connection是哪个包中的,还有是接口还是类。)

简单的连接池:自己编写的简单连接池太简单,实际开发中不能满足需求。
三、编写数据库连接池的原理。

编写连接池需实现javax.sql.DataSource接口。
只有实现了这个接口,才是一个标准的数据连接池(数据源)
框架中就是使用这个接口的实现得到连接。上面简单的连接池没有实现,框架没法调用所以不规范。

DataSource接口中定义了两个重载的getConnection方法:
Connection  getConnection() 
Connection getConnection(String username, String password)  (基本都不实现)

总体思路
实现DataSource接口,并实现连接池功能的步骤:
在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。
实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。
当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。
Collection保证将自己返回到LinkedList中是此处编程的难点。 

四、连接池的实现
使用包装设计模式,包装:com.mysql.jdbc.Connection
编写一个类,实现与被包装类相同的接口或继承被包装类
定义一个私有变量,记住被包装对象的引用,LinkedList<Connection>对象
定义构造方法,传入被包装对象的引用
对于要增强的方法,只管重写
对于不需要增强的方法,调用原有对象的原有方法。、

包装设计模式的实现
思路:
默认适配器实现了Connection接口,子类继承了默认适配器
用户要连接的时候,拿到的是适配器的子类
适配器的子类重写了close方法,并且引用了Connection对象和LinkedList<Connection>对象。当调用close时,把Connection对象添加到集合中。
下面为参考:
调用Connection对象的close()方法时, 为阻止系统销毁该连接, 而改为将连接归还给连接池, 可以采用包装设计模式. 此时包装的目标类为Connection, 因此需要定义MyConnection类并实现Connection接口. MyConnection类应该存在2个成员: Connection对象和LinkedList<Connection>对象,
这2个成员可以通过构造函数存入. 同时需要覆写close()方法, 在close()方法中实现将Connection添加到集合中.

核心代码

动态代理

编写一个与目标类具有相同接口的代理类,当外界访问被代理类时,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。 
代理类的缺陷是,只能代理接口中的方法。

思路:
用户要连接的时候,不给他原有对象,而是给一个代理(proxy)对象
这个代理类是com.mysql.jdbc.Connection的代理类$Proxy0

三个参数及用途
代理类的类加载器是所在的类的类加载器
代理类与被代理对象conn有相同的接口conn.getClass().getInterfaces(),所以有相同的行为。
代理类通过内部类invoke拦截想要的方法close,把conn返回池中

核心代码;

动态代理模式的实现
思路:
在内存中创建一集合,其中包含固定数目的连接。
获取连接时,直接从集合中获取集合对象。
关闭连接时,将不同的集合对象再加入集合中.
要遵循先入先出的原则(LinkedList.removeLast方法)

核心代码
proxyConn = (Connection) Proxy.newProxyInstance(this.getClass()
.getClassLoader(), conn.getClass().getInterfaces(),
new InvocationHandler() {
//此处为内部类,当close方法被调用时将conn还回池中,其它方法直接执行
public Object invoke(Object proxy, Method method,
  Object[] args) throws Throwable {
if (method.getName().equals("close")) {
pool.addLast(conn);
return null;
}
return method.invoke(conn, args);
}
});

五、开源的连接池使用

现在很多WEB服务器(Weblogic, WebSphere, Tomcat)都提供了DataSoruce的实现,即连接池的实现。通常我们把DataSource的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
也有一些开源组织提供了数据源的独立实现:

DBCP 数据库连接池 
C3P0 数据库连接池

实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序员编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。

DBCP():全称是Data Base Connectivity Pool
DBCP 是 Apache 软件基金组织下的开源连接池实现,使用DBCP数据源,应用程序应在系统中增加如下两个 jar 文件:
Commons-dbcp.jar:连接池的实现
Commons-pool.jar:连接池实现的依赖库
使用DBCP
static{
InputStream in = JdbcUtil.class.getClassLoader().
getResourceAsStream("dbcpconfig.properties");
Properties prop = new Properties();
prop.load(in);

BasicDataSourceFactory factory = new BasicDataSourceFactory();
dataSource = factory.createDataSource(prop);
}

<-这是DBCP使用的配置文件
原理是用的包装

C3P0()
原理是用的代理、

六、Tomcat管理数据源

Tomcat 的连接池是采用 DBCP连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
前提:拷贝数据库的驱动到Tomcat\lib目录下
在应用的META-INF目录下建立一个名字为context.xml的配置文件,内容如下:

  <?xml version="1.0" encoding="UTF-8"?>
<Context>
 <Resource name="jdbc/day14" auth="Container" type="javax.sql.DataSource"
               maxActive="30" maxIdle="10" maxWait="10000"
               username="root" password="sorry" driverClassName="com.mysql.jdbc.Driver"
               url="jdbc:mysql://localhost:3306/day14"/>
  </Context>

注:经过以上配置,容器启动时就是会创建好数据源。

获取数据源并使用。(JNDI的API)Java Naming and Directory Interface Java命名与目录服务
Context initContext = new InitialContext();
DataSource ds = (DataSource)initContext.lookup("java:/comp/env/jdbc/day14");
Connection conn = ds.getConnection();

特别提醒:此种配置下,驱动jar文件需放置在tomcat的lib下
JNDI简介:
JNDI(Java Naming and Directory Interface),Java命名和目录接口,它对应于J2SE中的javax.naming包,
这套API的主要作用在于:它可以把Java对象放在一个容器中(JNDI容器),并为容器中的java对象取一个名称,以后程序想获得Java对象,只需通过名称检索即可。
其核心API为Context,它代表JNDI容器,其lookup方法为检索容器中对应名称的对象。

七、获取数据库的元信息(编写框架用)

元数据:数据库、表、列的定义信息。
元数据- DataBaseMetaData
获得:Connection.getMetaData()
返回 DataBaseMetaData对象

DataBaseMetaData对象
getURL():返回一个String类对象,代表数据库的URL。
getUserName():返回连接当前数据库管理系统的用户名。
getDatabaseProductName():返回数据库的产品名称。
getDatabaseProductVersion():返回数据库的版本号。
getDriverName():返回驱动驱动程序的名称。
getDriverVersion():返回驱动程序的版本号。
isReadOnly():返回一个boolean值,指示数据库是否只允许读操作。

元数据- ParameterMetaData 
获得:PreparedStatement . getParameterMetaData() : 
返回代表PreparedStatement元数据的ParameterMetaData对象。(可得到有几个问号)  
Select * from user where name=? And password=?

ParameterMetaData对象
getParameterCount() :获得指定参数的个数
getParameterType(int param) :获得指定参数的sql类型(驱动可能不支持)

元数据- ResultSetMetaData
ResultSet. getMetaData() 
返回代表ResultSet对象元数据的ResultSetMetaData对象。 

ResultSetMetaData对象
getColumnCount() :返回resultset对象的列数
getColumnName(int column): 获得指定列的名称,从第一列开始
 getColumnTypeName(int column):获得指定列的类型 java.sql.Types

元数据的应用-使用元数据简化JDBC代码

业务背景:系统中所有实体对象都涉及到基本的CRUD操作:
所有实体的CUD操作代码基本相同,仅仅发送给数据库的SQL语句不同而已,因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句。
实体的R操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。(Bean,BeanList,Long)
核心代码

String columnName = md.getColumnName(i);
Object columnData = rs.getObject(i);
Field feild = clazz.getDeclaredField(columnName);
feild.setAccessible(true);
feild.set(obj, columnData);

八、自定义JDBC框架

策略设计模式
反射和泛型
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: