实战案例使用ADO和三层架构完成登录实例资料.docx
- 文档编号:9727884
- 上传时间:2023-02-06
- 格式:DOCX
- 页数:24
- 大小:220.70KB
实战案例使用ADO和三层架构完成登录实例资料.docx
《实战案例使用ADO和三层架构完成登录实例资料.docx》由会员分享,可在线阅读,更多相关《实战案例使用ADO和三层架构完成登录实例资料.docx(24页珍藏版)》请在冰豆网上搜索。
实战案例使用ADO和三层架构完成登录实例资料
实战案例:
使用ADO技术和三层架构实现用户登录案例
【案例描述】
现在我们将会通过一个简单的登录案例体会三层架构的开发。
登录是大部分的软件都会拥有的重要功能之一,虽然它不是在软件设计的功能用例之内,但它却是软件不可缺少的一部分,通过登录功能我们可以有效的阻止非授权的用户访问软件中保存的信息。
首先用户通过软件的提示输入用户名和密码,接下来软件将会通过ADO在数据库中使用select语句查询对应的信息,检测数据表中是否有满足用户输入的用户名和密码,并将检测出来的结果返回到记录集中,软件通过读取记录集中保存的信息,检测用户是否可以登录,如果可以登录则显示“允许登录”的提示,否则显示“不允许登录”的提示。
运行效果如下:
【三层架构的描述】
清楚了上面的需求之后我们可以使用三层架构来描述一下这个简单的功能。
首先我们从表示层出发。
表示层负责提示用户输入用户名和密码,并且接收用户输入的用户名和密码。
接下来通过调用事务层的函数Login,通过Login的形参将用户名和密码传递到事务层进行处理。
接下来事务层将传递的用户名和密码整理到SQL语句中,并调用数据库访问层的函数ExcuteSelect函数将这条SQL语句传递到数据库访问层。
数据库访问层通过ADO技术和数据库建立连接并且执行该SQL语句,将数据库执行出的结果通过RecordSet对象保存起来并且返回RecordSet对象的指针,通过该指针将RecordSet对象传递到事务层。
接下来事务层通过对该RecordSet对象进行“拆包”获取记录集中保存的结果,并判断该结果是否可以满足允许用户登录的条件,如果允许则返回true,否则返回false。
表示层中通过事务层的Login函数的返回值判断用户是否可以登录,如果可以则提示“允许登录”,否则提示“不允许登录”。
在VisualStudio中生成的关系依赖图如下:
(具体可见工程中的体系结构图)
【数据结构的设计】
该案例是一个非常简单的使用用户名和密码设计的案例,因此我们可以设计一个用于保存某个用户的用户名和密码的数据结构,在C++中,用户自定义的数据结构有很多种描述方案,我们此处选择面向对象的描述方法,使用类进行描述,例如User类,其类图如下:
【数据库设计】
接下来我们可以进行该项目的数据库设计了。
该数据库我们可以命名为Test,而数据库中只有一张数据表Login_Inf。
这张数据表中仅仅记录了可以登录软件的用户名和密码。
为了防止用户名有重复,我们规定用户名作为主键(作为主键的列不能为空,且值要唯一)。
同时为了防止非授权的用户登录软件(例如盗用密码或者清除密码),我们规定每个用户的密码文件不能够为空。
表格效果如下图:
建立数据库和数据表的代码如下:
createdatabaseTest--创建名称为Test的数据库
go
useTest--使用该数据库
go
createtableLogin_inf--创建登录信息表
(
UserNamevarchar(20)primarykey,--创建用户名列,为主键
PasswordKeyvarchar(20)notnull,--创建密码列,要求该列单元格添加数据不能为空值
)
go
insertintoLogin_infvalues('jianghanxin','jiang_1994221')--添加一个元组
运行之后在SQLServerManagementStudio的“对象资源管理器”中可以查看到我们所建立的数据库Test和数据表Login_inf。
如下图:
【第一步:
从数据库访问层开始】
接下来我们设计完成数据库之后我们可以开始对C++代码的设计。
首先我们从最基本的也是最底层的数据库访问层开始。
由于我们所有的操作都是要通过数据库访问层执行SQL语句完成的,因此数据库访问层我们可以这样描述。
数据库访问层用于和数据库打交道,用于将应用程序和SQLServer数据库建立连接,并且执行相应的数据操作。
如果有类似于查询的返回结果,则返回带有查询结果的记录集。
在完成数据库操作之后我们可以关闭数据库连接。
小提示:
每次执行数据库操作都必须要先打开数据库连接,完成操作之后,再将数据库连接关闭。
否则程序将会报错。
在了解了上述的功能描述后我们可以建立一个类描述数据库访问层,例如DataBase。
声明的代码如下:
#pragmaonce
#ifndefDATABASE_H
#defineDATABASE_H
#include
#include
#include
#include
#import"C:
\ProgramFiles\CommonFiles\System\ado\msado15.dll"no_namespacerename("EOF","adoEOF")
usingnamespacestd;
classDataBase
{
public:
DataBase(_bstr_tDataBaseName);
~DataBase(void);
intExcute(_bstr_tconst&CommandString);//执行非查询的数据库命令
_RecordsetPtr&ExcuteSelect(_bstr_tconst&CommandString);//执行查询的数据库命令
voidOpen();//打开数据库连接
voidClose();//关闭数据库连接
private:
_ConnectionPtrMyConnection;//数据库连接指针
_RecordsetPtrRecords;//数据库记录集指针
_bstr_tConnectionString;//连接字符串
};
#endif
DataBase函数:
DataBase函数是该类的构造函数,通常构造函数负责一些初始化的工作。
为了能够减少后期我们工作的麻烦,例如每次调用DataBase类都需要获取数据库字符串。
为了大家能够更好的理解数据库的连接字符串,我们在这里不妨对数据库的连接字符串进行一些分析。
有如下的连接字符串:
Provider=SQLOLEDB.1;IntegratedSecurity=SSPI;PersistSecurityInfo=False;InitialCatalog=Test;DataSource=(local)
其中Provider参数说明连接数据库所使用的驱动引擎,由于我们使用的是ADO技术,故此我们使用的SQLOLEDB.1驱动。
我们可以通过打开.udl文件,并且点击“提供程序”选项卡进行选择。
如下图:
此处我们可以看到所选择的是“MicorosoftOLEDBProviderforSQLServer”,其含义相信大家都能够明白吧!
当然,此处我需要再次解释一下ADO其实是对OLEDB访问技术的又一次封装,使得我们访问数据库变得更加的简单,因此说到底ADO技术的本质其实还是OLEDB。
因此我们在选择提供程序时应该选择MicorosoftOLEDBProvider,由于我们使用的数据库为SQLServer,因此我们需要选择的是forSQLServer。
接下来我们需要知道的是“InitialCatalog”参数,该参数用于描述应用程序究竟应该访问哪一个数据库,其后的等于号跟的是应用程序需要访问的数据库的名称,即Test。
另外我们还需要了解“DataScource”参数,该参数用于描述数据源,即数据库服务器的地址,由于我们的电脑上已经安装了SQLServer2008,此时我们的电脑也可以作为一个单独的数据库服务器,名称为(local)或者localhost。
上述的两个参数我们可以打开udl文件在“连接”选项卡中查看。
如下图:
为了我们每次编程的时候不需要去通过udl文件获取连接字符串,我们可以通过字符串的连接将连接字符串进行相应的处理。
由于我们此时需要操作的信息基本固定,除了每次所访问的数据库不同,因此我们只需要修改“InitialCatalog”参数后的值就可以了。
代码如下:
this->ConnectionString="Provider=SQLOLEDB.1;IntegratedSecurity=SSPI;PersistSecurityInfo=False;UserID=sa;InitialCatalog=";
this->ConnectionString+=DataBaseName;//将该变量的参数与InitialCatalog连接
this->ConnectionString+=";DataSource=(local)";
接下来我们还需要在构造函数中完成一件很重要的事情——初始化COM环境。
由于ADO也是COM组件,因此我们使用ADO之前都需要初始化COM组件,为了能够完成自动初始化环境,我们可以将初始化的代码放置到构造函数中(构造函数在创建对象时自动执行)。
初始化COM组件的代码如下:
//初始化COM编程环境
if(FAILED(:
:
CoInitialize(NULL)))
{
cout<<"初始化COM环境失败!
"< _getch(); return; } 大致来说构造函数中所要完成的事情都是一些琐碎的细枝末节,例如对于类中属性的初始化,以及对于环境的初始化等。 这些细枝末节通常是很多人都容易忘记的,也通常不在我们描述程序的范围之内,毕竟客户不需要知道这些技术细节,因此我们需要在构造函数中完成它们,让构造函数每次新建对象之时自动为我们进行初始化操作。 DataBase类的完整代码如下: DataBase: : DataBase(_bstr_tDataBaseName) { this->MyConnection=NULL;//设置连接指针为空,防止野指针 this->Records=NULL;//设置记录集指针为空,防止野指针 //设定连接字符串,需要连接的数据库通过构造函数的形参指定 this->ConnectionString="Provider=SQLOLEDB.1;IntegratedSecurity=SSPI;" this->ConnectionString+="PersistSecurityInfo=False;UserID=sa;InitialCatalog="; this->ConnectionString+=DataBaseName; this->ConnectionString+=";DataSource=(local)"; //初始化COM编程环境 if(FAILED(: : CoInitialize(NULL))) { cout<<"初始化COM环境失败! "< _getch();//使用C++标准的getch函数 return; } } ~DataBase函数: ~DataBase函数是DataBase类的析构函数,析构函数主要负责退出COM环境,以免每次新建对象时初始化环境失败。 析构函数代码如下: DataBase: : ~DataBase(void) { : : CoUninitialize();//退出COM环境 } Open函数: Open函数是DataBase类中由程序员所创建,负责创建连接对象,设定连接参数,并且打开数据库连接。 代码如下: voidDataBase: : Open() { try { MyConnection.CreateInstance(_uuidof(Connection));//创建连接对象 MyConnection->ConnectionString=this->ConnectionString;//指定连接字符串 MyConnection->ConnectionTimeout=10;//指定连接超时为10s do { MyConnection->Open("","","",adConnectUnspecified);//打开连接 } while(MyConnection->State! =adStateOpen); } catch(_com_errore){} } 其中try{}catch(_com_errore){}用于检测该代码是否会产生异常情况使得程序崩溃。 这一步是很有必要的。 当然我们在此设定只要连接状态处于关闭状态,就不断地向数据库使用MyConnection->Open函数不断的对数据库进行连接,直到连接上数据库为止。 Close函数: Close函数负责关闭数据库连接,为了能够提高数据库访问层的效率,我们可以先判断MyConnection中的State属性,如果处于打开状态(adStateOpen)则调用Close函数,关闭数据库连接。 代码如下: voidDataBase: : Close() { try { if(MyConnection->State==adStateOpen)//如果连接处于打开状态 { MyConnection->Close();//关闭数据库连接 } } catch(_com_errore){} } Excute函数: 对数据库的操作通常分为查询操作和非查询操作,非查询操作包括INSERT(插入)、UPDATE(更新)、DELETE(删除),Excute函数用于执行非查询操作的SQL语句,由于SQL语句的变数还是很多的,因此我们可以将SQL语句作为Excute函数的形参。 为了能够检测SQL语句是否执行成功,我们可以通过返回一个int类型的值(其含义为影响多少行)。 整体结构如下: intDataBase: : Excute(_bstr_tconst&CommandString) { } 此处我们将形参设定为const,防止程序员在使用CommandString参数时不小心改动了这个参数,而使用引用的目的则是为了节省该函数所占用的计算机内存。 如果我们直接使用值传递,则函数会在实参之外另外建立一个内存空间保存CommandString的值。 “浪费空间”这种事情对于一些配置较老的电脑来说是万万不能做的。 毕竟内存的容量还是有限的,不可能所有的内存都给你使用。 Excute函数的代码如下所示: intDataBase: : Excute(_bstr_tconst&CommandString) { try { _variant_tRecordsAffected;//记录影响的行数 this->Open();//打开数据库连接 this->MyConnection->CommandTimeout=10;//设置命令失败延时为10s this->MyConnection->Execute(CommandString,&RecordsAffected,adCmdText);//执行数据库命令 this->Close();//关闭数据库连接 return(int)RecordsAffected;//返回影响行数 } catch(_com_errore){} } 在此我们使用了刚刚已经在类中定义好的Open函数和Close函数负责打开和关闭数据库连接。 这样我们可以通过函数将代码封装起来,避免了重复写代码的麻烦。 ExcuteSelect函数: ExcuteSelect函数用于执行查询的操作,通常查询的过程是通过MyConnection中的Excute函数执行SQL语句,并且接收该函数返回的记录集指针(类型为_RecordSetPtr)。 接收完成之后我们可以将该记录集的指针作为返回值返回。 代码如下: _RecordsetPtr&DataBase: : ExcuteSelect(_bstr_tconst&CommandString) { try { _variant_tRecordsAffected; this->Records.CreateInstance(_uuidof(Recordset));//创建数据集 this->Open();//打开数据库连接 this->Records=MyConnection->Execute(CommandString,&RecordsAffected,adCmdText);//执行数据库命令 if(this->Records! =NULL) returnRecords; } catch(_com_errore){}//忽略异常 } 至此,数据库访问的构建结束,最底层的工作也已经做好了。 让我们来看看完整的DataBase类的代码吧! DataBase.h的内容如下: #pragmaonce #ifndefDATABASE_H #defineDATABASE_H #include #include #include #include #import"C: \ProgramFiles\CommonFiles\System\ado\msado15.dll"no_namespacerename("EOF","adoEOF") usingnamespacestd; classDataBase { public: DataBase(_bstr_tDataBaseName); ~DataBase(void); intExcute(_bstr_tconst&CommandString);//执行非查询的数据库命令 _RecordsetPtr&ExcuteSelect(_bstr_tconst&CommandString);//执行查询的数据库命令 voidOpen();//打开数据库连接 voidClose();//关闭数据库连接 private: _ConnectionPtrMyConnection;//数据库连接指针 _RecordsetPtrRecords;//数据库记录集指针 _bstr_tConnectionString;//连接字符串 }; #endif DataBase.cpp的内容如下: #include"DataBase.h" DataBase: : DataBase(_bstr_tDataBaseName) { this->MyConnection=NULL; this->Records=NULL; this->ConnectionString="Provider=SQLOLEDB.1;IntegratedSecurity=SSPI;PersistSecurityInfo=False;UserID=sa;InitialCatalog="; this->ConnectionString+=DataBaseName; this->ConnectionString+=";DataSource=(local)"; //初始化COM编程环境 if(FAILED(: : CoInitialize(NULL))) { cout<<"初始化COM环境失败! "< _getch(); return; } } DataBase: : ~DataBase(void) { : : CoUninitialize();//退出COM环境 } voidDataBase: : Open() { try { MyConnection.CreateInstance(_uuidof(Connection));//创建连接对象 MyConnection->ConnectionString=this->ConnectionString;//指定连接字符串 MyConnection->ConnectionTimeout=10;//指定连接超时为10s do { MyConnection->Open("","","",adConnectUnspecified);//打开连接 } while(MyConnection->State! =adStateOpen); } catch(_com_errore){} } voidDataBase: : Close() { try { if(MyConnection->State==adStateOpen)//如果连接处于打开状态 { MyConnection->Close();//关闭数据库连接 } } catch(_com_errore){} } intDataBase: : Excute(_bstr_tconst&CommandString) { try { _variant_tRecordsAffected; this->Open();//打开数据库连接 this->MyConnection->CommandTimeout=10; this->MyConnection->Execute(CommandString,&RecordsAffected,adCmdText);//执行数据库命令 this->Close();//关闭数据库连接 return(int)RecordsAffected;//返回影响行数 } catch(_com_errore){} } _RecordsetPtr&DataBase: : ExcuteSelect(_bstr_tconst&CommandString) { try { _variant_tRecordsAffected; this->Records.CreateInstance(_uuidof(Recordset));//创建数据集 this->Open();//打开数据库连接 this->Records=MyConnection->Execute(CommandString,&RecordsAffected,adCmdText);//执行数据库命令 if(this->Records! =NULL) returnRecords; } catch(_com_errore) { cout<<"错误原因: "< } } 【第二步: 借助数据库访问层完成任务的事务层】 接下来我们需要构造的是事务层,事务层我们也使用一个类进行描述,例如“Kernel”。 事务层的函数通过调用数据库访问层中的函数对数据库进行访问,而数据库访问层如何具体访问数据库的细节,事务层并不需要理会。 本案例中的事务层只有一个功能,就是通过数据库访问层的ExcuteSelect函数查找数据库中是否有符合条件的记录,然后将所得到的结果通过记录集返回到事务层,事务层再通过对记录集的“拆包”,取出返回的数据,并通过该
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实战 案例 使用 ADO 三层 架构 完成 登录 实例 资料