源码Demo下载

ADO数据库操作综合起来就是增删改查,而且这几个流程都是固定的。

查询:连接数据库-->打开记录集-->遍历记录集并获取记录内容

插入:连接数据库-->打开记录集-->插入数据库记录-->将记录保存到数据库

更新:连接数据库-->打开记录集-->遍历记录集并更新记录-->将记录保存到数据库

删除:连接数据库-->执行删除的SQL语句


将表的一行数据内容封装成一个数据记录类,该数据记录类提供三个接口:

AdoGetPrimaryKey:一个获取数据记录类主键值接口,主要是为了数据记录做定位,然后做相应的操作

AdoGetRecord:一个获取当前记录集当前行内容的接口,查询遍历记录集的时候调用这个接口即可把记录集当前行内容存放数据记录类对象中

AdoSetRecord:一个将数据记录类对象更新到记录集当前行,插入的时候将数据记录类对象更新到记录集当前行即可,更新的时候通过调用AdoGetPrimaryKey接口获取对应的数据记录类对象,将数据记录类对象更新到记录集当前行


需要做到快速,还需要一个小工具ADO助手来帮忙根据SQL内容生成数据记录类,具体操作及使用请参考《如何使用ADO助手生成数据库代码》。

具体的使用步骤:

1.将本例中的数据库封装类拷贝到你MFC工程目录下,并用include包含到预处理头文件stdafx.h中


#include "Ado/Ado.h"


2.将用小工具生成的数据库类也添加到工程,本例中生成的数据库类是CDBDemo


