目录
概述 2
开始编写一个数据库应用程序: 2
建立一个数据库应用程序 2
对数据库的操作:
1.打开数据库: 3
2.查询: 3
3.更新数据库: 4
4.添加数据: 4
5.删除:
6.排序: 5
CDaoDatabase class 和CdaoRecordset class
插入Crecordset派生类:
课程设计要求
概述
ODBC和ADO
MFC中对数据库的链接方法主要有两种:DOBC和ADO。
ODBC是微软公司提供的一组数据库访问方式,包含一系列的对数据库进行操作的API函数。一个ODBC应用程序对数据库的操作并不依赖于任何的DBMS,而是由对应的DBMS的ODBC驱动来完成。这些驱动有些在安装数据库或者操作系统或者编译软件的时候就已经装上了,所以我们并不需要考虑驱动安装的问题。而由于ODBC对数据库的访问操作时通过驱动来完成的,所以不管是什么类型的数据库,我们只要在数据源中指定了相应的驱动就可以在应用程序中使用ODBC来访问。
一个完整的ODBC程序包括:应用程序,ODBC管理器,驱动程序管理器,ODBC API,驱动程序和数据源,我们在编写一个ODBC应用程序的时候,就是一个注册数据源,编写程序对数据源的数据库进行访问的过程。
更多信息请查阅相关资料。
ADO。面向对象的数据库访问技术,基于OLE DB,是一种高层的访问技术,速度快,易于使用。在MFC中,ADO的许多使用方法类似于ODBC。
开始编写一个数据库应用程序:
前面说了一些必要的废话,只是为了象征性的说明我们在应用一门技术的时候要掌握其基本原理和特征。下面开始将怎么在XP + SQL Server2000 + VC++6.0环境中编写一个基于ODBC的数据库程序,ADO在使用中和ODBC有许多的相似之处,了解了ODBC后再去学习ADO会是一件很轻松的事情。
要写一个数据库系统,首先要做的是注册数据源,控制面板管理工具中有数据源。众所周知的事情就不说了。
建立一个数据库应用程序。
众所周知,在使用VC6编写MFC应用程序的时候先要建立工程。我们手头的参考书中有说明如何建立一个ODBC的数据库应用程序,不再浪费文字过多描述。书上的这个工程是一个单文档应用程序(貌似只中当文档应用程序在新建的时候可以添加数据源)。工程建立完成后在CobjNameView(objName为工程名)类中会自动定义了一个“m_pSet”成员变量,这个变量的类型是CobjNameSet(objName为工程名),其父类为CRecordset,拿这个类出来说事是因为这个类负责了应用程序和数据库相关的操作,打开这个类,我们可以看见其中定义了成员变量为public,仔细观察就会发现这几个变量的名称,类型和数据源表中的属性一一对应了。再看这个成员函数:DoFieldExchange()。和DoDataExchange()函数的功能很类似,不过一个是将变量(CobjNameSet类的几个数据成员)和数据库中某个元组的属性值关联,而另一个是将视图中的控件和某个变量项关联(是我们为控件添加变量的效果)。这应该ODBC的数据交换机制(我想不出更好的词汇来形容它,手头可查资料太少),这种实现机制的使用使得我们可以只需要考虑程序中的变量值而不需要去管数据库中的属性名是什么。
对数据库的操作:
1.打开数据库:
MFC中对CRecordset::Open()函数的定义如下:
virtual BOOL Open(
UINT nOpenType = AFX_DB_USE_DEFAULT_TYPE,
LPCTSTR lpszSQL = NULL,
DWORD dwOptions = none
);
nOpenType打开方式,包括以下方式:
CRecordset::dynaset
CRecordset::snapshot
CRecordset::dynamic
CRecordset::forwardOnly
lpszSQL,打开数据库的SQL语句。
dwOptions,操作方式,比如说是否是只读(CRecordset::readOnly)。
当然,当我们使用MFC自动创建数据库连接的CRcordSet派生类的时候,并不需要我们显式的调用这个函数。VC中创建MFC ODBC应用程序的时候貌似也没有调用这个函数,不过事实是这样的,你的数据库在程序中刚开始就处于打开状态了,具体是怎么实现的我也不是很清楚,大概和这个类里面定义的GetDefaultConnect()成员函数和GetDefaultSQL()函数有关。
2.查询:
要点:m_strFilter变量
CString m_strFilter;
这个变量的作用体现在查找的时候。变量的值取SQL的Select语句的WHERE关键字后面的部分,比如查找姓名为张三的学生:
Example:
m_pSet->m_ strFilter = _T(“sname=’张三’”);
m_pSet->Close();
m_pSet->Open();
while(m_pSet->IsEOF()){
*/
出查询结果
/
_pSet->MoveNext();
}
m_pSet->m_ strFilter = _T(“”);//这句很重要
从例子里可以猜测出,m_ strFilter被赋予打开条件的字符串值之后,数据库被关闭再打开(事实上这个变量的作用只体现在打开的时候,因为程序中的数据库在一开始就是处于打开状态,所以才要先关闭数据库再打开,如果你不确定,可以使用CRcordSet:: IsOpen()函数进行判断),得到的元组集合(不知道怎么描述比较准确)就是符合m_ strFilter
这里还用到了另外两个函数:
void Close( ); BOOL IsEOF( ) const;
功能显然易见,不多少。
另外补充几个函数:
CRcordSet:: IsBOF();是否到顶
CRcordSet:: IsOpen();是否打开
CRcordSet ::MoveFirst();移动到第一条记录
CRcordSet ::MoveLast();移动到最后一条记录
3.更新数据库:
easy了,ODBC中更新数据库是一件很轻松的事情,只要调用CRcordSet::Edit() 函数就OK了,看个简单的例子,出自MSDN:
Example:
// To edit a record, first set up the edit buffer
rsCustSet.Edit();
// Then edit field data members for the record
rsCustSet.m_BillingID = 2795;
rsCustSet.m_ContactFirstName = _T("Jones Mfg");
// Finally, complete the operation
if(!rsCustSet.Update())
{
// Handle the failure to update
AfxMessageBox(_T("Couldn't update record!"));
}
可以看见,更新数据库具有固定的“套用模式”,首先调用CRcordSet::Edit() 函数,如例子中所说,设置更新缓存,然后对对应变量的值进行修改,完成后调用CRcordSet::Update()
4.添加数据:
函数:virtual void AddNew( );
一个简单的例子,说明一切问题:
Example:
m_pSet->AddNew();
m_pSet->m_TestName = “TestName1”;
m_pSet->m_TestCode = “TestCode1”;
m_pSet->m_TestOthers = “Other Codes”;
m_pSet->Update();
如你所见,代码又一次很荣幸的被“夹在”了AddNew()和Update()之间。类似更新,首先告诉系统我要添加数据了(AddNew()),系统很无赖的给了你一块内存空间,让你可以对新的记录属性进行赋值,完了之后你又要告诉系统你已经搞定了,系统就不得不屁颠屁颠的去给你把数据写进数据库中(当然这之前会检测数据是否符合数据库的完整性约束)(Update())。添加操作到此完成。
5.删除:
这个不带劲,没什么好说的,delete谁都认识。
virtual void Delete( );
Example:
// Create a derived CRecordset object
CCustomer rsCustSet(&m_dbCust);
rsCustSet.Open();
if(rsCustSet.IsEOF() || !rsCustSet.CanUpdate() ||
!rsCustSet.CanTransact())
{
return;
}
m_dbCust.BeginTrans();
// Perhaps scroll to a new record...
// Delete the current record
rsCustSet.Delete();
// Finished commands for this transaction
if(IDYES == AfxMessageBox(_T("Commit transaction?"), MB_YESNO))
m_dbCust.CommitTrans();
else // User changed mind
m_dbCust.Rollback();
写的这么工整,肯定是出自MSDN了,这个家伙写的逻辑性很好。看见许多新鲜的东西吧,别问我,我懒得说,自己想去。额,你要看见的也就是从最后一行数起的第七行而已。
打开,关闭,更新,添加,删除,貌似我都说过了。排序么,其实你不用在数据库中完成了。
6.排序:
方法一:去程序里编写排序代码。
方法二:m_strSort。
那个已经快被遗忘的SQL语句:ORDER BY。聪明的孩子,想到了m_strFilter了没?没错,m_strSort 变量的取值就是SQL语句ORDER BY
Example:
CCustomer rsCustSet(&m_dbCust);
// Set the sort string
rsCustSet.m_strSort = _T("L_Name, ContactFirstName");
// Run the sorted query
rsCustSet.Open(CRecordset::snapshot, _T("Customer"));
出自MSDN。额外说明,如果属性名(例子中是L_Name)包含空格的话,要用方括号括起来:rsCustSet.m_strSort = _T("[L Name], ContactFirstName");
Got it?
程序中表名,数据库名亦然。
说了这么多其实都是VC帮我们建好了关联数据库的类,貌似我们在创建工程的时候只有在单文档工程中才能添加数据源,貌似也只是一个数据源,一个类。万一我们要在一个对话框中使用数据库(例如登陆对话框),万一我们想添加多个数据源,多个Crecordset 类的派生类,多个关联数据库的视图(CrecordView
CDaoDatabase class 和CdaoRecordset class
出于个人习惯,这里说“手动”连接数据库使用这两个类,当然你也可以使用CDatabase class和CRecordset class,用法差不多。
以写一个登陆对话框为例。这里假定登陆对话框上有两个输入框,变量分别为m_username,m_password。登陆确定按钮IDOK;单选按钮:IDC_RADIO1,IDC_RADIO2;分别表示普通用户和管理员,为了节省时间,下面是本人的登录对话框IDOK按钮消息映射函数,仅供参考:
首先在文件顶部包含头文件:
#include <afxdao.h>;
void CLoginDlg::OnOK()
{
UpdateData(TRUE);
CDaoDatabase cdb;
CDaoRecordset *m_pSet;
COleVariant cv;
//CString nullchar=" ";
/*调用CdaoDatabase::Open(),打开一个数据源(连接到一个数据库),并且分配相应的内存空间。第一个参数为数据源名称,第二个参数为是否是独占模式打开,第三参数为是否已只读模式打开,最后一个参数,额,缺少必要资料,不是很清楚,MSDN中也只是说A string expression used for opening the database。
*/
cdb.Open("Student_Score",FALSE,TRUE,"ODBC=;");
/*
初始化m_pSet变量,当然,这个必须要使用CDaoDatabase的对象来初始化,没什么好说的,函数调用而已。
*/
m_pSet = new CDaoRecordset(&cdb);
CString Sqlstr;
if(GetCheckedRadioButton(IDC_RADIO1,IDC_RADIO2)==IDC_RADIO1)
{
/*
如果登录用户选择的为学生
*/
/*
这老长的一串其实只是在写打开的SQL语句而而已,有一个好用的Open()函数可以用,我们就不用神经质一样的打开数据库再去查找了,easy的方法,写一个SQL的select语句,查询条件为用户名和密码,显然用户名是唯一的。用这个SQL语句做为Open()的第二个参数打开数据库。
*/
Sqlstr = "select StudentID,password from Student Where StudentID='" + m_UID + "' AND password='" + m_UPW + "'";
/*
“正式的调用CdaoRecordset::Open()”SQL语句,Open()函数在打开数据库时会自动按照这个字符串的条件筛选数据库的所有元组,并且返回符合条件的元组集合(记录集),这个字符串的功能和m_strFilter相似,最后一个参数:nOptions,懒得说,0,NULL,随便你选。
*/
m_pSet->Open(dbOpenDynaset,Sqlstr,NULL);
if(!m_pSet->IsEOF()){//用户名密码正确
/*判断依据?如果表中有符合条件的元组(用户名密码正确),Open()函数必然返回一个非空的元组集合,这时候当然不可能就到了表的结尾了*/
isAdmin = false;
m_pSet->Close();
cdb.Close();
CDialog::OnOK();
}
else
MessageBox("用户名或密码错误,\n请重新输入。","错误");
}
else{
Sqlstr = "select AdminName,Password from Admin where AdminName='" + m_UID + "' AND Password='" + m_UPW + "'";
m_pSet->Open(dbOpenDynaset,Sqlstr,NULL);
if(!m_pSet->IsEOF()){
//MessageBox("管理员登陆之后的界面");
isAdmin = true;
m_pSet->Close();
cdb.Close();
CDialog::OnOK();
}
else
MessageBox("用户名或密码错误,\n请重新输入。","错误");
}
}
插入Crecordset派生类:
VC6.0 MFC工程中:
1. 选择菜单栏InsertàNew Class…
2. Base class选择CRecordset,编辑好类名,如图所示,单击“OK”,弹出“Database Options”对话框。
3. 选择好数据源:如图。单击“OK”。弹出“Select Database Tables”对话框,选择数据库表。
4. 如图所示,选择一个数据库的表,建议一次只选择一个表,操作起来方便一些。单击“OK”。
5. 好,现在你创建的类已经添加到工程中了,可以在ClassView中看见他了。现在要说的就是如何使用它了。首先要在要包含这个类的头文件例如TestInsetClass.h;
定义类的对象:CtestInsetClass *m_pTestSet;
打开数据库:m_pTestSet->Open();
后面的操作和最前面所说的差不多了。
你可以很方便的进行查找,插入,更新,和删除。你只需要使用父类的成员变量和函数:CRecordset::m_strFilter、CRecordset::AddNew()、CRecordset::Update()、CRecordset::Delete()。
6. 用完之后记得关闭数据库。
插入CRecordView派生类
CRecordView的派生类是一个对话框。在这里我们用这个对话框作为MianWnd的一个字窗口,如图:
操作步骤:
1. 首先在ResourcesView的Dialog目录中右键,弹出快捷菜单选择Insert:
2. 在弹出的“nsert Resource”对话框中按图所示选择插入对话框为IDD_FORMVIEW,单击“New”插入对话框资源。
3. 编辑好这个对话款资源。为对话框添加类。双击对话框资源或者右键选择“Class Wizard”,确定添加类后弹出“New Class”对话框,基类(“Base Class”)选择“CRecordView”就可以了。
4. 确定后弹出如图对话框(名字N长,打字也很要命),选择前面添加的CRecordset派生类,确定。
5. 使用CRecordView派生类对话框。
首先在class View中找到CclassNameApp类,找到其成员函数“InitInstance()”,双击打开,在其中找到如下代码:
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTestDatabaseDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CTestDatabaseView));//注意这里
AddDocTemplate(pDocTemplate);
注意看代码中的倒数第二行,将其改为刚创建的CRecordView派生类对话款的类名,单击确定,编译运行程序,就可以看见窗口的视图改变了。可以使用全局变量加if语句进行判断来动态改变窗口视图。