自学总结:MFC之ADO数据库编程----仿SQL SERVER操作项目

 前段时间开始学习了ADO数据库编程,后来做了一个小项目,是用MFC制作了一个仿SQL SERVER操作的。项目完成了数据库连接、关闭,表的打开关闭,SQL语句的执行,数据实时的修改,增删记录。

关于ADO的相关知识会另外写,在此不详细讨论,下面只写出了项目的文档。



开发预备:vs2013  SQL SERVER2012

下面是具体操作步骤:


1、先写一个自己的类

--用创建向导先生成一个对话框程序,项目名叫UseADO,然后新建一个自己的C++类,不继承任何的类。

注意:一定要在stdafx.h文件里添加    #import "c:\Program Files\Common Files\System\ado\msado15.dll" \
no_namespace rename("EOF", "adoEOF") rename("BOF", "adoBOF")   否则编译通不过

//ADO_Operate.h
#pragma once

class CUseADODlg;

class ADO_Operate
{
public:
	ADO_Operate();
	~ADO_Operate();

public:
	_ConnectionPtr m_pConnection;
	_RecordsetPtr  m_pRecordSet;
	CUseADODlg* pDlg;

public:
	//初始化ADO
	BOOL OnInitADO_Operate();
	//执行查询语句
	BOOL GetRecordSet(_bstr_t bstrSQL);
	//_RecordsetPtr& GetRecordSet(_bstr_t bstrSQL);
	//退出连接
	BOOL ExitConnect();
	//执行sql语句
	BOOL ExecuteSQL(_bstr_t bstrSQL);
};


 -------------------------------

//ADO_Operate.cpp
#include "stdafx.h"
#include "ADO_Operate.h"
#include "UseADODlg.h"

ADO_Operate::ADO_Operate()
{
}


ADO_Operate::~ADO_Operate()
{
}

BOOL ADO_Operate::OnInitADO_Operate()
{
	if (!AfxOleInit())
	{
		AfxMessageBox(_T("OLE初始化出错!"), MB_ICONWARNING);
		return FALSE;
	}
	
	try{
		//创建connection的实例对象
		m_pConnection.CreateInstance("ADODB.Connection");
		_bstr_t ConnectStr = "Provider=SQLOLEDB.1;\
							 Integrated Security=SSPI;\
							 Persist Security Info=False;\
							 Initial Catalog=AdventureWorksDW2012;\
							 Data Source=admin1504091717";
		m_pConnection->Open(ConnectStr, "", "", adModeUnknown);
	}
	catch (_com_error e)
	{
		AfxMessageBox(e.Description());
		return FALSE;
	}
	AfxMessageBox(_T("连接成功!"));
	return TRUE;
}

//执行SQL语句
BOOL ADO_Operate::ExecuteSQL(_bstr_t bstrSQL)
{
	try
	{
		if (m_pConnection == NULL)
		{
			OnInitADO_Operate();
		}
		m_pConnection->Execute(bstrSQL, NULL, adCmdText);
		return TRUE;
	}
	catch (_com_error e)
	{
		AfxMessageBox(e.Description());
		return FALSE;
	}
}

//获得数据库里面的记录集--------打开数据库里面一个表,并将所有的信息放在m_pRecordSet里面
BOOL ADO_Operate::GetRecordSet(_bstr_t bstrSQL)
{
	try{
		if (m_pConnection == NULL)
			OnInitADO_Operate(); 
		//m_pRecordSet->Fields->Count;
		m_pRecordSet.CreateInstance(__uuidof(Recordset));
		m_pRecordSet->Open(bstrSQL,
			m_pConnection.GetInterfacePtr(),
			adOpenDynamic,
			adLockOptimistic,
			adCmdTable);
	}
	catch (_com_error e){
		AfxMessageBox(e.Description());
		return FALSE;
	}
	AfxMessageBox(_T("获取表记录成功!"));
	return TRUE;
}

//退出连接
BOOL ADO_Operate::ExitConnect()
{
	// 关闭记录集和连接
	if (m_pRecordSet != NULL)
		m_pRecordSet->Close();
	m_pConnection->Close();
	   // 释放环境
	AfxOleTerm();
	AfxMessageBox(_T("已成功关闭数据库!"), MB_OK);
	return TRUE;
}



2、设计对话框界面

设计细节不作讨论,大的空白框是CLIST CONTROL控件,view属性需要改为report。

