`
z75148885
  • 浏览: 191576 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

DAO设计模式

阅读更多

持久层设计与资源管理模式
无论是怎样的应用系统,都无法脱离对资源的管理和使用。而对于持久层而言,资源的合理管理和调度则显得尤为重要。

在大多数应用系统中,80%以上的应用逻辑并不需要特别复杂的数据库访问逻辑(可能只是几条简单的Select或作者Insert/Update语句)。对于这些占到多数的简单逻辑而言,如果SQL语句和数据库本身的设计不是太糟糕(合适的关联,字段索引以及数据库分区策略),在特定的硬件环境下,我们认为数据库的性能基本稳定。
此时,导致持久层性能地下的罪魁祸首可能并不是数据库本身,而是在于失败的资源管理调度机制。

Connection Pool
即使对于我们而言,通过JDBC获取数据库连接实在是件再简单不过的事情,但对于JDBC Driver来说,连接数据库却并非一件轻松差事。数据库连接不仅仅是在应用服务器与数据库服务器之间建立一个Socket Connection(对于Type4 的JDBC Driver而言),连接建立之后,应用服务器和数据库服务器之间还需要交换若干次数据(验证用户密码,权限等),然后,数据库开始初始化连接会话句柄,记录联机日志,为此连接分配相应得处理进程和系统资源。

系统如此忙碌,如果我们只是简单地仍过去两个SQL语句,然后就将此连接抛弃,实在可惜,而数据库连接池技术正是为了解决这个问题。
数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取或返回方法。如图

数据库连接池基本原理示意
外部使用者可以通过getConnection方法获取连接,使用完毕后再通过releaseConnection方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。
数据连接池技术带来下面的优势:
1. 资源重用
由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增进了系统运行环境的平稳性(减少内存碎片以及数据库临时进程/线程的数量)。
2. 更快的系统响应速度
数据库连接池仔初始化过程中,往往已经创建了若干数据库连接池置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而缩减了系统整体响应时间。
3. 新的资源分配手段
对于应用共享同一个数据库的系统而言,可在应用曾通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有数据库资源。
4. 统一的连接管理,避免数据库连接泄露
在较为完备的数据库连接池实现中,可根据预先的连接占用超时设定,强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄露。
一个最小化的数据库连接池实现:


代码
package net.wanjin.lab.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;

public class ConnectionPool {

private static Vector pool;
private final int POOL_MAX_SIZE =20;
/**
* 获取数据库连接
* 如果当前池中有可用连接,则将池中最后一个返回,如果没有,则新建一个返回
* @return
* @throws DBException
*/
public synchronized Connection getConnection() throws DBException{

if(pool ==null){
pool = new Vector();
}
Connection conn;
if(pool.isEmpty()){
conn = createConnection();
}else{
int last_idx = pool.size() -1;
conn =(Connection)pool.get(last_idx);
pool.remove(pool.get(last_idx));

}
return conn;
}
/**
* 将使用完毕的数据库连接放回备用池
*
* 判断当前池中连接数是否已经超过阀值
* 如果超过,则关闭该连接
* 否则放回池中以备下次重用
* @param conn
*/
public synchronized void releaseConnection(Connection conn){

if(pool.size()>POOL_MAX_SIZE){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}else{
pool.add(conn);
}
}
/**
* 读取数据库配置信息,并从数据库连接池中获得数据库连接
* @return
* @throws DBException
*/
private static Connection createConnection() throws DBException{
Connection conn;
try{
Class.forName("oracle.jdbc.driver.OracleDriver");
conn = DriverManager.getConnection("jdbc:oracle:thin:@1521:oracle","personal","personal");
return conn;
}catch(ClassNotFoundException e){
throw new DBException("ClassNotFoundException when loading JDBC Driver");
}catch(SQLException e){
throw new DBexception("SQLException when loading JDBC Driver");
}
}

}


完备的连接池实现固然错综复杂,但就其根本而言,还是源自同样的思想。

先脱离连接池本身的具体实现,我们看看这段代码在实际应用中可能产生的问题,注意到,由于getConnection方法返回的是一个标准的JDBC Connection,程序员由于编成习惯,可能会习惯性的调用其close方法关闭连接。如此一来,连接无法得到重用,数据库连接池机制形同虚设。为了解决这个问题,比较好的途径有:
1. Decorator 模式
2. Dynamic Proxy 模式
下面我们就这两种模式进行一些探讨:
“Decorator模式的主要目的是利用一个对象,透明地为另一个对象添加新的功能”。
这句话是从GOF关于设计模式的经典著作《设计模式-可复用面向对象软件的基础》一书中关于Decorator模式的描述直译而来,可能比较难以理解。简单来讲,就是通过一个Decorator对原有对象进行封装,同时实现与原有对象相同的接口,从而得到一个基于原有对象的,对既有接口的增强性实现。


对于前面所讨论的Connection释放的问题,理所当然,我们首先想到的是,如果能让JDBC Connection在执行close操作时自动返回到数据库连接池中,那么所有问题都将迎刃而解,但是,JDBC Connection自己显然无法根据实际情况判断何去何从。此时,引入Decorator模式来解决我们所面对的问题就非常合适。

首先,我们引入一个ConnectionDecorator类:


代码
import java.sql.Connection;
import java.sql.SQLException;

public class ConnectionDecorator implements Connection {
Connection dbconn;

public ConnectionDecorator(Connection conn){
this.dbconn = conn; //实际从数据库获得的Connection引用
}

/**
* 此方法将被子类覆盖,以实现数据库连接池的连接返回操作
* @see java.sql.Connection#close()
*/
public void close() throws SQLException{
this.dbconn.close();
}
/**
* (non-Javadoc)
* @see java.sql.Connectionn#commit()
*/
public void commit() throws SQLException{
this.dbconn.commit(); //调用实际连接的commit方法
}
//以下各个方法类似,均调用dbconn.xxx方法作为Connection接口中方法的简单实现.

}


可以看到,ConnectionDecorator类实际上的对传入的数据库连接加上一个外壳,他实现了java.slq.Connection接口,不过本身并没有实现任何实际内容,只是简单的把方法的实现委托给运行期实际获得的Connection实例,,而从外部来看,ConnctionDecorator与普通Connection实例并没有什么区别,因为他们实现了同样的接口,对外提供了同样的功能调用。

目标很清楚,通过这样的封装,我们的ConnectionDecorator对于外部的程序员而言,调用方法与普通的JDBC Connection完全相同,而在内部通过对ConnectionDecorator的修改,我们就可以透明地改变现有实现,为了增加新的特性:


代码
package net.wanjin.lab.persistence.Decorator;

import java.sql.Connection;

import net.wanjin.lab.utils.ConnectionPool;

public class PooledConnection extends ConnectionDecorator implements Connection{

private ConnectionPool connPool;

public PooledConnection(ConnectionPool pool,Connection conn){
super(conn);
connPool = pool;
}
/**
* 覆盖Close方法,将数据库连接返回连接池,而不是直接关闭连接
*/
public void close() throws SQLException{
connPool.releaseConnection(this.dbconn);
}
}
为了应用新的PooledConnection ,我们需要对原本的DBConnectionPool.getConnection和releaseConnection方法稍做改造:

/**
* 获取数据库连接
* 如果当前池中有可用连接,则将池中最后一个返回,如果没有,则新建一个返回
* @return
* @throws DBException
*/
public synchronized Connection getConnection() throws DBException{

if(pool ==null){
pool = new Vector();
}
Connection conn;
if(pool.isEmpty()){
conn = createConnection();
}else{
int last_idx = pool.size() -1;
conn =(Connection)pool.get(last_idx);
pool.remove(pool.get(last_idx));

}
//return conn;
return new PooledConnection(this,conn);
}
/*
* 将使用完毕的数据库连接放回备用池
*
* 判断当前池中连接数是否已经超过阀值
* 如果超过,则关闭该连接
* 否则放回池中以备下次重用
* @param conn
*/
public synchronized void releaseConnection(Connection conn){

if(conn instanceof PooledConnection||pool.size()>POOL_MAX_SIZE){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}else{
pool.add(conn);
}
}

此时获取数据库连接后,调用这只需要按照JDBC Connection的标准用法进行调用即可,从而实现了数据库连接的透明化。同样的道理,我们甚至可以利用Decorator模式对DriverManager类进行同样的改造,从而最小化数据库连接池实现对传统JDBC编码方式的影响。

Dynamic Proxy 模式
DynamicProxy是JDK1.3版本中新引入的一种代理机制。严格来讲,DynamicProxy本身并非一种模式,只能算是Proxy模式的一种动态实现方式,不过为了与传统Proxy模式相区分,这里暂且将其称为“Dynamic Proxy模式”,来泛指通过Dynamic Proxy机制实现的Proxy模式。
回到上面的问题,我们的目标是在引入数据库连接池机制的同时,保持JDBCConnection对外接口不变。前面通过Decorator接口中定义的方法众多,我们也只是能照单全收,在ConnecionDecorator中逐一实现这些方法,虽然只是简单的委托实现,也实在是件恼人的工作。
Dynamic Proxy模式则良好地解决了这一问题。通过实现一个邦定到Connection对象的InvocationHandler接口实现,我们可以在Connection.close方法被调用时将其截获,并以我们自己实现的Close方法将其代替,使连接返回到数据库连接池等待下次重用,而不是直接关闭。

下面是ConnectionHandler类,它实现了InvocationHandler接口,按照DynamicProxy机制的定义,Invoke方法将截获所有代理对象的方法调用操作,这里我们通过invoke方法截获close方法进行处理。


代码
package net.wanjin.lab.utils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;

public class ConnectionHandler implements InvocationHandler {

Connection dbconn;
ConnectionPool pool;

public ConnectionHandler(ConnectionPool connPool){
this.pool = connPool;
}
/**
*
*/
public Connection bind(Connection conn){
this.dbconn = conn;

Connection proxyConn =(Connection)Proxy.newProxyInstance(
conn.getClass().getClassLoader(),conn.getClass().getInterfaces(),this);

return proxyConn;
}

public Object invoke(Object proxy,Method method,Object[] args)
throws Throwable{
Object obj =null;
//如果调用的是close方法,则用pool.releaseConnection方法将其替换
if("close".equals(method.getName())){
pool.releaseConnection(dbconn);
}else{
obj = method.invoke(dbconn, args);
}
return obj;

}

}


配合Dynamic Proxy模式,我们的DBConnectionPool.getConnection方法也做了一点小的修改:

代码
/**
* 获取数据库连接
* 如果当前池中有可用连接,则将池中最后一个返回,如果没有,则新建一个返回
* @return
* @throws DBException
*/
public synchronized Connection getConnection() throws DBException{

if(pool ==null){
pool = new Vector();
}
Connection conn;
if(pool.isEmpty()){
conn = createConnection();
}else{
int last_idx = pool.size() -1;
conn =(Connection)pool.get(last_idx);
pool.remove(pool.get(last_idx));

}
//return conn;
//return new PooledConnection(this,conn);
ConnectionHandler connHandler = new ConnectionHandler(this);
return connHandler.bind(conn);
}


可以看到,基于Dynamic Proxy 模式的实现相对Decorator更加简洁明了。

分享到:
评论

相关推荐

    DAO设计模式 DAO 设计模式 JAVA设计模式

    DAO(Data Access Object)设计模式是软件开发中一种常见的用于处理数据访问的模式,它将业务逻辑与数据访问逻辑分离,使得代码结构更加...因此,深入理解并灵活运用DAO设计模式对于提升Java应用程序的质量至关重要。

    DAO设计模式DAO设计模式

    ### DAO设计模式详解 #### 一、概述 数据访问对象(Data Access Object,简称DAO)设计模式是一种在软件工程特别是企业级应用开发中非常常见的设计模式。它的主要目的是分离业务逻辑与数据访问逻辑,使系统架构...

    DAO设计模式辅助资料

    这个压缩包文件"DAO设计模式辅助资料"显然是为了帮助开发者更好地理解和应用DAO模式。下面,我们将深入探讨DAO设计模式的核心概念、实现方式以及它在实际开发中的应用。 DAO设计模式的基本思想是为数据库操作创建一...

    JSP+Servlet+AJAX的dao设计模式

    **JSP+Servlet+AJAX DAO设计模式详解** 在Web开发中,DAO(Data Access Object)设计模式是一种常用的设计模式,用于将业务逻辑与数据访问层进行解耦,提高代码的可维护性和可复用性。本篇我们将深入探讨如何在JSP...

    基于DAO设计模式的新闻发布系统

    在这个基于DAO设计模式的新闻发布系统中,我们可以深入探讨以下几个核心知识点: 1. DAO设计模式:DAO模式的核心思想是创建一个接口,该接口定义了对数据存储的通用操作,然后实现这个接口以处理具体的数据库交互。...

    DAO设计模式(工厂+代理)

    在这个“DAO设计模式(工厂+代理)”的例子中,我们将探讨如何结合工厂模式和代理模式来实现更灵活、更易于维护的DAO层。 首先,让我们理解DAO的基本概念。DAO是一个接口或抽象类,定义了对数据库进行操作的方法,...

    李兴华DAO设计模式

    在描述的课程中,李兴华先生讲解了DAO设计模式,并通过实例分析来阐述如何在业务层和服务层之间进行交互。例如,业务层可能需要实现以下功能: 1. 新雇员增加:业务层首先检查雇员编号是否已存在(调用数据层的查询...

    Dao设计模式

    DAO设计模式是Java开发中常用的一种数据访问模式,它将底层数据访问操作与上层业务逻辑相分离。这一模式的目的在于封装所有对数据源的访问,并提供抽象接口,使得业务逻辑层与数据访问层解耦,从而降低业务代码与...

    J2EE学习笔记--DAO设计模式基础.txt

    ### J2EE学习笔记——DAO设计模式基础 #### 一、引言 在J2EE(Java 2 Enterprise Edition)开发中,DAO (Data Access Object) 设计模式是一种常用的数据访问层实现方式,用于分离业务逻辑与数据访问逻辑,使得程序...

    李兴华 DAO设计模式 实现 增删改查 分页查询 完整代码

    在这个“李兴华 DAO设计模式 实现 增删改查 分页查询 完整代码”项目中,我们将探讨DAO模式如何应用于实现数据库的CRUD(创建、读取、更新、删除)操作以及分页查询。 1. DAO设计模式基础: DAO设计模式的核心是...

    DAO设计模式精讲(java web开发)

    在本精讲中,我们将深入探讨DAO设计模式的原理、实现方式以及在实际项目中的应用。 **DAO设计模式的核心思想** DAO设计模式的核心是创建一个接口,该接口定义了对数据库进行操作的方法,如查询、插入、更新和删除...

    Dao设计模式教程

    在本教程中,我们将深入探讨DAO设计模式的各个方面,包括其概念、优点、结构以及如何在实际项目中应用。 1. **DAO设计模式的概念** DAO设计模式是一种面向对象的设计模式,用于处理应用程序和数据存储之间的交互。...

    WEB开发经典之 DAO设计模式

    **DAO设计模式详解** 在Web开发中,DAO(Data Access Object)设计模式是一种常见的用于分离业务逻辑层与数据访问层的架构模式。DAO的主要目的是为了提供一个统一的接口,使得业务逻辑组件可以与数据库交互而无需...

    初步认识JAVA DAO设计模式

    总结来说,DAO设计模式在Java开发中扮演着重要的角色,尤其是在处理数据持久化问题时,它提供了一种灵活且可扩展的方式来管理数据访问,降低了系统的复杂性,提高了代码的可维护性。通过将数据访问逻辑封装在DAO中,...

    Java DAO 设计模式.pdf

    #### 一、DAO设计模式的概念 **DAO**(Data Access Object)设计模式是一种在Java EE环境中广泛使用的数据访问模式,它主要应用于处理数据存储组件的交互。这种模式的主要目的是将底层数据访问操作与业务逻辑分开,...

    初步认识JAVA+DAO设计模式.pdf

    本文将详细讲解JAVA+DAO设计模式的概念、原理和实现。 DAO 设计模式背景 在软件开发中,数据持久化是一个非常重要的步骤。不同的数据源会有不同的访问机制和API,这使得程序的开发和维护变得非常复杂。例如,在...

    JavaEE技术-DAO设计模式

    DAO设计模式是JavaEE中用于数据层操作的一种模式,其核心目的是简化代码编写、提高可移植性,以及实现更好的代码维护和可读性。在没有使用DAO设计模式之前,数据库操作通常直接在JSP页面中使用JDBC代码,或者通过...

Global site tag (gtag.js) - Google Analytics