资源描述
VC++使用ADO开发ACCESS数据库
作者:韩耀旭
本文通过实例演示如何在VC++中使用ADO进行ACCESS数据库编程,并对涉及到的几个概念进行详细解释。
本文不对ADO和ACCESS的基本概念进行详细解释,主要包括以下内容:
第一部分 ADO和ADOX到底是什么,二者的作用和区别建立数据库
第二部分 ADOX创建ACCESS数据库
第三部分 ADO创建ACCESS数据库的表
第四部分 使用_ConnectionPtr接口开发ACCESS数据库
第五部分 使用_RecordsetPtr接口开发ACCESS数据库
第一部分 ADO和ADOX到底是什么,二者的作用和区别
ADO是Microsoft 最新推出的数据库访问的高层软件接口。它和Microsoft以前的数据库访问接口DAO、RDO相比具有更大的灵活性,使用也更方便,开发效率大为提高。
ADOX是核心ADO对象的扩展库。它提供的附加对象可用于创建、修改和删除模式对象,如表和过程。要使用ADOX,则应建立对ADOX类型库的引用。ADOX库文件名为 Msadox.dll。
通俗地讲,ADO是访问数据库的一种接口,可以使用它方便地进行数据库编程。而ADOX是微软对ADO功能的扩展,比如:可以ADOX创建数据库(而ADO没有创建数据库的功能)。
第二部分 ADOX创建ACCESS数据库
用ADOX创建access数据库方法很简单,只需要创建一个Catalog对象,然后调用它的Create方法就可以了。
例程ADOXCreateDatabase演示如何使用ADOX创建一个ACCESS数据库。
打开VC++ 6.0,新建一个基于对话框的工程ADOXCreateDatabase。在对话框IDD_ADOXCREATEDATABASE_DIALOG中添加一个编辑框IDC_DBNAME和一个按钮IDC_BTN_CREATE,编辑框用以输入数据库名称。
使用ClassWizard给编辑框创建一个CString变量m_dbName。
双击IDC_BTN_CREATE按钮,并编辑OnBtnCreate()函数如下:
void CADOXCreateDatabaseDlg::OnBtnCreate()
{
//使输入到编辑框IDC_DBNAME的内容更新到m_dbName变量中
UpdateData(TRUE);
CString str;
str="d:\\"+m_dbName+".mdb";
//检查该数据库是否已经存在,如果该数据库已经存在,弹出消息框,返回
//使用API函数PathFileExists()检查路径文件是否存在
//请注意:为了使用API函数PathFileExists(),需要加入
//#include "Shlwapi.h"
//#pragma comment(lib,"shlwapi.lib")
if(PathFileExists(str))
{
CString strTemp;
strTemp.Format("%s已存在!",str);
AfxMessageBox(strTemp);
return ;
}
//定义ADOX对象指针并初始化为NULL
//用ADOX创建access数据库方法很简单,
//只需要新建一个Catalog对象,然后调用它的Create方法就可以了。
//Catalog是 ADOX 的一个对象,它包含描述数据源模式目录的集合。
//在这里,您只需知道创建数据库时使用这个对象就可以了。
//注意用try...catch组合捕捉错误
_CatalogPtr m_pCatalog = NULL;
CString DBName="Provider=Microsoft.JET.OLEDB.4.0;Data source=";
DBName=DBName+str;
try
{
m_pCatalog.CreateInstance(__uuidof(Catalog));
m_pCatalog->Create(_bstr_t((LPCTSTR)DBName));
}
catch(_com_error &e)
{
AfxMessageBox(e.ErrorMessage());
return ;
}
}
使用ADOX,需要引入ADOX的动态链接库msadox.dll,即在stdafx.h中加入如下语句:
#import "C:\Program Files\Common Files\system\ado\msadox.dll" no_namespace rename("EOF","adoEOF")
另外,ADOX属于COM对象,所以要在CADOXCreateDatabaseApp::InitInstance()函数中加入:
if(!AfxOleInit())
{
AfxMessageBox("OLE初始化出错!");
return FALSE;
}
初始化COM。
好了,编译并运行该例程,对于编译过程中弹出的4146号警告不要理会。在编辑框中输入一个数据库名称,点击“创建数据库”按钮,该数据库将在d盘根目录下创建,再次输入该数据库名称并点击“创建数据库”按钮,将弹出警告对话框。
在vc中使用ADO的时候会得到4146号警告信息,我们可以不去理会,也可以通过#pragma warning指令解决,方法为:
将在stdafx.h中加入的语句:
#import "C:\Program Files\Common Files\system\ado\msadox.dll" no_namespace rename("EOF","adoEOF")
前后再加一条语句,修改后为:
#pragma warning (disable:4146)
#import "C:\Program Files\Common Files\system\ado\msadox.dll" no_namespace rename("EOF","adoEOF")
#pragma warning (default:4146)
指令#pragma warning (disable:4146) 暂时屏蔽编译时4146警告信息
指令#pragma warning (default:4146) 重置编译器的4146警告到默认状态
第三部分 ADO创建ACCESS数据库的表
我们一般用ADOX创建数据库,然后再用ADO创建数据库的表。
例程CREATE_DB_AND_TABLE演示如何使用ADO创建ACCESS数据库的表。
打开VC++ 6.0,新建一个基于对话框的工程CREATE_DB_AND_TABLE。在对话框IDD_CREATE_DB_AND_TABLE_DIALOG中添加如下控件:
控件名称
ID
用途
编辑框
IDC_DBNAME
输入数据库名称
按钮
IDC_BTN_CREATE
创建数据库
编辑框
IDC_TABLENAME
输入表名
按钮
IDC_BTN_CREATE_TABLE
创建表
使用ClassWizard给两个编辑框创建CString变量:
编辑框
CString变量
编辑框IDC_DBNAME
m_dbName
编辑框IDC_TABLENAME
m_tableName
双击IDC_BTN_CREATE按钮,并编辑OnBtnCreate()函数如下:
void CADOXCreateDatabaseDlg::OnBtnCreate()
{
UpdateData(TRUE);
CString str;
str="d:\\"+m_dbName+".mdb";
if(PathFileExists(str))
{
CString strTemp;
strTemp.Format("%s已存在!",str);
AfxMessageBox(strTemp);
return ;
}
_CatalogPtr m_pCatalog = NULL;
CString DBName="Provider=Microsoft.JET.OLEDB.4.0;Data source=";
DBName=DBName+str;
try
{
m_pCatalog.CreateInstance(__uuidof(Catalog));
m_pCatalog->Create(_bstr_t((LPCTSTR)DBName));
}
catch(_com_error &e)
{
AfxMessageBox(e.ErrorMessage());
return ;
}
}
以上代码例程ADOXCreateDatabase中已经详细叙述。
双击IDC_BTN_CREATE_TABLE按钮,并编辑OnBtnCreateTable()函数如下:
void CCREATE_DB_AND_TABLEDlg::OnBtnCreateTable()
{
//先判断表名编辑框是否为空
UpdateData(TRUE);
if(!m_tableName.IsEmpty())
{
ADOX::_CatalogPtr m_pCatalog=NULL;
ADOX::_TablePtr m_pTable=NULL;
CString str;
str="c:\\"+m_dbName+".mdb";
CString DBName="Provider=Microsoft.JET.OLEDB.4.0;Data source=";
DBName=DBName+str;
//这段代码先检查表是否已经存在,如果表已经存在,不再创建,直接返回。
//其实这段代码不必深入研究,只需知道它的功能,直接拿来使用即可
try
{
m_pCatalog.CreateInstance(__uuidof(ADOX::Catalog));
m_pCatalog->PutActiveConnection(_bstr_t(DBName));
int tableCount=m_pCatalog->Tables->Count;
int i=0;
while(i<tableCount)
{
m_pTable=(ADOX::_TablePtr)m_pCatalog->Tables->GetItem((long)i);
CString tableName=(BSTR)m_pTable->Name;
if(tableName==m_tableName)
{
AfxMessageBox("该表已经存在!");
return;
}
i++;
}
}
catch(_com_error &e)
{
AfxMessageBox(e.Description());
return;
}
ADODB::_ConnectionPtr m_pConnection;
//创建表
_variant_t RecordsAffected;
try
{
m_pConnection.CreateInstance(__uuidof(ADODB::Connection));
//Open方法的原型:
//Open(_bstr_t ConnectionString,_bstr_t UserID,_bstr_t Password,long Options)
//ConnectionString为连接字串,UserID是用户名,Password是登陆密码
//Options是连接选项,可以是如下几个常量:
//adModeUnknown 缺省,当前的许可权未设置
//adModeRead 只读
//adModeWrite 只写
//adModeReadWrite 可以读写
//adModeShareDenyRead 阻止其它Connection对象以读权限打开连接
//adModeShareDenyWrite 阻止其它Connection对象以写权限打开连接
//adModeShareExclusive 阻止其它Connection对象打开连接
//adModeShareDenyNone 阻止其它程序或对象以任何权限建立连接
m_pConnection->Open(_bstr_t(DBName),"","",ADODB::adModeUnknown);
}
catch(_com_error e)
{
CString errormessage;
errormessage.Format("连接数据库失败!\r错误信息:%s",e.ErrorMessage());
AfxMessageBox(errormessage);
return;
}
try
{
CString strCommand;
/*
执行SQL命令:CREATE TABLE创建表格
该表包含三个字段:记录编号 INTEGER,姓名 TEXT,出生年月 DATETIME
SQL语言中的create table语句被用来建立新的数据库表格。
create table语句的使用格式如下:
create tablename (column1 data type,column2 data type,column3 data type);
如果用户希望在建立新表格时规定列的限制条件,可以使用可选的条件选项
create table tablename
(column1 data type[constraint],
column2 data type[constraint],
column3 data type[constraint]);
举例:
create table employee
(firstname varchar(15),
lastname varchar(20),
age number(3),
address varchar(30),
city varchar(20));
简单来说,创建新表格时,在关键词create table后面加入所要建立的表格的名称,
然后在括号内顺次设定各列的名称,数据类型,以及可选的限定条件等。
使用SQL语句创建的数据库表格和表格中列的名称必须以字母开头,
后面可以使用字母,数字或下划线,名称的长度不能超过30个字符,
注意,用户在选择表格名称时不要使用SQL语言中的保留关键字,
如select,create,insert等,作为表格或列的名称
*/
strCommand.Format("CREATE TABLE %s(记录编号 INTEGER,姓名 TEXT,出生年月 DATETIME)",m_tableName);
//Execute(_bstr_t CommandText,VARIANT* RecordsAffected,long Options)
//其中CommandText是命令字串,通常是SQL命令,
//参数RecordsAffected是操作完成后所影响的行数
//参数Options表示CommandText中内容的类型,可以取下列值之一:
//adCmdText 表明CommandText是文本命令
//adCmdTable 表明CommandText是一个表名
//adCmdProc 表明CommandText是一个存储过程
//adCmdUnknown 未知
m_pConnection->Execute(_bstr_t(strCommand),&RecordsAffected,ADODB::adCmdText);
if(m_pConnection->State)
m_pConnection->Close();
}
catch(_com_error &e)
{
AfxMessageBox(e.Description());
}
}
}
这段代码先用ADOX的Catalog对象检查表是否已经存在,如果该表已经存在,直接返回;如果还没有该表,使用ADO的Connection对象的Execute函数创建表。
Connection对象的用法:首先定义一个Connection类型的指针,然后调用CreateInstance()来创建一个连接对象的实例,再调用Open函数建立与数据源的连接。最后使用Execute()函数执行SQL语句创建表。
关于调用CreateInstance()来创建连接对象的实例,还需作一点说明。ADO库包含三个基本接口:_ConnectionPtr接口,_RecordsetPtr接口和_CommandPtr接口。其分别对应Connection对象(完成应用程序对数据源的访问连接),Recordset对象(将查询的结果以记录集的方式存储)和Command对象(对已连接的数据源进行命令操作)。
_ConnectionPtr m_pConnection;
_RecordsetPtr m_pRecordset;
_CommandPtr m_pCommand;
而这三个对象实例的创建,可以使用如下语句:
m_pConnection.CreateInstance(__uuidof(Connection));
或者:
m_pConnection.CreateInstance(“ADODB.Connection”);
m_pRecordset.CreateInstance(__uuidof(Recordset));
或者:
m_pRecordset.CreateInstance(“ADODB.Recordset”);
m_pCommand.CreateInstance(__uuidof(Command));
或者:
m_pCommand.CreateInstance(“ADODB.Command”);
两种方法的作用完全相同,使用哪种方法,完全是您的个人爱好问题。
如例程ADOXCreateDatabase,在BOOL CCREATE_DB_AND_TABLEApp::InitInstance()函数中加入:
if(!AfxOleInit())
{
AfxMessageBox("OLE初始化出错!");
return FALSE;
}
在stdafx.h中加入如下语句:
#import "C:\Program Files\Common Files\system\ado\msadox.dll"
#import "C:\Program Files\Common Files\system\ado\msado15.dll" rename("EOF","adoEOF")
关于这两条语句,需要进行特别说明:
由于该例程同时使用ADOX和ADO,需要同时引入msado15.dll和msadox.dll两个库。这两个库的名字空间是不同的,msado15.dll的名字空间是ADODB,msadox.dll的名字空间是ADOX。在使用ADO所属的名字空间的变量,函数时,在前面加上ADODB::,在使用ADOX所属的名字空间的变量,函数时,在前面加上ADOX::。
另外,一般ADOX和ADO分开操作。您也可以在ADOX操作部分使用using namespace ADOX::,而在ADO操作部分使用using namespace ADO:,以区分名字空间。这样,您就不必再使用ADOX::和ADODB::了。
rename(“EOF”,”adoEOF”) //重命名EOF是必要的,因为典型的VC应用都已经定义了EOF作为常数-1,为了避免冲突,将ADO中的EOF重命名为adoEOF。
#import中有一个属性为no_namespace,这是告诉编译器该类不在一个单独的名字空间中,使用no_namespace意味着你不需要在初始化变量的时候引用名字空间。当然如果在您的应用中需要导入多个类型库的话,不要使用no_namespace,以免引起名字冲突。
再通俗一点讲,就是只导入一个类型库的话,可以在#import语句中加入no_namespace属性,您的程序可以直接使用这个类型库的名字空间的内容,而不必使用using namespace XXX;或XXX::,这是因为no_namespace属性告诉编译器该类型库不再名字空间,而是在全局空间上工作;如果您导入几个类型库,而这几个类型库之间没有定义冲突,您也可以在使用no_namespace属性;但如果两个类型库中有定义冲突,就不能使用no_namespace属性,如果使用no_namespace属性,就会在全局空间产生定义冲突。
对于本例程,您可以把stdafx.h中的
#import "C:\Program Files\Common Files\system\ado\msadox.dll"
#import "C:\Program Files\Common Files\system\ado\msado15.dll" rename("EOF","adoEOF")
改为
#import "C:\Program Files\Common Files\system\ado\msadox.dll"
#import "C:\Program Files\Common Files\system\ado\msado15.dll" no_namespace rename("EOF","adoEOF")
这样改动后,void CCREATE_DB_AND_TABLEDlg::OnBtnCreateTable()中的ADODB::需要完全省略掉。
当然,您也可以把这两行改为:
#import "C:\Program Files\Common Files\system\ado\msadox.dll" no_namespace
#import "C:\Program Files\Common Files\system\ado\msado15.dll" rename("EOF","adoEOF")
但这样改动后,void CCREATE_DB_AND_TABLEDlg::OnBtnCreateTable()中的ADOX::需要完全省略掉。
由于ADOX和ADO有定义冲突,也就是说,msado15.dll和msadox.dll有相同的定义部分,所以在一个程序中,不允许同时使用no_namespace。
第四部分 使用_ConnectionPtr接口开发ACCESS数据库
ADO中最重要的对象有三个:Connection、Recordset和Command,分别表示连接对象、记录集对象和命令对象。三个对象对应的智能指针分别是:_ConnectionPtr、_RecordsetPtr、_CommandPtr。ADO使用_ConnectionPtr这个指针来操纵Connection对象,类似地,后面用到的_CommandPtr和_RecordsetPtr分别表示命令对象指针和记录集对象指针。
Connection对象是这三个对象的基础,它的主要作用是建立与数据库的连接,建立了与数据库的连接后,才能进行其它有关数据库的访问和操作。
也就是说,使用ADO操作数据库,通常先用Connection对象的Open方法打开一个库连接,然后才能进行数据库的操作。操作完成后,要关闭这个库连接。
本文只讲述Connection对象最常用的Open方法和Execute方法。Open方法用于打开一个库连接,而Execute方法一般用于执行一条SQL语句。
_ConnectionPtr智能指针的用法:
首先定义一个Connection类型的指针,然后调用CreateInstance()来创建一个连接对象的实例,再调用Open函数建立与数据源的连接。在建立连接对象后,可以使用连接对象的Execute()函数来执行SQL命令。
_ConnectionPtr智能指针Open方法的原型:
Open(_bstr_t ConnectionString,_bstr_t UserID,_bstr_t Password,long Options)
ConnectionString为连接字串,UserID是用户名,Password是登陆密码
Options是连接选项,可以是如下几个常量:
adModeUnknown 缺省,当前的许可权未设置
adModeRead 只读
adModeWrite 只写
adModeReadWrite 可以读写
adModeShareDenyRead 阻止其它Connection对象以读权限打开连接
adModeShareDenyWrite 阻止其它Connection对象以写权限打开连接
adModeShareExclusive 阻止其它Connection对象打开连接
adModeShareDenyNone 阻止其它程序或对象以任何权限建立连接
_ConnectionPtr智能指针Execute方法的原型:
_RecordsetPtr Connection15::Execute(_bstr_t CommandText,VARIANT* RecordsAffected,long Options)
其中CommandText是命令字串,通常是SQL命令,
参数RecordsAffected是操作完成后所影响的行数
参数Options表示CommandText中内容的类型,可以取下列值之一:
adCmdText 表明CommandText是文本命令
adCmdTable 表明CommandText是一个表名
adCmdProc 表明CommandText是一个存储过程
adCmdUnknown 未知
Execute执行完后返回一个指向记录集的指针。
例程CREATE_DB_AND_TABLE中已经使用了_ConnectionPtr指针的Open方法和Execute方法,在后面的例程我们将进一步详细说明。
我们先讲解几条最常用的SQL语句。
SELECT查询语句
我们希望用各种不同的方法来查看和分析数据,SELECT语句就是我们要使用的语句,用于有选择的从数据库返回我们需要的数据,也就是查询。
最基本的SELECT语句仅有两个部分:要返回的列和这些列源于的表
为了便于讲解演示,我们使用如下Northwind 示例数据库中的 Employees 表
EmployeeID
FirstName
LastName
HireDate
City
Country
1
Nancy
Davolio
1/5/1992 12:00:00
Seattle
USA
2
Andrew
Fuller
14/8/1992 12:00:00
Tacoma
USA
3
Janet
Leverling
1/4/1992 12:00:00
Kirkland
USA
4
Margaret
Peacock
3/5/1993 12:00:00
Redmond
USA
5
Steven
Buchanan
17/10/1993 12:00:00
London
UK
6
Michael
Suyama
17/10/1993 12:00:00
London
UK
7
Robert
King
2/1/1994 12:00:00
London
UK
8
Laura
Callahan
5/3/1994 12:00:00
Seattle
USA
9
Anne
Dodsworth
15/11/1994 12:00:00
London
UK
如果我们希望检索Employees表中所有客户的所有信息,我们可以使用星号(*)来简单地表示所有列,查询语句如下所示:
SELECT * FROM Employees
如果我们只需要特定列,我们应该在逗号分隔的列表中显式指定这些列,如下所示:
SELECT EmployeeID, FirstName,LastName,HireDate FROM Employees
结果会显示该表中所有行的指定字段的数据。
显式指定所需字段还允许我们控制字段返回的顺序,如果我们希望LastName显示在FirstName之前,我们可以编写以下语句:
SELECT EmployeeID, LastName,FirstName,HireDate FROM Employees
WHERE子句
接下来我们要做的是开始限制或筛选从数据库提取的数据。通过向SELECT语句添加WHERE子句,我们可以添加一个(或多个)条件,所选数据必须满足这些条件,这将限制答复查询的行数也就是被提取的行数。
我们可以在上一个查询的基础上,将其限制为City为London的员工
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE City = 'London'
查询结果如下:
EmployeeID
FirstName
LastName
HireDate
City
5
Steven
Buchanan
17/10/1993 12:00:00
London
6
Michael
Suyama
17/10/1993 12:00:00
London
7
Robert
King
2/1/1994 12:00:00
London
9
Anne
Dodsworth
15/11/1994 12:00:00
London
如果您希望返回相反条件的员工,即返回那些不住在伦敦的员工,您应该编写以下语句:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE City <> 'London'
您也可以使用大于、小于、大于等于、小于等于等运算符。例如,若要获取其雇佣日期等于某个给定日期或大于该日期的员工列表,您可以编写以下语句:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE HireDate >= '1-july-1993'
您可以得到以下结果行:
EmployeeID
FirstName
LastName
HireDate
City
5
Steven
Buchanan
17/10/1993 12:00:00
London
6
Michael
Suyama
17/10/1993 12:00:00
London
7
Robert
King
2/1/1994 12:00:00
London
8
Laura
Callahan
5/3/1994 12:00:00
Seattle
9
Anne
Dodsworth
15/11/1994 12:00:00
London
当然,我们可以编写更复杂的条件:在 WHERE 子句中加入多个条件。如果我们希望了解哪些员工是在两个给定日期之间雇佣的,我们可以编写以下语句:
SELECT EmployeeID, FirstName, LastName, HireDate, City
FROM Employees
WHERE (HireDate >= '1-june-1992') AND (HireDate <= '15-december-1993')
结果如下:
EmployeeID
FirstName
LastName
HireDate
City
2
Andrew
Fuller
14/8/1992 12:00:00
Tacoma
4
Margaret
Peacock
3/5/1993 12:00:00
Redmond
5
Steven
Buchanan
17/10/1993 12:00:00
London
6
Michael
Suyama
17/10/1993 12:00:00
London
SQL 还有一个BETWEEN 运算符,用于检查某个值是否在两个值之间(包括等于两端的值)。这使我们可以将以前的查询重新编写为:
SELECT EmployeeID, FirstName, LastName, HireDate, City
FROM Employees
WHERE HireDate BETWEEN '1-june-1992' AND '15-december-1993'
我们也可以使用 NOT 运算符来提取那些不在指定日期之间的行:
SELECT EmployeeID, FirstName, LastName, HireDate, City
FROM Employees
WHERE HireDate NOT BETWEEN '1-june-1992' AND '15-december-1993'
如果我们希望检查一个列值是否等于多个值,那该怎么办呢?如果只是两个值,则很容易对每个值进行测试,方法是,使用 OR 运算符将它们连接在一起,则编写的语句如下所示:
SELECT EmployeeID, FirstName, LastName, HireDate, City FROM Employees
WHERE City = 'London' OR City = 'Seattle'
但是,如果您希望与三个、四
展开阅读全文