论坛首页 Java企业应用论坛

mvnforum之数据源管理(Decorator)

浏览 3563 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-01-07  
  写这篇文章的目的是结合实际应用说明一下Decorator模式的使用,前提是要对Decorator模式的基本概念比较清晰,具体的Decorator介绍就不在这里叙述了。 前两天花了点时间装了个开源的project:mvnforum论坛,想学习一下这个项目里面的一些设计思想和实现,上午又花了点时间看了一下,先从它的底层封装数据库的一些代码看起的,看着看着,发现mvnforum的底层数据源和Connection管理就是一个典型的Decorator模式,所以就上来说一下,当然我知道,用Decorator模式来设计数据源的管理是一个十分常见的设计思路,这里主要是讲一下它的具体实现。 我还是列举了一些相对比较容易看懂的代码来说明,毕竟这个项目里有的地方还是比较复杂的,我旨在说明问题,所以一切从简。 我们知道,关于底层数据库的操作是每个项目都需要设计的,而关于数据资源的管理也是设计的重中之重,会有数据库连接池,当然,各大应用服务器都支持POOL的配置,来统一管理和调度这些资源,因为我们知道,创建应用与数据库之间的连接并不是我们看上去那么简单的,过程是非常繁琐的,是非常消耗资源的,所以这方面的设计是体现性能的关键,我们建议使用应用服务器中提供的这种数据库连接池配置,专业的服务器厂商对于这方面的设计和实现是有保障的,当然,也不是说自己不可以去设计,mvnforum就提供了两重设计,第一种是使用服务器配置的数据库连接池,如果你不使用也可以,mvnforum提供了一个自我实现的connection pool,并且进行了封装。大概的一个架构是这样的,如图:(祥见附件) 好了,我来解释一下这个图吧,既然我们要用Decorator模式,那么究竟为什么要用这个模式呢,我的理解是这样的,当应用程序和数据库建立连接后,当我们使用完这个连接后,理所当然的需要关闭,然而这样就带来的性能的损失,也不符合我们用数据库连接池的宗旨,你一旦con.close()那么完了,下次我要用又得去连接,比较理想的解决方案是我们开辟连接,并保持这种状态在连接池里,外程序要使用就可以来调用,池分配一个没有被使用的但是已经连接上的Connection给你,用完后,你没有权力去关闭它,你还给我(池),然后我来决定是不是要关闭,但是在我们使用时,得到一个Connection很容易,但是关闭一个Connection同样很容易,作为池来说,我不能保证外面的程序不会不经过我就把Connection给close了,这样我就很不爽了。那么怎么杜绝这样的一种情况呢,呵呵,Decorator模式就派上用场了,好来看看吧。 ConnectionWrapper这个class就是一个Decorator类,我贴下代码给大家看看,它实现了Connection接口。
/*
 * $Header: /cvsroot/mvnforum/myvietnam/src/net/myvietnam/mvncore/db/ConnectionWrapper.java,v 1.6 2007/09/26 04:11:07 minhnn Exp $
 * $Author: minhnn $
 * $Revision: 1.6 $
 * $Date: 2007/09/26 04:11:07 $
 *
 * ====================================================================
 *
 * Copyright (C) 2002-2007 by MyVietnam.net
 *
 * All copyright notices regarding MyVietnam and MyVietnam CoreLib
 * MUST remain intact in the scripts and source code.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Correspondence and Marketing Questions can be sent to:
 * info at MyVietnam net
 *
 * @author: Minh Nguyen  
 */
package net.myvietnam.mvncore.db;

import java.sql.*;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.lang.NotImplementedException;

public class ConnectionWrapper implements Connection {

    private DBConnectionManager connectionManager = null;
    
    private static int outsideConnection = 0;
    
    Connection delegate = null;

    ConnectionWrapper(Connection original, DBConnectionManager conManager) {
        if (original == null) {
            throw new IllegalArgumentException("Cannot accept the connection is null.");
        }
        if (conManager == null) {
            throw new IllegalArgumentException("Cannot accept the DBConnectionManager is null.");
        }
        delegate = original;
        connectionManager = conManager;
        outsideConnection++;
    }
    