自学总结:MFC之ADO数据库编程----仿SQL SERVER操作项目_第1张图片

3、代码编写

先在UseADODlg.h文件里面添加以下代码:

private:
	int FieldsCount;//字段总数
	CString ItemStr[30];//存放一行中,所有列的数据--一条修改前的记录
	CString UpdateStr[30];//存放修改后的数据
	int nItem;//当前所选的行索引

	void ShowTableData();//显示表内容
	void DeleteListCtrlData();//删除ListCtrl里面的数据
	int	  GetStringArray(CString str);//获得修改的字符串,返回获得的字段数

public:
	CListCtrl m_DataList;
	ADO_Operate MyADO;
	CString m_SQLCommand;

	afx_msg void OnBnClickedButtonConnect();
	afx_msg void OnBnClickedButtonClose();
	afx_msg void OnBnClickedButtonUpdate();
	afx_msg void OnBnClickedButtonAdd();
	afx_msg void OnBnClickedButtonDelete();
	afx_msg void OnBnClickedButtonOpenTable();
	afx_msg void OnBnClickedButtonCloseTable();
	afx_msg void OnNMDblclkListData(NMHDR *pNMHDR, LRESULT *pResult);



当然还要添加

#include "ADO_Operate.h"           

#include "comutil.h"                      

#include "afxcmn.h"

这几个东东才可以

在UseADODlg.cpp文件中添加:

BOOL CUseADODlg::OnInitDialog()中添加:

m_DataList.SetExtendedStyle(
		LVS_EX_FULLROWSELECT | 
		LVS_EX_GRIDLINES);


	GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_OPEN_TABLE)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_CLOSE_TABLE)->EnableWindow(FALSE);



继续添加代码:
void CUseADODlg::ShowTableData()
{
	if (MyADO.m_pRecordSet != NULL)
	{
		FieldsCount = MyADO.m_pRecordSet->Fields->Count;//获取字段总数
		_bstr_t FieldName;
		CString str;
		for (int i = 0; i < FieldsCount; i++)
		{
			FieldName = MyADO.m_pRecordSet->Fields->GetItem(long(i))->GetName();//获取第一个字段名字
			str = (LPCTSTR)FieldName;//转换成cstring类型

			m_DataList.InsertColumn(i, str, LVCFMT_CENTER, 100, 10);//插入字段名
		}

		//用游标操作数据
		CString DataStr;
		MyADO.m_pRecordSet->MoveFirst();
		for (int j = 0; MyADO.m_pRecordSet->adoEOF == VARIANT_FALSE; j++)
		{
			m_DataList.InsertItem(j, NULL);//listctrl控件中插入新的一行
			for (int i = 0; i < FieldsCount; i++)
			{
				_variant_t VarStr= MyADO.m_pRecordSet->GetCollect(_variant_t(long(i)));//获得i列的数据
				if (VarStr.vt != VT_NULL) //读到NULL值时
				{
					DataStr = (TCHAR*)(_bstr_t)MyADO.m_pRecordSet->GetCollect(_variant_t(long(i)));//获得当前记录的内容
				}
				else
				{
					DataStr = "NULL";
				}
				m_DataList.SetItemText(j, i, DataStr);//在 j 行 i 列写入内容
			}
			MyADO.m_pRecordSet->MoveNext();
		}
	}//if (MyADO.m_pRecordSet != NULL)
}

//删除ListCtrl控件里面的数据
void CUseADODlg::DeleteListCtrlData()
{
	int nColumnCount = m_DataList.GetHeaderCtrl()->GetItemCount();
	// Delete all of the columns.
	for (int i = 0; i < nColumnCount; i++)
	{
		m_DataList.DeleteColumn(0);
		m_DataList.GetNextItem(-1, LVNI_SELECTED);
	}
}

//从Edit控件的字符串,处理后获得每个字段的数据
int CUseADODlg::GetStringArray(CString str)
{
	//UpdateStr[30] = { "" };
	for (int i = 0; i < 30; i++)
		UpdateStr[i].Empty();//UdateStr初始化为空
	int i = 0;
	int j = 0;
	int Ncount = 0;
	//取出已修改的字符串
	for (; i < FieldsCount; i++)
	{
		for (; j < str.GetLength(); j++)
		{
			if (str.GetAt(j) != ';')
			{
				UpdateStr[i] += str.GetAt(j);
			}
			else
			{
				if (str.GetAt(j + 1) == ';')
				{
					AfxMessageBox(_T("含有非法字符!\n如果你想将字段设为空值,请填写NULL"));
					return -1;
				}
				Ncount++;
				j++;
				break;
			}
		}//for j++
	}//for i++
	return Ncount;
}

