实战案例:使用ADO和三层架构完成登录实例课案_第1页
实战案例:使用ADO和三层架构完成登录实例课案_第2页
实战案例:使用ADO和三层架构完成登录实例课案_第3页
实战案例:使用ADO和三层架构完成登录实例课案_第4页
实战案例:使用ADO和三层架构完成登录实例课案_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、实战案例:使用 ADO技术和三层架构实现用户登录案例【案例描述】现在我们将会通过一个简单的登录案例体会三层架构的开发。登录是大部分的软件都会拥有的重要功能之一,虽然它不是在软件设计的功能用例之内, 但它 却是软件不可缺少的一部分,通过登录功能我们可以有效的阻止非授权的用户访 问软件中保存的信息。首先用户通过软件的提示输入用户名和密码,接下来软件将会通过ADO在数据库中使用select语句查询对应的信息,检测数据表中是否有满足用户输入的 用户名和密码,并将检测出来的结果返回到记录集中, 软件通过读取记录集中保 存的信息,检测用户是否可以登录,如果可以登录则显示“允许登录”的提示, 否则显示“不允

2、许登录”的提示。运行效果如下:【三层架构的描述】清楚了上面的需求之后我们可以使用三层架构来描述一下这个简单的功能。首先我们从表示层出发。表示层负责提示用户输入用户名和密码, 并且接收用户 输入的用户名和密码。接下来通过调用事务层的函数 Login,通过Login的形参 将用户名和密码传递到事务层进行处理。接下来事务层将传递的用户名和密码整理到SQL语句中,并调用数据库访问层的函数ExcuteSelect函数将这条SQL语句传递到数据库访问层。数据库访问层通过ADO技术和数据库建立连接并且执行该 SQL语句,将数 据库执行出的结果通过 RecordSe对象保存起来并且返回RecordSetM象的

3、指针, 通过该指针将RecordSet对象传递到事务层。接下来事务层通过对该RecordSet对象进行“拆包”获取记录集中保存的结 果,并判断该结果是否可以满足允许用户登录的条件,如果允许则返回true,否则返回false。表示层中通过事务层的Login函数的返回值判断用户是否可以登录, 如果可 以则提示“允许登录”,否则提示“不允许登录”在Visual Studio中生成的关系依赖图如下:0住Uwr? aetUsi*F,4*ie KerrrPl色Ul% Kernel(具体可见工程中的体系结构图)I PrirrtLogiH【数据结构的设计】该案例是一个非常简单的使用用户名和密码设计的案例,因此

4、我们可以设计 一个用于保存某个用户的用户名和密码的数据结构, 在C+中,用户自定义的数 据结构有很多种描述方案,我们此处选择面向对象的描述方法,使用类进行描述, 例如User类,其类图如下:农User日特性* password : Stringusername : String-操作+ getPassWord() : String+ getUserName(): String亠 setPassWordfpassword : String)+ setUserNamefusename : String)【数据库设计】接下来我们可以进行该项目的数据库设计了。该数据库我们可以命名为Test,而数据库中

5、只有一张数据表 Loginnf。这张数据表中仅仅记录了可以登录软件的用户名和密码。为了防止用户名有重复,我们规定用户名作为主键(作为 主键的列不能为空,且值要唯一)。同时为了防止非授权的用户登录软件(例如 盗用密码或者清除密码),我们规定每个用户的密码文件不能够为空。表格效果 如下图:UserNamePasswordKeyji 吕 n gh 吕 nxinjiang_1994221NULL建立数据库和数据表的代码如下:create database Test - 创建名称为Test的数据库gouse Test -使用该数据库gocreate table Loginn f -创建登录信息表(Use

6、rName varchar(20) primary key,-创建用户名列,为主键PasswordKey varchar(20) not null,-创建密码列,要求该列单元格添加数据不能为空值)goin sert into Loginn f values(jia ngha nxin ,jia ng_1994221)-添加一个元组运行之后在SQL Server Management Studio的 “对象资源管理器”中可以查 看到我们所建立的数据库Test和数据表Loginnf。如下图:B J Test曰口表1 dbo.Login_inf 视图田LJ同义词S 口可编程性| |_i Servic