    private void makeSureNotClose() {
        if (delegate == null) {
            throw new IllegalStateException("Connection has been closed (delegate == null).");
        }
    }
    
    public void close() throws SQLException {
        //delegate.close();
        if (delegate != null) {
            connectionManager.freeConnection(delegate);
            delegate = null;
            outsideConnection--;
        }
    }

    public void clearWarnings() throws SQLException {
        makeSureNotClose();
        delegate.clearWarnings();
    }

    public void commit() throws SQLException {
        makeSureNotClose();
        delegate.commit();
    }

    public Statement createStatement() throws SQLException {
        makeSureNotClose();
        return delegate.createStatement();
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        makeSureNotClose();
        return delegate.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        makeSureNotClose();
        return delegate.createStatement(resultSetType, resultSetConcurrency);
    }

    public boolean getAutoCommit() throws SQLException {
        makeSureNotClose();
        return delegate.getAutoCommit();
    }

    public String getCatalog() throws SQLException {
        makeSureNotClose();
        return delegate.getCatalog();
    }

    public int getHoldability() throws SQLException {
        makeSureNotClose();
        return delegate.getHoldability();
    }

    public DatabaseMetaData getMetaData() throws SQLException {
        makeSureNotClose();
        return delegate.getMetaData();
    }

    public int getTransactionIsolation() throws SQLException {
        makeSureNotClose();
        return delegate.getTransactionIsolation();
    }

    public Map getTypeMap() throws SQLException {
        makeSureNotClose();
        return delegate.getTypeMap();
    }

    public SQLWarning getWarnings() throws SQLException {
        makeSureNotClose();
        return delegate.getWarnings();
    }

    public boolean isClosed() throws SQLException {
        makeSureNotClose();
        return delegate.isClosed();
    }

    public boolean isReadOnly() throws SQLException {
        makeSureNotClose();
        return delegate.isReadOnly();
    }

    public String nativeSQL(String sql) throws SQLException {
        makeSureNotClose();
        return delegate.nativeSQL(sql);
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        makeSureNotClose();
        return delegate.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        makeSureNotClose();
        return delegate.prepareCall(sql, resultSetType, resultSetConcurrency);
    }

