ODBC
2018-03-19 本文已影响14人
downdemo
几个常用类
- afxdb.h
- CDBException:CException 171-197
- CDatabase:CObject 204-318
- CFieldExchange 322-401
- CRecordset:CObject 556-957
- CDBVariant 1025-1056
- CRecordView:CFormView 1061-1101
- 1.0Set.cpp
- CMy10Set:CRecordset
- GetDefaultConnect()
- GetDefaultSQL()
- DoFieldExchange // RFX
- AssertValid()
- Dump()
构建数据源
- 控制面板-管理工具-ODBC数据源64位
- 建立ODBC数据源-连接数据库-选择和处理记录-数据库应用程序中的文档与视图
- ODBC数据源-系统DSN-添加Access-数据源名My_DataSource-确定
- 访问数据要连接数据源,连接封装到CDatabase类中
- 连接后可以
- 构造CRecordset派生类对象,从数据库中读取相应记录,保存在CRecordset派生类中
- 管理事务
- 执行SQL
- 要正确使用CDatabase必须在ODBC数据源控制台中正确注册,同意程序可以有多个数据源,对应多个CDatabase对象,也可以用多个CDatabase对象连接同一数据源
- CRecordset一般要派生出一个新子类,在CRecordset派生类中的数据对应数据库中相应的行
- 用户要重载CRecordset类中的GetDefaultSQL函数来返回使用的表的名字
- CRecordset对象负责定制SQL、在数据库中移动记录指针、增删改查,不需要时就要释放CRecordset对象
- 视图文档结构:视图显示数据,文档对象(有多个)存取不同的数据,视图还负责和文档的数据交换和更新。有时这样的结构多余,如当只操作一个数据源中的一个数据库,数据放在视图类中就行了
- 新建一个表My_Access_DB,作者、出版社、价格,内容自填
- 新建MFC-单文档-文件支持+ODBC+选择数据源-新建DSN(.mdb)-浏览位置,取名My_1.0,完成-进入ODBC Access安装-数据库:选择-选择之前建好的表
关联编辑框控件和数据
- 建DIALOG,4个Static Text+4个Edit Control,关联完即可显示
- 1.0set.h
- CMy10Set:CRecordset
- long m_ID
- CStringW column1
- CStringW column2
- double column3
- GetDefaultConnect()
- GetDefaultSQL()
- DoFieldExchange()
- AssertValid()
- Dump()
- CMy10Set:CRecordset
- 1.0View.cpp
- CMy10View:CRecordView
- DoDataExchange() // 关联编辑框控件和数据库字段,使用下面的函数关联
- DDX_FieldText(pDX, IDC_EDIT1, m_pSet->m_ID, m_pSet)
- DDX_FieldText(pDX, IDC_EDIT2, m_pSet->column1, m_pSet)_
- PreCreateWindow()
- OnInitialUpdate()
- OnFilePrintPreview()
- OnPreparePrinting()
- OnBeginPrinting()
- OnEndPrinting()
- OnRButtonUp()
- OnContextMenu()
- AssertValid()
- Dump()
- GetDocument()
- OnGetRecordset()
菜单项更新和删除
- Resource View-1.0.rc-Menu-IDR_MAINFRAME
- 加入三个菜单项,分别为删除、更新、清空,为其分配ID如下,并右键添加Event Handler
- ID_DELETE_RECORD
- ID_UPDATE_RECORD
- ID_CLEAR_DOMAIN
- 第一个COMMAMD
void CMy10View::OnDeleteRecord()
{
// TODO: Add your command handler code here
CRecordsetStatus m_cStatus;
try{ m_pSet->Delete(); }
catch (CDBException* m_pEx)
{
AfxMessageBox(m_pEx->m_strError);
m_pEx->Delete();
m_pSet->MoveFirst(); // 失败则将记录指针移到开头
UpdateData(FALSE);
return;
}
m_pSet->GetStatus(m_cStatus);
if (m_cStatus.m_lCurrentRecord == 0)
m_pSet->MoveFirst();
else
m_pSet->MoveNext();
UpdateData(FALSE);
}
- 第一个UPDATE_COMMAND_UI
void CMy10View::OnUpdateDeleteRecord(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable(!m_pSet->IsEOF());
}
- 第二个COMMAND
void CMy10View::OnUpdateRecord()
{
// TODO: Add your command handler code here
m_pSet->Edit();
UpdateData(TRUE);
if (m_pSet->CanUpdate()) // 记录可更新则返回非零值
m_pSet->Update();
}
- 第二个UPDATE_COMMAND_UI
void CMy10View::OnUpdateUpdateRecord(CCmdUI *pCmdUI)
{
// TODO: Add your command update UI handler code here
pCmdUI->Enable(!m_pSet->IsEOF());
}
- 第三个COMMAND
void CMy10View::OnClearDomain()
{
// TODO: Add your command handler code here
m_pSet->SetFieldNull(NULL);
UpdateData(FALSE);
}
重载OnMove
- Class View-右键CMy10View,Class Wizard-Virtual Functions-双击左边的OnMove会出现到右边的重写虚函数的列表中,Edit Code
BOOL CMy10View::OnMove(UINT nIDMoveCommand)
{
// TODO: Add your specialized code here and/or call the base class
switch (nIDMoveCommand)
{
case ID_RECORD_PREV:
if (!m_pSet->IsBOF())
m_pSet->MovePrev();
break;
case ID_RECORD_FIRST:
m_pSet->MoveFirst();
break;
case ID_RECORD_NEXT:
if (!m_pSet->IsEOF())
m_pSet->MoveNext();
break;
case ID_RECORD_LAST:
m_pSet->MoveLast();
break;
default:
ASSERT(FALSE);
}
UpdateData(FALSE);
return CRecordView::OnMove(nIDMoveCommand);
}
菜单项添加
- 给菜单项加一个选项,ID设为ID_ADD_RECORD
- 在class view中,右键Set类,选择Add-Add Functions
- 添加函数名为GetMaxID
- 在xxxSet.cpp中即可看到添加的函数,修改如下
int CMy10Set::GetMaxID()
{
MoveLast(); // 移到最后一条记录
return m_ID;
}
- 右键新的菜单项,Event Handler添加COMMAND消息
void CMy10View::OnAddRecord()
{
// TODO: Add your command handler code here
CRecordset* pSet = OnGetRecordset(); // 获取指向数据库的指针
if (pSet->CanUpdate() && !pSet->IsDeleted())
{
pSet->Edit();
if (!UpdateData()) return;
pSet->Update();
}
long m_INewID = m_pSet->GetMaxID() + 1; // 获取新ID值
m_pSet->AddNew(); // 添加一个新记录
m_pSet->m_ID = m_INewID;
m_pSet->Update(); // 保存新记录
m_pSet->Requery(); // 刷新数据库
m_pSet->MoveLast();
UpdateData(FALSE); // 更新表单
}
菜单项浏览和排序
- 新建一个Dialog,ID改为IDD_MOVE_RECORD,右键Add Class,类名为CMoveToRecord,在MoveToRecord.h中#include "resource.h"
- 添加一个Static Text和Edit Control,Edit Control的ID设置为IDC_RECORD_ID
-
给Edit Control添加一个变量m_RecordID,Category设置为Value,类型设置为long
-
增加菜单项如下,ID设置为ID_MOVEToRecord
-
添加COMMAND消息CMy10View::OnMovetorecord(),在视图类1.0View.cpp中#include "MoveToRecord.h"
-
打开Resource View-Tool Bar-IDR_MAINFRAME,添加一个按钮,点击上方的A(Text Tool)输入字符为M,ID选择为ID_MOVEToRecord
- 接着响应前一步的COMMAND消息
void CMy10View::OnMovetorecord()
{
// TODO: Add your command handler code here
CMoveToRecord dlgMoveTo;
if (dlgMoveTo.DoModal() == IDOK)
{
CRecordset *pSet = OnGetRecordset();
pSet->SetAbsolutePosition(dlgMoveTo.m_RecordID);
UpdateData(FALSE);
}
}
- 此时运行就可以使用移动到某条记录的功能了,接下来是排序。在CRecordSet和其派生类中有一个m_strSort成员,决定了记录的排序。在菜单项中添加菜单项“按价格排序”,ID设为ID_SORT_PRICE,映射COMMAND消息处理函数OnSortPrice(),之后运行就是按价格递增的排序了
void CMy10View::OnSortPrice()
{
// TODO: Add your command handler code here
// TODO: Add your command handler code here
m_pSet->Close(); // 关闭数据库
m_pSet->m_strSort = "价格";
m_pSet->Open();
UpdateData(FALSE); // 更新已排序过的记录
}