操作系统 程序设计 图形图像 媒体动画 机械电子 WEB开发 数据库 办公软件 路由技术 网络原理 网络架设 网络管理 认证培训
您的位置:计算机资讯网 >> 程序设计 >> VC专栏 >> 基础教程 -> 29、操纵数据库的内容
29、操纵数据库的内容
2004-10-12 11:14:28

    要真正编写数据库应用程序的话,前面的示例确实派不上大用场。甚至,一个实用程序的功能都不只是限于阅读数据库的内容,而这正是目前我们所能全部做到的。例如,我们甚至还没有在数据库中执行过添加新记录或者查找数据的方法。可以说,这两种特征是任何功能数据库应用程序甚至是实用程序的一部分。在本章节中,我们要向示例数据库中添加这两种功能。
    向表中添加记录
    只要在设计阶段做出了正确的选择,那么向表中添加新记录的实际过程就不会困难。在大多数情况下,DBMS会为你处理一切。你只需要以正确的方式提供正确的信息。例如,你可能想知道,为什么在本章上一节设计的窗体中,我选择使用Orders表的Food_ID字段,而不是Foods表的相应字段。一旦你考虑到了这一点,原因相对就简单了。使用表的Food_ID字段允许DBMS做出决定。如果在附加记录以后输入一个现有的Food_ID值,DBMS会简单地把新记录添加到Orders表中。但是,如果输入一个新的Food_ID值,DBMS就会把新记录添加到Foods和Orders两个表中。如果使用Foods表的Food_ID字段,就不可能有这个决定——DBMS每次都会指示把新记录添加到两个表中。如果用户输入现有Food_ID值的话,会引起主键值重复的结果,并最终导致新记录被拒绝。
    还有其它要考虑的原则。例如,你的应用程序处理数据有效性了吗?或者DBMS会为你处理数据有效性吗?在多数情况下,如果处理数据有效性的话,你可以提供一个更为友好的用户界面,但这样做也意味着应用程序会增大。我们的应用程序并没有执行任何数据有效性检查工作,原因有两个:第一,我想让这个示例足够简单,你可以看到所发生的一切。第二,它是一个实用型的程序,即大多数使用这个应用程序的人是像你一样的专业人员。
    在向应用程序中添加任何代码之前,必须执行一些设置。要做的第一件事是,添加一个增加记录的菜单项。下面的过程说明了如何完成这项任务(我们已经在第3章中学习了添加菜单项的方法,所以这里就不再讨论每一个详细步骤了)。
    1. 在Visual C++中选择ResourceView(资源视图)。双击Menu(菜单),然后双击IDR_MAINFRAME会看到如下图如示的菜单栏。

    2. 单击Record(记录)菜单,然后单击菜单底部的空白条目。输入&Append(添加),会看到如下图所示的Menu Item Properties(菜单项属性)对话框。

    3. 在Prompt(提示)域中输入Append a new record to the table.nAppendRecord。
    4. 单击Menu Item Properties(菜单项属性)对话框中的Close(关闭)框。Visual C++自动把ID_RECORD_APPEND ID的值赋给新的菜单项(双击的话,可以看到实际完成的情况)。
    5. 单击工具条上的Save All(全部保存)按钮或使用File|Save All(文件|全部保存)命令,确定已保存了全部的更改并进行了正常的登记,以便MFCClassWizard能够使用。
    现在我们有了可供使用的菜单项,下面要在ClassWizard中添加一些条目,以使其发挥作用。右击菜单条目,从上下文菜单中选择ClassWizard(类向导)。在MFC ClassWizard对话框中,应该看到Object IDs列表中已选择了ID_RECORD_APPEND条目(否则请选择它)。在Class Name(类名)下拉列表框中选择CODBC1View——这个类允许你查看数据库的内容,所以还要在这里为添加记录增加一些代码。加亮Messages(消息)列表中的COMMAND(命令)条目,然后单击Add Function(添加函数),你会看到Add Member Function(添加成员函数)对话框。单击OK接受缺省的函数名。MFC ClassWizard对话框看上去如下图所示。

    单击OK关闭MFC ClassWizard对话框。现在我们要修改先前创建的窗体(IDD_ODBC1_FORM)。有几种处理向数据库中添加新记录的方式,但是我已经选择了添加按钮棗实际上这是个一般按钮,如果需要的话,也可以把这个按钮用于其它目的。这时,把窗体大小调整为220×160,然后添加两个按钮。窗体的外观如下图所示。

    我们始终不想让按钮处于可见状态(因此可访问),所以需要修改几个属性。右击第一个按钮,然后从上下文菜单中选择Properties(属性),打开Properties(属性)对话框。清除Visible(可见)属性的选中标记,把ID修改为IDC_CHOICE1。对第二个按钮执行相同的操作,把它的ID修改为IDC_CHOICE2。
    注 如果想在对话框或窗口刚显示时隐藏某个控件,那么请不选中该控件的Visible属性。
    添加一些函数来处理用户在按钮上的单击操作,这一点也很重要。你只要右击第一个按钮,从上下文菜单中选择ClassWizard(类向导)选项。选择MessageMaps(消息映射)选项卡。在Class Name(类名)字段中选择CODBC1View。在Object IDs(对象ID)列表中找到IDC_CHOICE1条目并加亮它。现在加亮Messages(消息)字段中的BN_CLICKED条目,然后单击Add Function(添加函数)创建处理用户单击操作的函数框架。要为IDC_CHOICE2 Object ID条目进行相同的操作。下面是Member Functions(成员函数)列表的外观。

    单击OK关闭MFC ClassWizard对话框。我们以前已经为窗体上的每一个数据项字段都添加了一些成员变量,那些成员变量允许程序和数据库交换数据。但是,现在我们需要直接访问那些数据项字段,所以要再创建一些成员变量。
    注 Visual C++允许你把多个成员变量赋给单个对象。
    按住CTRL并双击窗体上的IDC_FOOD_ID控件。会看到Add MemberVariable(添加成员变量)对话框。在Member Variable Name(成员变量名)域中输入m_oFood_ID,然后在Category(分类)域中选择Control(控件)。单击OK完成这个过程。刚刚完成的是创建一个成员变量,它可以直接访问窗体上的控件。用下面的成员变量名为其它数据项控件执行相同的任务:IDC_NAME(m_oName),IDC_PERISHABLE(m_oPerishable)IDC_PRICE(m_oPrice),IDC_PURCHASE(m_oPurchase),IDC_QUANTITY(m_oQuantity)和IDC_STORAGE_LIFE(m_oStorage_Life)。
    我们来检查一下添加新变量的结果(这个想法不错,因为你要确保可以实际访问它们)。右击任何地方,然后从上下文菜单选择ClassWizard(类向导)。选择MFC ClassWizard对话框的Member Variables(成员变量)选项卡。应该在窗体上看到每个数据项控件的两个变量,如下图所示。
    我们再做一点提供程序可用性的修改。如果每次用户只是单击工具条上的按钮而不使用菜单的话,结果不是更好吗?打开ResourceView(资源视图)中的Toolbar(工具条)文件夹,然后双击IDR_MAINFRAME。你会看到系统显示一个工具条。在四个数据库移动按钮后面添加一个新按钮,并在上面画一个如下图所示的符号。这是和Access中New Record(新记录)按钮图案相同的符号(或者是合理的模仿符号)。

    要这个按钮发挥作用并不太困难。双击工具条上新的Append Record(添加记录)按钮(不是刚才你在绘图区所画的按钮,而是在工具条上的按钮),你就会看到Toolbar Button Properties(工具条按钮属性)对话框。从ID下拉列表框中选择ID_RECORD_APPEND。这把按钮和菜单命令联系起来。Prompt(提示)字段应该自动以正确的文本值填充,如下图所示。

    单击Close(关闭)框完成本次操作。这些就是把按钮添加到工具条上并使其发挥作用的全部操作步骤。
    现在向这一部分示例中添加一些代码。程序列表6.4说明了需要添加的代码。一定要把每一段代码都添加到正确的函数中。因为一些操作必须以特定的顺序进行,所以还要仔细地观察任务完成的顺序。最后,这个代码很明显不做任何类型的边界检查,所以在测试它时必须仔细地输入。Food ID字段一定要包含足够多的类型正确的字符。如果犯了某个错误而程序不正常恢复的话,请使用Access手工编辑表。








    看上去上述代码可真不少,但如果把它们分开来看的话,也就不难理解了。我们从OnRecordAppend()函数开始。应该做的第一件事是,检查数据库是否允许你添加记录。可以用CRecordset类的CanAppend()函数来完成这个工作。如果可以添加记录,这个函数返回TRUE。如果应用程序不能添加记录,它就显示一条出错消息,并像示例代码中那样退出。此时,你可以以只读方式之外的方式重新打开数据库。
    OnRecordAppend()函数的第二部分完成三个任务。第一,我们用与CWnd类相关联的SetWindowsText()给添加到窗体的两个通用按钮赋上一个标题。类总是继承其导出的基类的特征,认识这一点很重要。如果你查阅一下CButton类的文档,就会发现不能像Visual Basic语言那样设置Caption(标题)属性,所以完成这项工作需要一个像SetWindowsText()这样的函数(尽管函数名好像与其功能不相配)。第二,我们用CWnd类的ShowWindow()函数使按钮可见。同样,这一点可能与直觉有些不符,但它确实工作得很好。第三,我们要创建空白编辑框让用户编辑。你还可以提供建议值或者其它适合的值来填充CEdit控件。
    我们暂时先跳过OnChoice1()函数(用户单击Submit(提交)按钮时将调用这个函数),看看如果用户单击Cancel(取消)(它调用OnChoice2()函数)时会发生什么。OnChoice2()函数有两部分。第一部分应该很熟悉了。这一次我们不是让CEdit控件可见,而是把它隐藏起来。
    第二部分检索当前的查询值,并把它放回到窗体上的CEdit控件中。对用户来说,就好像是你刚刚恢复了记录,而实际完成的是恢复窗体中的值。请注意,我们必须用四种不同的技术来恢复前面的值。第一种技术是直接数据传输。第二种技术是把数据库提供的数值转换成字符串,然后把字符串放入窗体中。第三种技术重复我们在上一节所做的工作棗它只是格式化现有的字符串,所以实际显示成货币值。第四种技术使用Format()函数把日期转换成字符串。
    现在我们来看一看,如果用户填充窗体并单击Submit Data(提交数据)按钮的话,会发生什么。请记住,为了演示方便,这段代码被简化了棗可以添加一些代码来验证每个字段都包含了数据库可能实际使用的值。OnChoice1()函数需要做的第一件事是,看看我们是否正在向Foods(食品)表中的现有Food ID添加新条目,或者是否我们正在添加全新的Food ID。这个差别是很重要的,你只会在几个段落中看到。查找这个信息的过程分成如下四个步骤。
    1. 获得用户提供的Food ID值。在本例的情况下,我们用GetWindowText()函数来完成。
    2. 使用CRecordset类的MoveFirst()函数移动到查询的开始位置。
    3. 将查询的Food ID字段中的每一个值与用户提供的值进行比较。请注意,我们用MoveNext()函数在记录间移动。
    4. 如果找到了一个匹配值,那么把1New设置成FALSE并退出搜索循环。
    一旦你决定了要添加哪种类型的记录,你就需要使用AddNew()函数向数据库中添加一个空白记录。此时,这个空白记录只是保存在内存中。如果用户的机器出现电源故障,那么数据库中便不会有空白记录。此时,空白记录只是作为一个模板(如果你喜欢,也可以是空白窗体)来使用,以保存用户提供的数据值。
    现在向我们的空白记录中添加数据。请注意,从窗体获取数据的过程不像最初想像得那样简单。最简单的传输过程是从CEdit控件到查询中的字符串字段之间的传输。这个过程分成两个步骤。要做的第一件事是,用GetWindowText()函数把CEdit控件中当前的文本放入一个临时变量。填充了临时变量后,就可以把信息传输到数据库字段中了。
    复选框很有意思。Visual C++支持的复选框可以包含三个值中的一个:选中,未选中或者不确定。结果,CButton类的GetCheck()函数返回一个整数,而不是Boolean(布尔)值。你需要把这个整数值转换成Access使用的Boolean(布尔)值。
    下一个转换发生在Quantity和Storage Life字段中。这个过程分成三个步骤,模仿了我们为文本字段使用的二步骤过程。但是,我们要把用GetWindowText()获得的文本值转换成字段使用的整数值棗这算是第三步。示例代码用简单的atoi()函数来完成这项任务。
    你应该见过下面的两个转换。它们都以相对未变的形式出现在DoDataExchange()函数中。你仍然需要得到窗体上控件提供的Purchased(购买)字段的文本值,并把它转换成日期。这一点没有任何变化。同样地,要从Price(价格)字段删除美元符号。否则,Access不会接受字段值。
    现在我们有了填满了新数据值的空白窗体,可以用Update()函数来更新数据库了。这是数据库唯一真正脆弱的时刻。如果在更新的很短暂的时间内出现了电源故障,可能会得到不可预知的后果。必须说,虽然这种方法不安全,但它确实要比长时间打开记录的老技术的数据库好。
    OnChoice1()函数与数据库相关的最后一个动作是移动到最后一个条记录。你必须完成这个操作,这样用户才能实际看到新的记录。否则,用户可能会认为记录没有放到数据库中,并试图再添加一个。请注意,OnChoice1()函数以隐藏两个通用按钮来结束这个函数。除非用户实际上有新的记录要编辑,否则他们不能访问这些按钮,这一点很重要。所以,要尽可能地把它们隐藏起来。
    那么,空白记录看上去是什么样子呢?下图是我们的示例程序显示的空白记录。

    查找数据库中的数据
    不管应用程序看上去有多好,如果用户找不到他们所需要的东西,那么它也不会有太大的价值。构造出使用户在数据库中移动的最佳方式一直是众说纷纭的主题。有一些书,它们整章的主题都是关于以尽可能有效的方式查找所需要的东西。我们应该花很多的时间来学习搜索技术,但最后也并不会为你提供太多的帮助。使用ODBC时,有一种简单的查询方式;它也许不是最快或者最有效,但肯定能找到你所需要的东西。我们要在本节中集中讨论这项技术。
    很明显,在我们进一步深入探讨之前,还要再在菜单和工具条上花点时间。再次打开IDR_MAINFRAME菜单,这一次我们准备在Record(记录)菜单下添加Find(查找)菜单项。选择最后的空白并输入F&ind(注意,已经为First Record(第一个记录)选项在F下加了下划线)。还要在Prompt(提示)域中输入下述提示:Find a record in the database.nFind Record。此时的Menu Item Properties(菜单项属性)对话框应该如下图所示。

    单击Close(关闭)框完成新菜单项的创建工作。打开IDR_MAINFRAME工具条。下面是我添加的新的Find(查找)按钮,你当然也可以使用任何一种想要的按钮(用不着奇怪棗它确实是一副眼镜)。

    制作了按钮后,双击工具条上该按钮,就会看到一个Toolbar Button Properties(工具条按钮属性)对话框。从ID下拉列表框中选择ID_RECORD_FIND,然后单击Close(关闭)按钮。
    