-- SQL SERVER 2005建表SQL语句
CREATE TABLE [dbo].[tbl_Demo](
	[ID] [int] IDENTITY(1,1) NOT NULL,
	[Guid] [nvarchar](80) NOT NULL,
	[Name] [nvarchar](20) NULL,
	[Age] [int] NULL,
	[Photo] [image] NULL,
 CONSTRAINT [PK_tbl_Demo] PRIMARY KEY CLUSTERED 
(
	[Guid] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]


用ADO助手生成的MFC数据库代码如下,支持二进制字段。

生成的数据库类还需要增加一个成员函数CString AdoGetPrimaryKey() const,该函数返回表主键值,最终数据记录类如下:


/*----------------------------------------------------------------------------------------------------------------
Version:	AdoAssist 1.20
   Time:	2012-08-16 09:37:11
    SQL:	SELECT * FROM tbl_Demo
----------------------------------------------------------------------------------------------------------------*/
#pragma once

class CDBDemo
{
public:
	long			m_lID;	//0	ID	adInteger
	CString			m_strGuid;	//1	Guid	adVarWChar
	CString			m_strName;	//2	Name	adVarWChar
	long			m_lAge;	//3	Age	adInteger
	//CAdoByteArray			m_brPhoto;	//4	Photo	adLongVarBinary
	CAdoBinaryRes			m_brPhoto;	//4	Photo	adLongVarBinary

public:
	long GetID() const
	{
		return this->m_lID;
	}

	void GetID(long& tID) const
	{
		tID = this->m_lID;
	}

	void SetID(const long& tID)
	{
		this->m_lID = tID;
	}

	CString GetGuid() const
	{
		return this->m_strGuid;
	}

	void GetGuid(CString& tGuid) const
	{
		tGuid = this->m_strGuid;
	}

	void SetGuid(const CString& tGuid)
	{
		this->m_strGuid = tGuid;
	}

	CString GetName() const
	{
		return this->m_strName;
	}

	void GetName(CString& tName) const
	{
		tName = this->m_strName;
	}

	void SetName(const CString& tName)
	{
		this->m_strName = tName;
	}

	long GetAge() const
	{
		return this->m_lAge;
	}

	void GetAge(long& tAge) const
	{
		tAge = this->m_lAge;
	}

	void SetAge(const long& tAge)
	{
		this->m_lAge = tAge;
	}

	/*CAdoBinaryRes GetPhoto() const
	{
		return this->m_brPhoto;
	}

	void GetPhoto(CAdoBinaryRes& tPhoto) const
	{
		tPhoto = this->m_brPhoto;
	}

	void SetPhoto(const CAdoBinaryRes& tPhoto)
	{
		this->m_brPhoto = tPhoto;
	}
*/

public:
	// Database Interface
	CString AdoGetPrimaryKey() const
	{
		return GetGuid();
	}
	bool AdoGetRecord(const CAdoRecordset& rs, DWORD dwFlag = 0);
	bool AdoSetRecord(const CAdoRecordset& rs, DWORD dwFlag = 0) const;
};
#pragma warning( push )
#pragma warning(disable:4100)
// Database Interface
inline bool CDBDemo::AdoGetRecord(const CAdoRecordset& rs, DWORD dwFlag)
{
	ADO_GETFIELDVALUE(_T("ID"), m_lID);	//0
	ADO_GETFIELDVALUE(_T("Guid"), m_strGuid);	//1
	ADO_GETFIELDVALUE(_T("Name"), m_strName);	//2
	ADO_GETFIELDVALUE(_T("Age"), m_lAge);	//3
	ADO_GETFIELDVALUE(_T("Photo"), m_brPhoto);	//4

	/*
	ADO_GETFIELDVALUE((long)0, m_lID);	//ID
	ADO_GETFIELDVALUE((long)1, m_strGuid);	//Guid
	ADO_GETFIELDVALUE((long)2, m_strName);	//Name
	ADO_GETFIELDVALUE((long)3, m_lAge);	//Age
	ADO_GETFIELDVALUE((long)4, m_brPhoto);	//Photo
	*/

	return true;
}

// Database Interface
inline bool CDBDemo::AdoSetRecord(const CAdoRecordset& rs, DWORD dwFlag) const
{
	//ADO_SETFIELDVALUE(_T("ID"), m_lID);	//0
	ADO_SETFIELDVALUE(_T("Guid"), m_strGuid);	//1
	ADO_SETFIELDVALUE(_T("Name"), m_strName);	//2
	ADO_SETFIELDVALUE(_T("Age"), m_lAge);	//3
	ADO_SETFIELDVALUE(_T("Photo"), m_brPhoto);	//4

	/*
	//ADO_SETFIELDVALUE((long)0, m_lID);	//ID
	ADO_SETFIELDVALUE((long)1, m_strGuid);	//Guid
	ADO_SETFIELDVALUE((long)2, m_strName);	//Name
	ADO_SETFIELDVALUE((long)3, m_lAge);	//Age
	ADO_SETFIELDVALUE((long)4, m_brPhoto);	//Photo
	*/

	return true;
}

#pragma warning( pop )



3.添加一个业务数据库类CDBBusiness,该类从CAdoDatabase派生,并把CDBDemo类的头文件用include包含到业务数据库类CDBBusiness的头文件中


4.在业务数据库类CDBBusiness中编写连接数据库代码


BOOL CDBBusiness::Connect(void)
{
	CString strAccessFile;
	GetModuleFileName(NULL, strAccessFile.GetBuffer(MAX_PATH), MAX_PATH);
	strAccessFile.ReleaseBuffer();
	// 获取程序的运行目录
	strAccessFile = strAccessFile.Left(strAccessFile.ReverseFind('\\')+1);
	
	CString strConnction;

	strAccessFile += _T("Demo.mdb");
	strConnction.Format(_T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=%s;Persist Security Info=False"),
		strAccessFile);

	return Open(strConnction);
}



5.数据库增删查改操作

需要用到两个宏,一个声明宏ADO_DECLARE,一个实现宏ADO_IMPLEMENT


/*
<summary>
声明数据类与数据库的交互
</summary>
<param name="_FunPrefix">添加的成员函数名修饰前缀</param>
<param name="_FunSuffix">添加的成员函数名修饰后缀</param>
<param name="_DbClass">数据类</param>
*/
#define ADO_DECLARE(_FunPrefix, _FunSuffix, _DbClass)																												\
	typedef std::vector<_DbClass> Vector_##_DbClass;																												\
	typedef std::vector<_DbClass>::iterator _DbClass##_Iterator;																									\
	typedef const Vector_##_DbClass Const_Vector_##_DbClass;																										\
	typedef Vector_##_DbClass::const_iterator Const_##_DbClass##_Iterator;																							\
	bool _FunPrefix##Select##_FunSuffix(Vector_##_DbClass& rData, LPCTSTR lpszCondition= _T(""), LPCTSTR lpszTop = _T(""), DWORD dwFlag = 0) const;					\
	bool _FunPrefix##Update##_FunSuffix(const _DbClass& rData, DWORD dwFlag = 0) const;																				\
	bool _FunPrefix##Save##_FunSuffix(const _DbClass& rData, DWORD dwFlag = 0) const;																				\
	bool _FunPrefix##Insert##_FunSuffix(const _DbClass& rData, DWORD dwFlag = 0) const;																				\
	bool _FunPrefix##Delete##_FunSuffix(const _DbClass& rData) const;																								\
	bool _FunPrefix##Count##_FunSuffix(const _DbClass& rData, long &lCount) const;																					\
	bool _FunPrefix##Count##_DbClass##_FunSuffix(long &lCount, LPCTSTR lpszWhere= _T("")) const;																	\
	bool _FunPrefix##Delete##_DbClass##_FunSuffix(LPCTSTR lpszWhere= _T("")) const;																					\
	bool _FunPrefix##SelectOrder##_FunSuffix(Vector_##_DbClass& rData, LPCTSTR lpszWhere= _T(""), DWORD dwFlag = 0) const;											\
	bool _FunPrefix##Update##_FunSuffix(Const_Vector_##_DbClass& rData, DWORD dwFlag = 0) const;																	\
	bool _FunPrefix##UpdateBatch##_FunSuffix(Const_Vector_##_DbClass& rData, DWORD dwFlag = 0) const;																\
	bool _FunPrefix##Insert##_FunSuffix(Const_Vector_##_DbClass& rData, DWORD dwFlag = 0) const;																	\
	bool _FunPrefix##InsertBatch##_FunSuffix(Const_Vector_##_DbClass& rData, DWORD dwFlag = 0) const;																\
	bool _FunPrefix##Count##_FunSuffix(Const_Vector_##_DbClass& rData, long &lCount) const;																			\
	bool _FunPrefix##Delete##_FunSuffix(Const_Vector_##_DbClass& rData) const;																						\
	bool _FunPrefix##Select##_FunSuffix(_DbClass& rData, DWORD dwFlag = 0) const;																					\
	bool _FunPrefix##Save##_FunSuffix(Const_Vector_##_DbClass& rData, DWORD dwFlag = 0) const;																		\
	bool _FunPrefix##SaveBatch##_FunSuffix(Const_Vector_##_DbClass& rData, DWORD dwFlag = 0) const;


/*
<summary>
实现数据类与数据库的交互
</summary>
<param name="_FunPrefix">添加的成员函数名修饰前缀</param>
<param name="_FunSuffix">添加的成员函数名修饰后缀</param>
<param name="_ClasName">业务数据库类,必须从CAdoDatabase派生</param>
<param name="_DbClass">数据类</param>
<param name="_TableName">数据表名称</param>
<param name="_PrimeKey">数据表名主键名称</param>
<param name="_FieldList">数据库字段,偷懒的话直接用“"*"”</param>
*/
#define ADO_IMPLEMENT(_FunPrefix, _FunSuffix, _ClasName, _DbClass, _TableName, _PrimeKey, _FieldList)


本例子如下添加声明和实现

ADO_DECLARE(_, _, CDBDemo);



ADO_IMPLEMENT(_, _, CDBBusiness, CDBDemo, "tbl_Demo", "Guid", "Id, Guid, Name, Age, Photo");



本例中上面两个宏编译后实现了如下函数

A.查询


/*
	<summary>
	通过主键定位获取单个记录
	</summary>
	<param name="rData">记录的类对象</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _Select_(CDBDemo& rData, DWORD dwFlag = 0) const;
	
	/*
	<summary>
	获取多个记录
	注:请调用Count(*)计算个数或者直接定量,调用std::vector<T>::reserve预留内存空间,否则可能有性能影响
	</summary>
	<param name="rData">记录的类向量对象</param>
	<param name="lpszCondition">SQL语句的WHERE子句,ORDER子句或者LIMIT子句任意组合,为空则查询整个表</param>
	<param name="lpszTop">SQL语句的TOP子句</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _Select_(std::vector<CDBDemo>& rData, LPCTSTR lpszCondition= _T(""), 
		LPCTSTR lpszTop = _T(""), DWORD dwFlag = 0) const;
		
	/*
	<summary>
	获取表数据,数据按主键升序排列
	注:请调用Count(*)计算个数或者直接定量,调用std::vector<T>::reserve预留内存空间,否则可能有性能影响
	</summary>
	<param name="rData">记录的类向量对象</param>
	<param name="lpszWhere">SQL语句的WHERE子句</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _SelectOrder_(std::vector<CDBDemo>& rData, LPCTSTR lpszWhere= _T(""), DWORD dwFlag = 0) const;

B.计数

/*
	<summary>
	计算当前主键在表中个数,为0或者1
	</summary>
	<param name="rData">记录的类对象</param>
	<param name="lCount">返回计算的个数,为0或者1</param>
	<returns>是否执行成功</returns>
	*/
	bool _Count_(const CDBDemo& rData, long &lCount) const;
	
		
	/*
	<summary>
	计算类向量对象在数据库中的个数
	</summary>
	<param name="rData">记录的类向量对象</param>
	<param name="lCount">计算类向量对象在数据库中的个数</param>
	<returns>是否执行成功</returns>
	*/
	bool _Count_(const std::vector<CDBDemo>& rData, long &lCount) const;

	
	/*
	<summary>
	计算表中数据个数
	</summary>
	<param name="lCount">返回计算的个数</param>
	<param name="lpszWhere">SQL语句的WHERE子句</param>
	<returns>是否执行成功</returns>
	*/
	bool _CountCDBDemo_(long &lCount, LPCTSTR lpszWhere= _T("")) const;

C.保存(有则更新无则插入)


/*
	<summary>
	通过主键定位记录,没有则插入,有则更新
	</summary>
	<param name="rData">记录的类对象</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _Save_(const CDBDemo& rData, DWORD dwFlag = 0) const;
	
	/*
	<summary>
	用Update进行插入或者更新多个记录
	</summary>
	<param name="rData">记录的类向量对象</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _Save_(const std::vector<CDBDemo>& rData, DWORD dwFlag = 0) const;
	
	/*
	<summary>
	用UpdateBatch进行批量插入或者更新多个记录
	</summary>
	<param name="rData">记录的类向量对象</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _SaveBatch_(const std::vector<CDBDemo>& rData, DWORD dwFlag = 0) const;

D.插入



/*
	<summary>
	用Update进行插入单个记录
	</summary>
	<param name="rData">记录的类对象</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _Insert_(const CDBDemo& rData, DWORD dwFlag = 0) const;
	
	/*
	<summary>
	用Update进行插入多个记录
	</summary>
	<param name="rData">记录的类向量对象</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _Insert_(const std::vector<CDBDemo>& rData, DWORD dwFlag = 0) const;
	
	/*
	<summary>
	用UpdateBatch进行批量插入多个记录
	</summary>
	<param name="rData">记录的类向量对象</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _InsertBatch_(const std::vector<CDBDemo>& rData, DWORD dwFlag = 0) const;

E.更新


/*
	<summary>
	用Update进行更新单个记录
	</summary>
	<param name="rData">记录的类对象</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _Update_(const CDBDemo& rData, DWORD dwFlag = 0) const;
	
	/*
	<summary>
	用Update进行更新多个记录,确保记录集与类向量对象个数及顺序一致性,否则会发生更新错乱
	</summary>
	<param name="rData">记录的类向量对象</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _Update_(const std::vector<CDBDemo>& rData, DWORD dwFlag = 0) const;
	
	/*
	<summary>
	用UpdateBatch进行批量更新多个记录,确保记录集与类向量对象个数及顺序一致性,否则会发生更新错乱
	</summary>
	<param name="rData">记录的类向量对象</param>
	<param name="dwFlag">用来控制类对象参与操作的字段</param>
	<returns>是否执行成功</returns>
	*/
	bool _UpdateBatch_(const std::vector<CDBDemo>& rData, DWORD dwFlag = 0) const;

F.删除



/*
	<summary>
	通过主键定位,删除单个记录
	</summary>
	<param name="rData">记录的类对象</param>
	<returns>是否执行成功</returns>
	*/
	bool _Delete_(const CDBDemo& rData) const;
	
	/*
	<summary>
	删除多个记录
	</summary>
	<param name="rData">记录的类向量对象</param>
	<returns>是否执行成功</returns>
	*/
	bool _Delete_(const std::vector<CDBDemo>& rData) const;
	
	/*
	<summary>
	删除表记录
	</summary>
	<param name="lpszWhere">SQL语句的WHERE子句</param>
	<returns>是否执行成功</returns>
	*/
	bool _DeleteCDBDemo_(LPCTSTR lpszWhere= _T("")) const;


以后每增加一个表只需要按上面的格式用ADO_DECLARE和ADO_IMPLEMENT增加相应的数据库增删查改操作。

增加成员函数名修饰前缀和成员函数名修饰后缀作用是可以使得一个数据记录类可以对应多个表。



6.在本例子中是建立一个长连接来做数据库操作

在主对话框CADODemoDlg中头文件中用include添加了业务数据库的头文件DBBusiness.h,然后添加了CDBBusiness类型成员变量m_dbBusiness,故在程序初始化的时候连接用如下代码连接数据库


BOOL CADODemoDlg::DBInit(void)
{
	AfxOleInit();
	BOOL bRet =  m_dbBusiness.Connect();
	if (!bRet)
	{
		AfxMessageBox(m_dbBusiness.GetLastError());
	}
	return bRet;
}


注:有的数据库驱动程序可能无法提供Batch结尾的函数,比如SQL Server2005测试了需要连接之前将CursorLocation设置成adUseClient。

至此,大部分数据封装操作代码都写完了,然后在你需要的地方调用ADO_DECLARE声明的函数即可,以上完成了大部分数据库增删改查代码的编写。

把上面的连接ACCESS的连接字符串改成SQLSERVER2005的即可,表结构相同的话上面的数据库增删查改代码一样可以是通用(这个做不同数据库数据导入导出可就方便了)。


BOOL CDBBusiness::Connect(void)
{
	// 服务器:127.0.0.1 端口:1434 
	// 用户名:sa
	// 密码:123456
	// 数据库:test
	strConnction = _T("Provider=SQLOLEDB;Server=127.0.0.1,1434;Database=test;User ID=sa;pwd=123456;");
	return Open(strConnction);
}



调用存储过程


--SQL Server 2005 存储过程
Create Procedure [dbo].[usp_ProcedureTest]  
@firstParam int,  
@secondParam int Output,  
@thirdParam datetime Output  
As 
BEGIN   
	-- 返回记录集的话最好是用下面语句关闭一些不必要的返回记录集
	SET NOCOUNT ON; 

	DECLARE @Count int
	SET @secondParam = @firstParam * 10
    SELECT @thirdParam = getdate() 
	SELECT @Count = Count(*) FROM tbl_Demo
	SELECT * FROM tbl_Demo
    return @Count 
END
GO



调用代码:


CAdoCommand cmd(m_dbBusiness);
	cmd.PutCommandText(_T("usp_ProcedureTest")); // 设置存储过程名称
	long nFirstParam = 10;
	cmd[_T("@firstParam")].PutValue(nFirstParam); // 设置输入参数值
	CAdoRecordset rs;
	if (cmd.Execute(rs)) //执行存储过程
	{
		long nSecondParam = 0;
		cmd[_T("@secondParam")].GetValue(nSecondParam); // 获取output参数值
		SYSTEMTIME stThirdParam;
		cmd[_T("@thirdParam")].GetValue(stThirdParam);  // 获取output参数值
		std::vector<CDBDemo> lDBDemoRecord;
		CAdoDatabase::DBMultiSelectS(lDBDemoRecord, rs, 0, false); // 获取记录集
		rs.Close(); // 需要先关闭记录集才能获取返回值
		UINT nReturn;
		cmd[(long)0].GetValue(nReturn); // 获取返回值,用索引访问不能用int类型
		ASSERT(nReturn == lDBDemoRecord.size());

		CString strMsg;
		strMsg.Format(_T("%d*10是%d\n记录集个数为%d\n数据库时间是%02d:%02d:%02d"),
			nFirstParam, nSecondParam, nReturn,
			stThirdParam.wHour, stThirdParam.wMinute, stThirdParam.wSecond);
		AfxMessageBox(strMsg);
	}
	else
	{
		AfxMessageBox(m_dbBusiness.GetLastError());
	}