前段时间开始学习了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。
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;
}
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)
--------------------------------------
_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