//连接数据库
void CUseADODlg::OnBnClickedButtonConnect()
{
	// TODO:  在此添加控件通知处理程序代码
	if (MyADO.OnInitADO_Operate() == TRUE)
	{
		GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(FALSE);
		GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(TRUE);
		GetDlgItem(IDC_BUTTON_OPEN_TABLE)->EnableWindow(TRUE);
	}
}

//关闭数据库
void CUseADODlg::OnBnClickedButtonClose()
{
	// TODO:  在此添加控件通知处理程序代码
	MyADO.ExitConnect();
	GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_OPEN_TABLE)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_CLOSE_TABLE)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(TRUE);
}

//修改数据
void CUseADODlg::OnBnClickedButtonUpdate()
{
	// TODO:  在此添加控件通知处理程序代码
	//UpdateData(TRUE);
	if (MyADO.m_pRecordSet == NULL || m_SQLCommand == "")
		return;
	
	CString str;
	GetDlgItem(IDC_EDIT_COMMAND)->GetWindowText(str);
	if (str != m_SQLCommand)
	{
		if (GetStringArray(str) != FieldsCount)//获取已修改的字符串到UpdateStr[30]
		{
			AfxMessageBox(_T("请填写正确的数据"));
			return;
		}

		MyADO.m_pRecordSet->MoveFirst();
		MyADO.m_pRecordSet->Move(nItem);
		for (int i = 0; i < FieldsCount; i++)
		{
			if (UpdateStr[i] != ItemStr[i] )
			{
				try{
					_bstr_t FieldsName;
					FieldsName = MyADO.m_pRecordSet->Fields->GetItem(long(i))->GetName();//获取要个修改字段的名字
					if (UpdateStr[i] = "NULL")
					{
						_variant_t strstr = _variant_t(UpdateStr[i]);
						strstr.vt = VT_NULL;
						MyADO.m_pRecordSet->Fields->GetItem(_variant_t(FieldsName))->Value = strstr;
					}
					else{
						MyADO.m_pRecordSet->Fields->GetItem(_variant_t(FieldsName))->Value = _variant_t(UpdateStr[i]);
					}
					MyADO.m_pRecordSet->Update();
				}
				catch (_com_error e){
					AfxMessageBox(e.Description());
					return;
				}
			}	
		}
		DeleteListCtrlData();//先删除之前ListCtrl里面的数据
		ShowTableData();//在重新显示修改后的数据
		m_SQLCommand = "";
		UpdateData(FALSE);
	}
}

//添加记录
void CUseADODlg::OnBnClickedButtonAdd()
{
	// TODO:  在此添加控件通知处理程序代码
}

//删除记录
void CUseADODlg::OnBnClickedButtonDelete()
{
	// TODO:  在此添加控件通知处理程序代码
}

//打开表
void CUseADODlg::OnBnClickedButtonOpenTable()
{
	// TODO:  在此添加控件通知处理程序代码
	UpdateData(TRUE);
	if (m_SQLCommand != "")
	{
		_bstr_t TableName = (_bstr_t)m_SQLCommand;
		
		if(!MyADO.GetRecordSet(TableName))
			return;
	}
	else{
		if (!MyADO.GetRecordSet("DimAccount"))
			return;
	}
	GetDlgItem(IDC_BUTTON_OPEN_TABLE)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_CLOSE_TABLE)->EnableWindow(TRUE);
	ShowTableData();
}

//关闭表
void CUseADODlg::OnBnClickedButtonCloseTable()
{
	// TODO:  在此添加控件通知处理程序代码
	if (MyADO.m_pConnection != NULL && MyADO.m_pRecordSet != NULL)
	{
			MyADO.m_pRecordSet->Close();
			m_DataList.DeleteAllItems();

			DeleteListCtrlData();
			MyADO.m_pRecordSet = NULL;
	}
	GetDlgItem(IDC_BUTTON_OPEN_TABLE)->EnableWindow(TRUE);
	GetDlgItem(IDC_BUTTON_CLOSE_TABLE)->EnableWindow(FALSE);
}