    public CallableStatement prepareCall(String sql) throws SQLException {
        makeSureNotClose();
        return delegate.prepareCall(sql);
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        makeSureNotClose();
        return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        makeSureNotClose();
        return delegate.prepareStatement(sql, resultSetType, resultSetConcurrency);
    }

    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        makeSureNotClose();
        return delegate.prepareStatement(sql, autoGeneratedKeys);
    }

    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        makeSureNotClose();
        return delegate.prepareStatement(sql, columnIndexes);
    }

    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        makeSureNotClose();
        return delegate.prepareStatement(sql, columnNames);
    }

    public PreparedStatement prepareStatement(String sql) throws SQLException {
        makeSureNotClose();
        return delegate.prepareStatement(sql);
    }

    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        makeSureNotClose();
        delegate.releaseSavepoint(savepoint);
    }

    public void rollback() throws SQLException {
        makeSureNotClose();
        delegate.rollback();
    }

    public void rollback(Savepoint savepoint) throws SQLException {
        makeSureNotClose();
        delegate.rollback(savepoint);
    }

    public void setAutoCommit(boolean autoCommit) throws SQLException {
        makeSureNotClose();
        delegate.setAutoCommit(autoCommit);
    }

    public void setCatalog(String catalog) throws SQLException {
        makeSureNotClose();
        delegate.setCatalog(catalog);
    }

    public void setHoldability(int holdability) throws SQLException {
        makeSureNotClose();
        delegate.setHoldability(holdability);
    }

    public void setReadOnly(boolean readOnly) throws SQLException {
        makeSureNotClose();
        delegate.setReadOnly(readOnly);
    }

    public Savepoint setSavepoint() throws SQLException {
        makeSureNotClose();
        return delegate.setSavepoint();
    }

    public Savepoint setSavepoint(String name) throws SQLException {
        makeSureNotClose();
        return delegate.setSavepoint(name);
    }

    public void setTransactionIsolation(int level) throws SQLException {
        makeSureNotClose();
        delegate.setTransactionIsolation(level);
    }

    public void setTypeMap(Map map) throws SQLException {
        makeSureNotClose();
        delegate.setTypeMap(map);
    }

    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        throw new NotImplementedException("createArrayOf");
    }

    public Blob createBlob() throws SQLException {
        throw new NotImplementedException("createBlob");
    }

    public Clob createClob() throws SQLException {
        throw new NotImplementedException("createClob");
    }

    public NClob createNClob() throws SQLException {
        throw new NotImplementedException("createNClob");
    }

    public SQLXML createSQLXML() throws SQLException {
        throw new NotImplementedException("createSQLXML");
    }

    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        throw new NotImplementedException("createStruct");
    }

    public Properties getClientInfo() throws SQLException {
        throw new NotImplementedException("getClientInfo");
    }

    public String getClientInfo(String name) throws SQLException {
        throw new NotImplementedException("getClientInfo");
    }

    public boolean isValid(int timeout) throws SQLException {
        throw new NotImplementedException("isValid");
    }

    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        throw new NotImplementedException("setClientInfo");
    }

    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        throw new NotImplementedException("setClientInfo");
    }

    public boolean isWrapperFor(Class iface) throws SQLException {
        throw new NotImplementedException("isWrapperFor");
    }

    public Object unwrap(Class iface) throws SQLException {
        throw new NotImplementedException("unwrap");
    }
}
  

  其实这个class没有做什么,只做了一件比较有意义的事,那就是重写了close这个方法,它没有单纯的关闭这个connction,而是交给了DBConnectionManager的freeConnection去做,很显然,是释放不是关闭,那么DBConnectionManager的freeConnection会去让DBConnectionPool去做这个事情,DBConnectionPool会检查现在池中的空闲连接个数,如果满了就把你还回来的Connection给close掉,没有满就放在池中,等待下次的调用。 这样似乎还不行,为什么,如果要达到我们之前所说的要求,那么我觉得你一个定要能保证给我个ConnectionWapper类型的Connection实现实例,这样就算我去close也没有正真的关闭这个Connection,而是释放。好,再往下看看就知道了。 我们可以看一下ConnectionWrapper的构造器,实际上它维护了一个Connection,一个DBConnectionManager和一个int类型的计数器。那么我现在来贴一下DBConnectionManager的代码出来看看:
/*
 * $Header: /cvsroot/mvnforum/myvietnam/src/net/myvietnam/mvncore/db/DBConnectionManager.java,v 1.22 2007/11/22 04:40:29 minhnn Exp $
 * $Author: minhnn $
 * $Revision: 1.22 $
 * $Date: 2007/11/22 04:40:29 $
 *
 * ====================================================================
 *
 * Copyright (C) 2002-2007 by MyVietnam.net
 *
 * All copyright notices regarding MyVietnam and MyVietnam CoreLib
 * MUST remain intact in the scripts and source code.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Correspondence and Marketing Questions can be sent to:
 * info at MyVietnam net
 *
 * @author: Minh Nguyen  
 * @author: Mai  Nguyen  
 */
package net.myvietnam.mvncore.db;

import java.sql.*;
import java.util.*;

import net.myvietnam.mvncore.MVNCoreConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This class is a Singleton that provides access to the
 * connection pool. A client gets access to the single
 * instance through the static getInstance() method
 * and can then check-out and check-in connections from a pool.
 * When the client shuts down it should call the release() method
 * to close all opened connections and do other clean up.
 */
class DBConnectionManager {

    private static Log log = LogFactory.getLog(DBConnectionManager.class);

    private static final int TIME_BETWEEN_RETRIES = 500; // O.5 second

    // static variable
    static private DBConnectionManager instance = null;       // The single instance