7、e Brokeri I存赭B LJ安锂【第一步:从数据库访问层开始】接下来我们设计完成数据库之后我们可以开始对 C+代码的设计。首先我们 从最基本的也是最底层的数据库访问层开始。 由于我们所有的操作都是要通过数 据库访问层执行SQL语句完成的,因此数据库访问层我们可以这样描述。数据库访问层用于和数据库打交道,用于将应用程序和 SQL Server数据库 建立连接,并且执行相应的数据操作。如果有类似于查询的返回结果,则返回带 有查询结果的记录集。在完成数据库操作之后我们可以关闭数据库连接。小提示:每次执行数据库操作都必须要先打开数据库连接,完成操作之后,再将数据库连接关闭。否则程序将会报错。在了

8、解了上述的功能描述后我们可以建立一个类描述数据库访问层,例如DataBase声明的代码如下:#pragma once#ifndef DATABASE#defi ne DATABASE_H#i nclude #i nclude #in clude #in clude vconi o.h#importC:ProgramFilesCom monFilesSystemadomsado15.dllno_namespace rename(EOF,adoEOF) using n amespace std;class DataBasepublic:DataBase(_bstr_t DataBaseName);

9、DataBase(void);int Excute(_bstr_t co nst & Comma ndStri ng);/执行非查询的数据库命令_RecordsetPtr & ExcuteSelect(_bstr_t co nst & Comma ndStri ng);/执行查询的数据库命令void Ope n(); 打开数据库连接void Close();/关闭数据库连接private:_Co nn ectio nPtr MyCo nn ectio n;数据库连接指针_RecordsetPtr Records;/数据库记录集指针_bstr_t Co nn ectio nStri ng;/连接

10、字符串; 一一#en difDataBase 函数:DataBase函数是该类的构造函数,通常构造函数负责一些初始化的工作。为 了能够减少后期我们工作的麻烦,例如每次调用 DataBase类都需要获取数据库 字符串。为了大家能够更好的理解数据库的连接字符串,我们在这里不妨对数据库的连接字符串进行一些分析。有如下的连接字符串:Provider=SQLOLEDB.1;l ntegratedSecurity=SSPI;PersistSecurityIn fo=False;I nitial Catalog=Test;Data Source=(local)其中Provider参数说明连接数据库所使用的驱

11、动引擎,由于我们使用的是ADO技术,故此我们使用的SQLOLEDB.1驱动。我们可以通过打开.udl文件, 并且点击“提供程序”选项卡进行选择。如下图:提供程序连挨高级所有选择您蒂望连接的姒据:Mi croEoft OLH DEMi crosoft OLE DBMicrcsoft OLE DEMi croEoft OLE DEMi crosoft OLE EBPrcvi der Provi derFrovi der Prcvi derProvi tierfor Analysie Sarvi c?s 10.0 for iRdexing Servi cs for ODBC Driversfor S

12、tarchfor SQL ServerMicrcsoft OLE DE Simple FrcviderHSDataShpeOLE DB Fravidar far Microsoft Directory ServicesSQL Native ClientQL Sarwr Fativw Cli ent 10.0SQL Sarver Kative Cli ent 11.0此处我们可以看到所选择的是“Micorosoft OLE DB Provider for SQLServer”,其含义相信大家都能够明白吧!当然,此处我需要再次解释一下ADO其实是对OLE DB访问技术的又一次封装,使得我们访问数据

13、库变得更加的简 单,因此说到底ADO技术的本质其实还是OLE DB。因此我们在选择提供程序 时应该选择 Micorosoft OLE DB Provider,由于我们使用的数据库为 SQL Server,因此我们需要选择的是for SQL Server。接下来我们需要知道的是“ Initial Catalog”参数,该参数用于描述应用程序 究竟应该访问哪一个数据库,其后的等于号跟的是应用程序需要访问的数据库的 名称,即Test。另外我们还需要了解“ Data ScourcW参数,该参数用于描述数据源,即数 据库服务器的地址,由于我们的电脑上已经安装了SQL Server 2008,此时我们的电