现在,我们已经创建了一个函数框架。按住CTRL并双击ID_RECORD_FIND按钮,会看到MFC ClassWizard。应该选择ID_RECORD_FIND条目。一定要在Class Name字段中选择CODBC1View。加亮Messages(消息)列表中的COMMAND(命令)条目,然后单击Add Function(添加函数)。单击OK接受Add Member Function(添加成员函数)对话框中的缺省函数名OnRecordFind(我们要在本节的后面向这个函数中添加代码)。此时,MFC ClassWizard对话框看上去应该如下图所示。

     还遗漏了一件事。你打算如何询问用户要查找什么呢?完成这个任务需要一个比较小的对话框。单击OK关闭MFC ClassWizard对话框。回到ResourceView(资源视图)。右击Dialog(对话框)文件夹,从上下文菜单中选择Insert Dialog(插入对话框)。Visual C++会生成一个新的对话框。在我们继续操作之前,可以更改该对话框的一些属性。原因只有一个,为对话框赋予更容易辩认的名字。右击ResourceView(资源视图)中的IDD_DIALOG1条目,从上下文菜单中选择Properties(属性),会看到如下图所示的Dialog Properties(对话框属性)对话框。

    在ID字段中输入IDD_FIND_FOOD_ID。单击Close(关闭)框完成对话框属性的更改。
    对对话框本身我们只需要做两件事。要做的第一件事是,给对话框说明一个意义明确的标题。右击对话框本身,从上下文菜单中选择Properties(属性)。这一次会看到如下图所示的Dialog Properties(对话框属性)对话框(尽管这个对话框的名称和前面看到的相同,但是它的目的不同)。

    在Caption(标题)字段中输入Find Food ID(查找食品ID),然后单击Close(关闭)框完成此次更改。
    现在我们要添加单行Static Text(静态文本)控件和相关的Edit Box(编辑框)控件来完成这个对话框。下图是最后的对话框外观。

    对这个对话框所做的最后一件工作是,设置Edit Box(编辑框)控件。右击Edit Box(编辑框)控件,从上下文菜单中选择Properties(属性)。把Edit Properties(编辑属性)对话框中的ID属性改成IDC_FIND_FOOD_ID。单击Close(关闭)框完成这个动作。现在按住CTRL并双击Edit Box(编辑框)控件。你首先看到的是如下图所示的Adding a Class(添加类)对话框,它使人感到有些迷惑。

    出现Adding a Class(添加类)对话框的原因是,程序中的每一个对话框都属于某一个类。在很少情况下,你会想把对话框赋给一个现有类。就像应用程序中的About(关于)框,每一个对话框都应该有其自己的类。单击OK为FindFood ID(查找食品ID)对话框创建新类。Visual C++显示如下图所示的New Class(新类)对话框。

    在这个对话框中需要完成的工作只是在Name(名字)域中输入类名。可以使用任何想要的类名,但是我已经为示例程序中的类名选择了FindFoodID。单击OK完成新类的创建。单击OK选择MFC ClassWizard。现在,再次按住CTRL并双击Edit Box(编辑框)控件,这次会看到一开始就希望看到的Add MemberVariable(添加成员变量)对话框。在Member Variable Name(成员变量名)域中输入m_FindFoodID。确保Category(分类)域包含Value(值),Variable Type(变量类型)域包含CString。单击OK添加成员变量。
    还记得我们以前在CODBC1View类中创建的OnRecordFind()函数框架吗?现在要添加一些代码了。程序列表6.5显示了用来查找数据库中记录的代码。




    这段程序代码中的一部分看上去与本示例程序中其它部分的代码很相似。该代码引进了一两个新的函数。应该注意的第一件事是,要检查当前的数据库位置。某些类型的DBMS(Access不属于这一类)支持书签。它们的作用就像是看书时用来记住阅读位置的书签一样。不过,必须做的第一件事是,用CanBookmark()函数询问DBMS实际上是否支持书签。如果它支持书签,就可以用GetBookmark()函数来保存当前的位置,用SetBookmark()函数来恢复它。
    请注意,我们的搜索例程非常简单。如果找到了用户在对话框中请求的记录,就把Boolean(布尔)变量设成TRUE。否则,翻到下一条记录。继续这一查找过程,直至找到与用户查找标准相匹配的第一个记录,或者是已经查询到了最后一条记录。如果你的程序采用了相似的搜索例程,那么这两种情况一定都要测试到。
    注 完成一次搜索后总要复位记录指针,这样查询才能在搜索例程结束时停留在某个已知的记录位置。
    此时,程序要处理两种情况。如果没有找到所要检索的记录,程序显示一个错误消息对话框。如果DBMS支持书签的话,程序还试图返回到前一条记录。否则,程序将返回查询中的第一个记录。设置记录指针的位置是极其重要的;否则,DBMS可能会出现异常。如果找到了记录,那么我们就处理另一种情况,在数据输入窗体中显示信息。这由本节剩余的代码来完成。你应该注意到,这些代码看上去与我们在OnChoice2()函数末尾使用的代码十分相似。
    在实际编译并运行这个应用程序之前,还要再编写一段编码。在ODBCView1.CPP文件的顶部,你会看到很多#include指令。如果想使用Find FoodID(查找食品ID)对话框,就要在模块中包括它的头文件。添加一条如下所示的新#include指令:
    // Added for Find Food ID dialog support.
    #include "FindFoodID.h"
    注:在模块中用#include指令包括你想要使用的对话框的头文件。
    现在把所有的代码段都集中在一起,编译并运行程序。你会发现,在我们的小型表上,搜索例程运行得非常快,而当表变大时,它的速度就慢了下来。在大多数情况下,如果你计划处理巨大的数据库,就要购买一本大部头的包含了几千个搜索例程的书。当表包含了多达10,000条记录时,这个搜索例程也应该运行良好。当然,所有这些都依赖于表之间关系的复杂性以及它们所包含的字段数量。下面是搜索过程中Find FOOD ID对话框的外观。


转自:不详 作者:未知 关闭
加入收藏 推荐给好友 打印本文
内容为网上收集,并不代表本站同意或者赞同其观点,如果有任何版权,内容问题,请联系本站,我们将在第一时间处理.
查询
关键字
搜索范围
热点专题
服务
计算机资讯网 | 联系方式 | 广告服务 | 意见留言 | 友情链接 | 网站地图 | 设为首页