    // instance variable
    private DBConnectionPool pool = null;// please be careful if u want to make this variable static
    
    private static Map dbManagers = new HashMap();
    private static final int MANAGER_MAX = 5;
    
    /**
     * A private constructor since this is a Singleton
     * Note: This constructor is lightweight since DBConnectionPool is lightweight,
     *       so no connection is created until the first time getConnection() is called
     */
    private DBConnectionManager() {
        String driverClassName = MVNCoreConfig.getDriverClassName();
        try {
            Class.forName(driverClassName).newInstance();
        } catch (Exception e) {
            log.fatal("DBConnectionManager: Unable to load driver = " + driverClassName, e);
        }

        String url = MVNCoreConfig.getDatabaseURL();
        String user = MVNCoreConfig.getDatabaseUser();
        String password = MVNCoreConfig.getDatabasePassword();
        int maxConnection = MVNCoreConfig.getMaxConnection();

        //always new the pool because pool is an instance variable
        pool = new DBConnectionPool(url, user, password, maxConnection);
    }

    private DBConnectionManager(DBOptions dbOptions) {
        String driverClassName = dbOptions.getDriverClass();
        try {
            Class.forName(driverClassName).newInstance();
        } catch (Exception e) {
            log.fatal("DBConnectionManager: Unable to load driver = " + driverClassName, e);
        }

        String url = dbOptions.getDbUrl();
        String user = dbOptions.getUsername();
        String password = dbOptions.getPassword();
        int maxConnection = dbOptions.getConMax();

        //always new the pool because pool is an instance variable
        pool = new DBConnectionPool(url, user, password, maxConnection);
    }
    
    /**
     * Returns the single instance, creating one if it's the
     * first time this method is called.
     *
     * @return DBConnectionManager The single instance.
     */
    /*
    public static synchronized DBConnectionManager getInstance() {
        if (instance == null) {
            DBOptions option = new DBOptions();
            instance = new DBConnectionManager(option);
        }
        return instance;
    }*/

    /**
     * Returns the single instance, creating one if it's the
     * first time this method is called.
     *
     * @return DBConnectionManager The single instance.
     */
    /*
    private static synchronized DBConnectionManager getInstance(DBOptions option) {
        if (instance == null) {
            if (option == null) {
                option = new DBOptions();
            }
            instance = new DBConnectionManager(option);
        }
        return instance;
    }*/

    /**
     * DBUtil use this method
     */
    public static synchronized DBConnectionManager getInstance(boolean useConfig) {
        if (instance == null) {
            instance = new DBConnectionManager();
        }
        return instance;
    }

    /**
     * DBUtil2 use this method
     */
    public static synchronized DBConnectionManager getDBConnectionManager(DBOptions dbOptions) {
        
        if (dbOptions == null) {
            throw new IllegalArgumentException("Cannot get DBConnectionManager. Missing DBOptions.");
        }
        
        if ( (dbOptions.getDbManagerName() == null) || (dbOptions.getDbManagerName().length() == 0) ) {
            throw new IllegalArgumentException("Cannot get DBConnectionManager. Missing [Database Connection Manager Name].");
        }

        DBConnectionManager dbManager = (DBConnectionManager)dbManagers.get(dbOptions.getDbManagerName());
        if (dbManager == null) {
            dbManager = createDbConnectionManager(dbOptions);
        }
        return dbManager;
    }
    
    private static DBConnectionManager createDbConnectionManager(DBOptions dbOptions) {
        
        if (dbManagers.size() >= MANAGER_MAX) {
            throw new IllegalStateException("System only support max " +  MANAGER_MAX + " DBConnectionManager(s)");    
        }
        
        DBConnectionManager instance = new DBConnectionManager(dbOptions);
        dbManagers.put(dbOptions.getDbManagerName(), instance);
        
        return instance;
    }

    /**
     * Returns a connection to the pool.
     *
     * @param con The Connection
     */
    void freeConnection(Connection con) {
        pool.freeConnection(con);
    }

