连接池在静态库中
连接池在静态库中
引言
我们都知道,建立数据库连接是一个耗时的过程。因此,使用连接池的主要原因就是为了克服这个过程的延迟。连接池持有一批连接,除非连接池关闭,否则这些连接永远不会被释放。在设计连接池时,引入一个抽象层很重要,这个抽象层可以支持不同类型的连接,甚至不同类型的连接池。实现这一点的主要方法是引入一些设计模式。所有模式都在 [1] 中进行了描述,并在下方引用 [1] 的内容进行了总结。
抽象工厂
提供一个接口,用于创建相关或依赖对象的家族,而无需指定它们的具体类。

当系统应独立于其产品的创建、组合和表示方式时;当系统应使用多个产品家族之一进行配置时;当一组相关产品对象被设计为一起使用,并且您需要强制执行此约束时;或者当您想提供一个产品类库,但只想暴露它们的接口,而不是它们的实现时,请使用抽象工厂模式。
单例
确保一个类只有一个实例,并为其提供一个全局访问点。

当一个类必须只有一个实例,并且必须从一个众所周知的访问点访问它;或者当唯一的实例可以通过子类化来扩展,而客户端无需修改代码即可使用扩展的实例时,请使用单例模式。
工厂方法
定义一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法允许一个类将实例化推迟到其子类。

当一个类无法预料它必须创建的对象的类时;当一个类希望其子类创建它创建的对象时;或者当类将责任委托给多个辅助子类之一,并且您想本地化关于哪个辅助子类是委托方的知识时,请使用工厂方法模式。
适配器
将一个类的接口转换为另一个客户端期望的接口。适配器允许原本由于接口不兼容而无法一起工作的类协同工作。

当您想使用一个现有类,但其接口与您需要的接口不匹配时;当您想创建一个可重用的类,该类可以与不相关或未预料到的类(即接口不一定兼容的类)协同工作时;或者(仅限对象适配器)当您需要使用几个现有的子类,但通过子类化所有子类来适配它们的接口不切实际时,请使用适配器模式。对象适配器可以适配其父类的接口。
连接池的构建
通过使用这些模式,可以将它们组合起来,得到一个适合连接池问题的设计。这种模式称为池化,并在 [2] 中进行了通用描述。CPool
和 CResource
都是抽象的。它们用于创建抽象层,使得连接池可以与不同的连接甚至不同的连接池(从 CPoolFactory
的角度来看)重用。当深入一个级别时,就可以达到具体层。这一层包含了每个 abstract
对象的实现。