//ListCtrl控件双击消息
void CUseADODlg::OnNMDblclkListData(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR);
	// TODO:  在此添加控件通知处理程序代码
	nItem = m_DataList.GetNextItem(-1, LVNI_SELECTED);
	if (nItem >= 0 && FieldsCount > 0)
	{
		//ItemStr[30] = { "" };
		for (int i = 0; i < 10; i++)
			ItemStr[i].Empty();//ItemStr初始化为空

		m_SQLCommand = "";
		for (int i = 0; i < FieldsCount; i++)
		{
			ItemStr[i] = m_DataList.GetItemText(nItem, i);
			m_SQLCommand = m_SQLCommand + ItemStr[i] + ';';
		}
		UpdateData(FALSE);
	}

	*pResult = 0;
}



看下结果:

自学总结:MFC之ADO数据库编程----仿SQL SERVER操作项目_第2张图片

4、对相关代码、思路的解释

ADO_Operate类完成的是数据库的连接关闭、sql语句的执行、记录集打开关闭、数据库环境初始化。之所以把他写成单独的一个类,是因为对后面的编写要方便,容易扩展,易维护。如果要增加数据库其他的操作和功能,只要在此类里面添加就可以了,然后使用时就用他的对象(如:项目里的MyADO对象)进行操作完成。需要注意的是数据操作,需要经常使用try  catch语句进行控制解析错误的发生,这会对你的调试维护非常有帮助。

-----------------------------------------

在LIST CONTROL控件中,是不支持实时修改数据的,所以我们用了双击消息,以便获得整条记录信息,需要先设置控件的风格:

m_DataList.SetExtendedStyle(
		LVS_EX_FULLROWSELECT | 
		LVS_EX_GRIDLINES);//设置CLIST CONTROL的初始风格

//这段代码就是设置了CLIST CONTROL控件的网格风格和可以选取整行风格

同时将选取行的信息存入一个变量( CString ItemStr[30])里面并显示在命令语句编辑框中,在按照对应格式去修改你想要修改的数据。这其中牵涉很多字符串处理的知识,假如你将要修改的数据修改好后,按修改数据的BUTTON,会将你修改后的数据存入另一个变量( CString UpdateStr[30] )中,具体的取字符串、比较字段值是否修改的算法请详见上面给的代码。

--------------------------------

在代码里有

//从Edit控件的字符串,处理后获得每个字段的数据
int CUseADODlg::GetStringArray(CString str){};

这个函数之所以要返回一个int,是因为要与打开表后获得一个 int FieldsCount (存放总字段数)去比较,以确定字符串的正确与否。

-----------------------------------------

在下面这个函数里

//ListCtrl控件双击消息
void CUseADODlg::OnNMDblclkListData(NMHDR *pNMHDR, LRESULT *pResult)

nItem = m_DataList.GetNextItem(-1, LVNI_SELECTED);获取当前所选的记录在数据库里面的索引。nItem是私有变量,之所以定义成私有的变量,是因为在很多地方都要用到。比如移动数据库的游标到选择的记录上。

--------------------------------------

_bstr_t  , _variant_t 都是com里面的数据类型,都是可以转换成cstring类型的变量,但要注意

_variant_t VarSt;

VarStr.vt = VT_NULL;

if(VarStr.vt != VT_NULL)

{

}

在vc里面ADO经常遇到数据是NULL的情况,直接转换是有问题的,所以读取或者赋值都应该采用上面的方法

-------------------------------------

关于连接数据库SQL SERVER的open函数中字符串格式问题:

_bstr_t ConnectStr = "Provider=SQLOLEDB.1;\
Integrated Security=SSPI;\
Persist Security Info=False;\
Initial Catalog=AdventureWorksDW2012;\
Data Source=admin1504091717";
m_pConnection->Open(ConnectStr, "", "", adModeUnknown);

这是我自己的电脑上连接的字符串,其实并不用自己去写ConnectStr的值,详细方法在:

http://blog.csdn.net/zo2k123/article/details/48178241

-----------------------------------------------

可能你已经发现增加和删除记录的方法还没有写,留给你们自己去完善。

-----------------------------------------------


tip:这个小小的项目只是我自学ADO编程写的一个实验性东西,还有很多可以修改和完善的,比如在LIST CONTROL控件里面可以完成双击后,生成一个编辑框覆盖在你点击的数据上,来实时的修改你所要修改的数据,这里就不讨论了。

OK到这里结束了。

                                                                                                                                                                                     


 Kong_Sir

你可能感兴趣的:(MFC)