14、脑也可以作为一个单独的数据库服务器,名称为(local)或者localhost。上述的两个参数我们可以打开udl文件在“连接”选项卡中查看。如下图:提洪程序连接一1高级丨所有指定下列设蛊以连接到SQL Server数扌民选择或输入朋务器名称i;(local)2.3.输入登录朋务器的信息:0使用Windowz NT隼戚安全设置 使用指走的用户名称和密码(V): 用户名称3);密码(F):二空白密码0在朋务器上选择数拥库6上允许保存癱用程序標作Test1.为了我们每次编程的时候不需要去通过udl文件获取连接字符串,我们可以通过字符串的连接将连接字符串进行相应的处理。由于我们此时需要操作的信息基本固

15、定,除了每次所访问的数据库不同,因此我们只需要修改“Initial Catalog 参数后的值就可以了。代码如下:this-Co nn ectio nStri ng= Provider=SQLOLEDB.1;l ntegrated Security=SSPI;PersistSecurity Info=False;User ID=sa;Initial Catalog=;this-Co nn ectio nStri ng += DataBaseName;/将该变量的参数与In itial Catalog连接this-C onnection Stri ng += ;Data Source=(loca

16、l);接下来我们还需要在构造函数中完成一件很重要的事情初始化 COM环 境。由于ADO也是COM组件,因此我们使用ADO之前都需要初始化COM组 件,为了能够完成自动初始化环境,我们可以将初始化的代码放置到构造函数中(构造函数在创建对象时自动执行)。初始化COM组件的代码如下:/初始化COMS程环境if(FAILED(:Col ni tialize(NULL)cout初始化 COM境失败! MyCo nn ection = NULL;/设置连接指针为空,防止野指针this-Records = NULL;/设置记录集指针为空,防止野指针/设定连接字符串,需要连接的数据库通过构造函数的形参指定th

17、is-Co nn ectio nStri ng=Provider=SQLOLEDB.1;l ntegratedSecurity=SSPI;this-C onn ectio nStri ng+= Persist SecurityIn fo=False;UserID=sa;I nitial Catalog=;this-C onn ectio nStri ng += DataBaseName;this-C onn ecti on Stri ng += ;Data Source=(local);/初始化COMS程环境if(FAILED(:CoI ni tialize(NULL)coutvv初始化COM

18、环境失败! Co nn ectio nStri ng = this-C onn ectio nStri ng;/指定连接字符串MyCo nn ectio n-Co nn ectio nTimeout = 10;/指定连接超时为 10sdoMyCo nn ectio n-Ope n( ,adCo nn ectU nspecified);/打开连接while(MyCo nn ectio n-State != adStateOpe n);catch(_com_error e) 其中trycatch(_com_error e)用于检测该代码是否会产生异常情况使得程序崩溃。这一步是很有必要的。当然我们在

19、此设定只要连接状态处于关闭状态,就不断地向数据库使用MyConnection-Open函数不断的对数据库进行连接,直 到连接上数据库为止。Close函数:Close函数负责关闭数据库连接,为了能够提高数据库访问层的效率,我们 可以先判断MyConnection中的State属性,如果处于打开状态(adStateOpe则 调用Close函数,关闭数据库连接。代码如下:void DataBase:Close()tryif(MyCo nn ectio n-State = adStateOpe n)如果连接处于打开状态MyCo nn ectio n-Close(); 关闭数据库连接 catch(_co

20、m_error e) Excute 函数:对数据库的操作通常分为查询操作和非查询操作,非查询操作包括INSERT(插入)、UPDATE (更新)、DELETE (删除),Excute函数用于执行非查询操 作的SQL语句,由于SQL语句的变数还是很多的,因此我们可以将 SQL语句 作为Excute函数的形参。为了能够检测 SQL语句是否执行成功,我们可以通过 返回一个int类型的值(其含义为影响多少行)。整体结构如下:int DataBase:Excute(_bstr_t const &Comma ndStri ng) _此处我们将形参设定为con st,防止程序员在使用Comma ndStri

21、 ng参数时不 小心改动了这个参数,而使用引用的目的则是为了节省该函数所占用的计算机内 存。如果我们直接使用值传递,则函数会在实参之外另外建立一个内存空间保存 CommandString的值。“浪费空间”这种事情对于一些配置较老的电脑来说是万 万不能做的。毕竟内存的容量还是有限的,不可能所有的内存都给你使用。Excute函数的代码如下所示:int DataBase:Excute(_bstr_t const &Comma ndStri ng)try_varia nt_t RecordsAffected;/记录影响的行数this-Ope n();打开数据库连接this-MyCo nn ectio

22、n-Comma ndTimeout = 10;/设置命令失败延时为10sthis-MyC onn ectio n-Execute(Comma ndStri ng,&RecordsAffected,adCmdT ext);/执行数据库命令this-Close();关闭数据库连接return (in t)RecordsAffected;/返回影响行数catch(_com_error e) 在此我们使用了刚刚已经在类中定义好的 Open函数和Close函数负责打开 和关闭数据库连接。这样我们可以通过函数将代码封装起来,避免了重复写代码 的麻烦。ExcuteSelect函数:ExcuteSelect函

23、数用于执行查询的操作,通常查询的过程是通过 MyConnection中的Excute函数执行SQL语句,并且接收该函数返回的记录集指 针(类型为_RecordSetPt)。接收完成之后我们可以将该记录集的指针作为返回 值返回。代码如下:_RecordsetPtr & DataBase:ExcuteSelect(_bstr_t const &Comma ndStri ng)try_varia nt_t RecordsAffected;this-Records.Create In sta nce(_uuidof(Recordset);仓 U建数据集this-Ope n();打开数据库连接this-

24、Records=MyCo nn ectio n-Execute(Comma ndStri ng,&RecordsAffected,adCmdText);执行数据库命令if(this-Records!=NULL)return Records; catch(_com_error e)忽略异常至此,数据库访问的构建结束,最底层的工作也已经做好了。让我们来看看完整的DataBase类的代码吧!DataBase.h的内容如下:#pragma once#ifndef DATABASE#defi ne DATABASE_H#i nclude #i nclude #in clude #in clude vco

25、ni o.h#importC:ProgramFilesCom monFilesSystemadomsado15.dllno_namespace rename(EOF,adoEOF) using n amespace std;class DataBasepublic:DataBase(_bstr_t DataBaseName);DataBase(void);int Excute(_bstr_t co nst & Comma ndStri ng);/执行非查询的数据库命令_RecordsetPtr & ExcuteSelect(_bstr_t co nst & Comma ndStri ng);/

26、执行查询的数据库命令void Ope n(); 打开数据库连接void Close();/关闭数据库连接private:_Co nn ectio nPtr MyCo nn ectio n;数据库连接指针_RecordsetPtr Records;/数据库记录集指针_bstr_t Co nn ectio nStri ng;/连接字符串; 一一#en difDataBase.cpp的内容如下:#include DataBase.hDataBase:DataBase(_bstr_t DataBaseName) this-MyCo nn ection = NULL; this-Records = NU

27、LL;this-Co nn ectio nStri ng=Provider=SQLOLEDB.1;l ntegratedSecurity=SSPI;Persist Security In fo=False;User ID=sa;I nitialCatalog=;this-C onn ectio nStri ng += DataBaseName;this-C onn ecti on Stri ng += ;Data Source=(local);/初始化COMS程环境if(FAILED(:Col ni tialize(NULL)coutvv初始化COM环境失败! Co nn ectio nStr

28、i ng = this-C onn ectio nStri ng;/扌旨定连接字符串MyCo nn ectio n-Co nn ectio nTimeout = 10;/指定连接超时为 10sdoMyCo nn ectio n-Ope n( ,adCo nn ectU nspecified);/打开连接while(MyCo nn ectio n-State != adStateOpe n); catch(_com_error e) _void DataBase:Close()tryif(MyC onnection-State = adStateOpe n)女口果连接处于打开状态MyCo nn

29、ectio n-Close();关闭数据库连接catch(_com_error e) _int DataBase:Excute(_bstr_t const &Comma ndStri ng) _try_varia nt_t RecordsAffected;this-Ope n();打开数据库连接this-MyC onn ectio n-Comma ndTimeout = 10;this-MyC onn ectio n-Execute(Comma ndStri ng,&RecordsAffected,adCmdT ext);/执行数据库命令this-Close();关闭数据库连接return (

30、in t)RecordsAffected;返回影响行数catch(_com_error e)_RecordsetPtr & DataBase:ExcuteSelect(_bstr_t const &Comma ndStri ng)try_varia nt_t RecordsAffected;this-Records.Create In sta nce(_uuidof(Recordset);仓 U建数据集this-Ope n();打开数据库连接this-Records=MyCo nn ectio n-Execute(Comma ndStri ng,&RecordsAffected,adCmdTe

31、xt);执行数据库命令if(this-Records!=NULL)return Records;catch(_com_error e) _coutvv错误原因:e.ErrorMessage()endl;【第二步:借助数据库访问层完成任务的事务层】接下来我们需要构造的是事务层,事务层我们也使用一个类进行描述,例如“ Kernel”。事务层的函数通过调用数据库访问层中的函数对数据库进行访问, 而数据库访问层如何具体访问数据库的细节,事务层并不需要理会。本案例中的事务层只有一个功能,就是通过数据库访问层的 ExcuteSelect函数查找数据库中 是否有符合条件的记录,然后将所得到的结果通过记录集返

32、回到事务层,事务层再通过对记录集的“拆包”,取出返回的数据,并通过该数据判断是否可以登录。用于判断用户是否存在于数据表中,我们可以使用Select语句以及SQL语句中的聚合函数COUNT(*)完成。SQL语句如下:select COUNT(*) from Loginnfwhere UserName = USERNAME and PasswordKey = PASSWORD其中USERNAME指示需要查找的用户名,PASSWORD指示用户的密码。 例如:selectCOUNT(*)-符合条件的记录数fromLoginn f - 查找的数据表whereUserName = jian gha nxi

33、 n and PasswordKey = jia ng_1994221-查找条件接下来我们可以先在 SQL Server Management Studio中执行一下,看一下返回的记录的样子。执行的结果如下图所示:二结果占消息1氏列名)工二我们可以看到上图中所写的列的名称是“(无列名)”,那对于这种没有列名的表格的记录集我们又应该怎么访问呢?其实这些没有列名的表格默认从0开 始升序排序。因此我们可以使用该编号进行访问。 上图中只有一个单元格,也就 是第0列。好了,话题扯得有些远了,我们现在来看一下Kernel类的声明代码吧!Kernel.h文件用于存放Kernel类的声明,事实上,通常类的声明

34、都放置在头文件 中。代码如下:#pragma once #ifndef KERNEL #defi ne KERNEL_H #include DataBase.h #include User.h #in clude #i nclude using n amespace std; class Kernel public:Kernel(); Kernel(); bool Log in( User &user); private:DataBase *db;第三层指针 ;#en dif其中db是DataBase类的指针,也就是指向数据库访问层的指针,接下来我们将会通过这个指针调用数据库访问层的函数,进而

35、完成访问数据库的操作。接下来我们还是解析一下Kernel类的函数吧!Kernel 函数:Kernel函数是Kernel类的构造函数,用于初始化私有对象,也就是db。由于db只是一个指针,因此我们需要使用new关键字调用DataBase类的构造函数 创建对象。当然很多人有个疑问,为什么不能够直接使用DataBase类建立对象呢?那是因为私有对象不能够直接初始化,而且DataBase类没有无参构造函数,但却有有参构造函数,结果有参构造函数先入为主,就不能够直接完成初始化的 操作了。Kernel类的代码如下:Kernel:Kernel()this-db = new DataBase(Test);创建

36、数据库访问层的对象Kernel 函数:Kernel函数是Kernel类的析构函数,用于使用 delete关键字释放db所指 向的数据库访问层对象。代码如下:Kernel:Kernel()delete db;/ 删除数据库对象Login函数:Login函数是程序员自定义的函数,用于判断用户是否登录,代码如下:bool Kern el:Log in( User &user)bool flag = false;/flag变量指示用户是否能够登录int coun t = 0;/保存查询的结果/设置需要执行的SQL语句stri ng Comma ndStri ng = select COUNT(*)from Loginnf where UserName一川一 JComma ndStri ng += user.getUserName() + and PasswordKey =;Comma ndStri ng += use

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论