CPoolFactory
是一个单例 [1],它总是返回一个 CPool
类型的对象。CPool
总是返回一个 CRe
类型的对象。这两个对象的初始化都在具体层完成,因此对客户端是隐藏的,因为有 source
abstract
层。这使我们能够重用而不是重新实现,因为当连接到另一个数据库时,只需要创建一个 CConnectionAdapter
的新实现。完成之后,需要确保连接池实例化正确的连接——但这也很容易。接口仍然规定它返回一个 CResource
。这意味着新的实现也必须实现它,以防止违反派生合同。因此,只需要更改池中之前实现创建的位置,因为两个实现都使用 CResource
抽象——客户端无需更改任何内容。
C++ 中的实现
创建连接池时,它会从数据库获取初始大小并创建初始数量的连接。所有这些都在 CConnectionPool
类的构造函数中完成。
CConnectionPool::CConnectionPool()
{
m_pool.clear();
m_initSize = 0;
long connstr = 0;
CRegistryFacade *registry = new CRegistryFacade;
registry->getKeyFromRegistry("InitSize", &connstr);
if (0 == connstr)
{
registry->setKeyToRegistry("InitSize", "10");
registry->getKeyFromRegistry("InitSize", &connstr);
}
delete registry;
m_initSize = connstr;
for (int i=0; i<m_initSize; i++)
{
CConnectionAdapter * resource = new CConnectionAdapter;
resource->setInUse(false);
m_pool.push_back(resource);
}
}
CConnectionPool
类有一个名为 CConnectionPool::findNewConnection()
的特殊方法,用于在池中查找一个可用的新连接,并且该方法由 CConnectionPool::aquire()
方法使用。
CResource* CConnectionPool::findNewConnection()
{
bool found = false;
CResource *entry;
list<CResource*>::iterator ite;
ite=m_pool.begin();
while(ite!=m_pool.end() && !found) {
entry = *ite;
CConnectionAdapter * adapter =
dynamic_cast<CConnectionAdapter*>(entry);
if (! adapter->getInUse()) {
adapter->setInUse(true);
found = true;
}
ite++;
}
return entry;
}
连接是通过 CConnectionPool::aquire()
和 CConnectionPool::release(CResource* res)
方法获取和释放的。这些方法也可以通过 abstract
层访问,可以在 CPool
类中找到。
CResource* CConnectionPool::aquire()
{
CResource * resource = NULL;
if (NULL == (resource = findNewConnection())) {
resource = new CConnectionAdapter;
m_pool.push_back(resource);
}
return resource;
}
void CConnectionPool::release(CResource* res)
{
bool found = false;
list<CResource*>::iterator ite;
ite=m_pool.begin();
while(ite!=m_pool.end() && !found) {
CResource* entry = *ite;
if (entry->operator == (*res)) {
CConnectionAdapter *adapter =
dynamic_cast<CConnectionAdapter*>(entry);
adapter->setInUse(false);
found = true;
}
ite++;
}
}
ADO 连接封装在 CConnectionAdapter
类中。这个类的主要职责是持有连接,但它也包含其他信息,例如一个布尔值,用于指示连接是否正在使用,以及一个唯一的标识符等。
CConnectionAdapter::CConnectionAdapter()
{
CoInitialize(NULL);
char* connstr = NULL;
CRegistryFacade *registry = new CRegistryFacade;
registry->getKeyFromRegistry("ConnectionString", &connstr);
if (NULL == connstr || 0 == strlen(connstr))
{
//Failed to retrieve the connection string
//Write default settings to the registry and use them
registry->setKeyToRegistry
("ConnectionString", "DATABASE=db;DSN=dsn;UID=sa;PWD=sa");
if (NULL != connstr) {
free(connstr);
connstr = NULL;
}
registry->getKeyFromRegistry("ConnectionString", &connstr);
}
delete registry;
m_connection.CreateInstance(__uuidof(Connection));
m_connection->CursorLocation = adUseServer;
m_connection->Open( connstr, L"", L"", adConnectUnspecified);
m_currentlyUsed = true;
CoCreateGuid (&m_id);
}
创建 CConnectionAdapter
类的实例时,它会从注册表中获取连接字符串(目前是未加密的!),建立连接并设置唯一标识符。该类有一个 CConnectionAdapter::getConnection()
方法,该方法提供当前连接的指针。
使用连接池
使用此连接池时,它提供了开箱即用的功能,其中所有设置(如连接字符串和初始大小)都存储在注册表中。它还提供了基于此库提供的抽象层创建另一个连接池的可能性。通过这种方式,可以创建一个支持 Oracle 连接的连接池(如果需要)。
//An instance of the pool factory has to be achieved
CPoolFactory *factory = CPoolFactory::getPoolFactory();
//Get an instance of the pool
CPool *pool = factory->getPool();
//Retrieve a connection from the pool
CResource *res = NULL;
res = pool->aquire();
//Release the connection when it is no more needed
pool->release(res);
请注意,该库基于 Microsoft 数据访问组件 (MDAC)。所有与 MDAC 无关的实现都封装在 CConnectionAdapter
类中。
参考文献
[1] 设计模式:可复用面向对象软件的基础
作者:Erich Gamma、Richard Helm、Ralph Johnson、John Vissides
Addison Wesley,1995
ISBN: 0-201-63361-2
第 3 卷:资源管理模式
作者:Michael Kircher、Prashant Jain
Wiley,1995 年 10 月
ISBN: 0-470-84525-2
历史
- 2007 年 1 月 1 日:首次发布