    /**
     * Returns an open connection. If no one is available, and the max
     * number of connections has not been reached, a new connection is
     * created.
     *
     * @return Connection The connection or null
     */
    Connection getConnection() {
        return getConnection(0);
    }

    /**
     * Returns an open connection. If no one is available, and the max
     * number of connections has not been reached, a new connection is
     * created. If the max number has been reached, waits until one
     * is available or the specified time has elapsed.
     *
     * @param time The number of milliseconds to wait
     * @return Connection The connection or null
     */
    Connection getConnection(long time) {
        Connection connection = pool.getConnection(time);
        if (connection == null) {
            return null;
        }
        
        try {
            // we always setAutoCommit(true) for backward compatible with mvnForum
            connection.setAutoCommit(true);
        } catch (SQLException e) {
            log.error("Cannot setAutoCommit", e);
        }
        ConnectionWrapper wrapper = new ConnectionWrapper(connection, this);
        return wrapper;
    }

    /**
     * Closes all open connections.
     * @return true if the pool is empty and balance
     *         false if the pool has returned some connection to outside
     */
    boolean release() {
        return pool.release();
    }

    /**
     * This inner class represents a connection pool. It creates new
     * connections on demand, up to a max number if specified.
     * It also checks to make sure that the connection is still open
     * before it is returned to a client.
     */
    class DBConnectionPool {
        private int checkedOut  = 0;//NOTE: this variable should be changed in synchronized method only
        private Vector freeConnections = new Vector();

        private int    maxConn  = 0;
        private String password = null;
        private String URL      = null;
        private String user     = null;

        /**
         * Creates new connection pool.
         * NOTE: new an instance of this class is lightweight since it does not create any connections
         *
         * @param URL The JDBC URL for the database
         * @param user The database user, or null
         * @param password The database user password, or null
         * @param maxConn The maximal number of connections, or 0 for no limit
         */
        public DBConnectionPool(String URL, String user, String password, int maxConn) {
            this.URL = URL;
            this.user = user;
            this.password = password;
            this.maxConn = maxConn;
        }

        /**
         * Checks in a connection to the pool. Notify other Threads that
         * may be waiting for a connection.
         *
         * @todo: Maybe we dont need notifyAll(); ???
         *
         * @param con The connection to check in
         */
        synchronized void freeConnection(Connection con) {
            // Put the connection at the end of the Vector
            if (con != null) {//make sure that the connection is not null
                if (checkedOut <= 0) {
                    // this means that connection is open too much
                    // There are 2 cases:
                    // 1. Not get from this connection pool (maybe get directly)
                    // 2. this connection is gotten and then the whole pool is released
                    // In these case, just close the connection
                    try {
                        log.debug("DBConnectionManager: about to close the orphan connection.");
                        con.close();
                    } catch (SQLException ex) { }
                } else {
                    // Return this connection to the pool
                    // note that we dont have to check if the connection is not connected
                    // this will be check in the getConnection method
                    freeConnections.addElement(con);
                    // FIXME: posible negative value
                    // NOTE: checkOut should never be negative here
                    checkedOut--; // NOTE: this number can be negative (in case connection does not come from the pool)
                    notifyAll(); // can I remove it ???
                }
            }
        }

        /**
         * Checks out a connection from the pool. If no free connection
         * is available, a new connection is created unless the max
         * number of connections has been reached. If a free connection
         * has been closed by the database, it's removed from the pool
         * and this method is called again recursively.
         */
        synchronized Connection getConnection() {
            Connection con = null;

            while ( (freeConnections.size() > 0) && (con == null) ) {
                // Pick the first Connection in the Vector
                // to get round-robin usage
                con = (Connection) freeConnections.firstElement();
                freeConnections.removeElementAt(0);
                try {
                    if (con.isClosed()) {
                        log.info("Removed bad connection in DBConnectionPool.");
                        con = null; // to make the while loop to continue
                    }
                } catch (SQLException e) {
                    con = null; // to make the while loop to continue
                }
            } // while

            if (con == null) {// cannot get any connection from the pool
                if (maxConn == 0 || checkedOut < maxConn) {// maxConn = 0 means unlimited connections
                    con = newConnection();
                }
            }
            if (con != null) {
                checkedOut++;
            }
            return con;
        }

