65.9K
CodeProject 正在变化。 阅读更多。
Home

连接池在静态库中

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.79/5 (5投票s)

2007年1月1日

CPOL

5分钟阅读

viewsIcon

64424

downloadIcon

1063

连接池在静态库中

引言

我们都知道,建立数据库连接是一个耗时的过程。因此,使用连接池的主要原因就是为了克服这个过程的延迟。连接池持有一批连接,除非连接池关闭,否则这些连接永远不会被释放。在设计连接池时,引入一个抽象层很重要,这个抽象层可以支持不同类型的连接,甚至不同类型的连接池。实现这一点的主要方法是引入一些设计模式。所有模式都在 [1] 中进行了描述,并在下方引用 [1] 的内容进行了总结。

抽象工厂

提供一个接口,用于创建相关或依赖对象的家族,而无需指定它们的具体类。

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

单例

确保一个类只有一个实例,并为其提供一个全局访问点。

当一个类必须只有一个实例,并且必须从一个众所周知的访问点访问它;或者当唯一的实例可以通过子类化来扩展,而客户端无需修改代码即可使用扩展的实例时,请使用单例模式。

工厂方法

定义一个创建对象的接口,但让子类决定实例化哪一个类。工厂方法允许一个类将实例化推迟到其子类。

当一个类无法预料它必须创建的对象的类时;当一个类希望其子类创建它创建的对象时;或者当类将责任委托给多个辅助子类之一,并且您想本地化关于哪个辅助子类是委托方的知识时,请使用工厂方法模式。

适配器

将一个类的接口转换为另一个客户端期望的接口。适配器允许原本由于接口不兼容而无法一起工作的类协同工作。

当您想使用一个现有类,但其接口与您需要的接口不匹配时;当您想创建一个可重用的类,该类可以与不相关或未预料到的类(即接口不一定兼容的类)协同工作时;或者(仅限对象适配器)当您需要使用几个现有的子类,但通过子类化所有子类来适配它们的接口不切实际时,请使用适配器模式。对象适配器可以适配其父类的接口。

连接池的构建

通过使用这些模式,可以将它们组合起来,得到一个适合连接池问题的设计。这种模式称为池化,并在 [2] 中进行了通用描述。CPool CResource 都是抽象的。它们用于创建抽象层,使得连接池可以与不同的连接甚至不同的连接池(从 CPoolFactory 的角度来看)重用。当深入一个级别时,就可以达到具体层。这一层包含了每个 abstract 对象的实现。

CPoolFactory 是一个单例 [1],它总是返回一个 CPool 类型的对象。CPool 总是返回一个 CResource 类型的对象。这两个对象的初始化都在具体层完成,因此对客户端是隐藏的,因为有 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

[2] 面向模式的软件架构
第 3 卷:资源管理模式
作者:Michael Kircher、Prashant Jain
Wiley,1995 年 10 月
ISBN: 0-470-84525-2

历史

  • 2007 年 1 月 1 日:首次发布
© . All rights reserved.