        /**
         * Checks out a connection from the pool. If no free connection
         * is available, a new connection is created unless the max
         * number of connections has been reached. If a free connection
         * has been closed by the database, it's removed from the pool
         * and this method is called again recursively.
         * <P>
         * If no connection is available and the max number has been
         * reached, this method waits the specified time for one to be
         * checked in.
         *
         * @param timeout The timeout value in milliseconds
         */
        /**
         * Note that this method is not synchronized since it relies on the getConnection(void) method
         * I also believe that this method SHOULD NOT synchronized because I use #sleep() method
         * @todo: check if we should synchronize this method and use wait instead of sleep ???
         */
        Connection getConnection(long timeout) {
            long startTime = System.currentTimeMillis();
            Connection con;
            while ((con = getConnection()) == null) {
                long elapsedTime = System.currentTimeMillis() - startTime;
                if (elapsedTime >= timeout) {
                    // Timeout has expired
                    return null;
                }

                long timeToWait = timeout - elapsedTime;
                if (timeToWait > TIME_BETWEEN_RETRIES) timeToWait = TIME_BETWEEN_RETRIES;// we dont want to wait for more than TIME_BETWEEN_RETRIES second each time
                try {
                    Thread.sleep(timeToWait);
                } catch (InterruptedException e) {}
            }
            return con;
        }

        /**
         * Closes all available connections.
         * @return true if the pool is empty and balance
         *         false if the pool has returned some connection to outside
         */
        synchronized boolean release() {
            boolean retValue = true;
            Enumeration allConnections = freeConnections.elements();
            while (allConnections.hasMoreElements()) {
                Connection con = (Connection) allConnections.nextElement();
                try {
                    con.close();
                } catch (SQLException e) {
                    log.error("Cannot close connection in DBConnectionPool.");
                }
            }
            freeConnections.removeAllElements();
            if (checkedOut != 0) {
                retValue = false;
                log.warn("DBConnectionManager: the built-in connection pool is not balanced.");
            }
            checkedOut = 0;
            return retValue;
        }

        /**
         * Creates a new connection, using a userid and password
         * if specified.
         * @todo: check if this method need synchronized
         */
        private Connection newConnection() {
            Connection con = null;
            try {
                if (user == null) {
                    con = DriverManager.getConnection(URL);
                } else {
                    con = DriverManager.getConnection(URL, user, password);
                }
                // Note that we dont need to call setAutoCommit here because we 
                // will call it at DBConnectionManager.getConnection()
                //con.setAutoCommit(true);//thread 804 by trulore
            } catch (SQLException e) {
                log.error("Cannot create a new connection in DBConnectionPool. URL = " + URL, e);
                return null;
            }
            return con;
        }
    }
    
}


  请注意这个class中的getConnection方法,这个方法从pool里拿了一个原始的connection出来然后构成了一个ConnectionWrapper给用户,给谁呢?
给DBUtils这个class使用,这个class是外程序来取connection的一个途径,所以这样就保证了我们给出去的conneciton就是一个包装过的ConnecitonWrapper,这样当然就可以避免了前面讲到的问题了。
  从代码来看就知道,mnvforum其实提供了两种取connection的方法,一中用server的数据库连接池一种用自己实现的数据库连接池。
  一般DBUtils都被一些DAO在使用,这样就基本完成了一个底层数据源使用的封装,也实现了Decorator模式,因为对于外面的DAO来说,其实是对于DBUtils来说,我只想要一个Connection来和DB做交互,做操作,而至于你在后面对这个Connection做了什么我并不关心,实际上当我们返回给客户程序的这样一个Connection已经被我们装饰过了,有了一些特殊功能,这样即使客户程序close掉这个Connection,其实系统只是释放这个Connection给Pool而已。
  好了,讲了这么多,希望讲清楚了。
  • 描述: 结构图
  • 大小: 852.2 KB
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics