华中科技大学电子科学与技术系
软件课程设计
中期进展报告
题目:学生成绩管理系统
组长:
组员:
组员:
组员:
指导老师:
目录
1. 设计任务................................................ 1
2. 基本思路................................................ 1
3. 方案设计................................................ 1
3.1 主要算法说明................................................................................................................ 1
3.1.1 ADO基本介绍........................................................................................................ 4
3.1.2 ADO中的主要函数使用方法................................................................................... 8
3.2 程序框架设计............................................................................................................... 11
4. 程序的源代码........................................... 11
5. 调试过程中出现的问题及相应解决办法...................... 16
6. 总结................................................... 17
参考文献.................................................. 17
我们组选的题目是:学生成绩管理系统。中期基本完成了所要求的大部分功能,可以添加、修改、删除、查找、排序,并且设计了登陆界面。但是仍然存在很多不足的地方,比如说不能保存排序得到的结果,不能新建表,使用ADO访问数据库的时候遇到了很多困难。
打开登陆界面,可以输入学号和密码登陆;添加学生信息部分可以添加和修改学生的姓名、学号、年龄、各科成绩,也可以添加班级;修改部分除了和添加部分相同的项目基本相同;查找部分可以按学号或姓名查找;排序可以按照各科成绩或学号排序;删除部分可以删除整个学生信息;“显示全部”部分可以显示全部的学生信息。
在完成本次任务的过程中,为了使用VC++实现界面可视化,我们首先自学了关于MFC的一些知识,并且了解了visual studio 2008的基本操作。并且在后续的过程中使用了ADO技术访问数据库。
由于之前我们对MFC也不太了解,所以花了一些时间学习了如何新建MFC工程,如何添加消息响应函数,如何添加对话框资源,如何添加事件处理函数,如何在控件里添加内容等等。其中最关键的是登录对话框的设计,使用ADO技术访问数据库。
在对话框中主要采用了列表框控件其中主要用到的函数为GetDlgItem(),GetFirstSelectedItemPosition(),GetNextSelectedItem(),DeleteAllItems(),InsertItem(),SetItemText(),SetItemState(),具体作用见下表
由于在任务中使用的ADO访问数据库,所以具体的查询排序添加等等函数都不需要自己写,只需要直接调用系统的函数就可以了。主要的函数包括OnAddclass()(添加班级记录该函数还未实现),OnAddcourse()(添加课程),OnFindname()(按姓名查找),OnFindnumber()(按学号查找),OnAddstudent()(添加学生信息),OnAll()(显示全部记录),OnDelete()(删除一个记录),OnModify()(修改学生信息),OnSortage()(按年龄排序)。中间所有的操作均采用ADO提供的方法来实现。
为了让更改能够动态在列表视图控件里动态显示,每次数据发生更改以后都需要调用CListCtrl的DeleteAllItems,然后再读取出记录集里面的内容。
例如OnModify函数的实现(只列出了部分完整的见代码的OnModify函数):
判断记录集是否已经打开
if (!(m_pRecordset->State & adStateOpen))
{
AfxMessageBox((_T("记录集未打开。")));
return;
}
获取记录集的当前位置
CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST);
POSITION pos = pListCtrl->GetFirstSelectedItemPosition();
if (pos == NULL)
{
return;
}
int nItem = pListCtrl->GetNextSelectedItem(pos);//获取当前选中的位置
CModifyDlg dlg; //实例化修改对话框的对象
dlg.m_Name = pListCtrl->GetItemText(nItem, 1); //获取修改前的内容
if (dlg.DoModal() != IDOK)
{
return;
}
//在响应IDOK时候文本控件的值已经被更新
m_pRecordset->MoveFirst(); //移动到第一条记录
m_pRecordset->Move((long)nItem); //移动到选中记录的位置
m_pRecordset->PutCollect(_variant_t(_T("SNAME")), //修改记录的值
_variant_t(dlg.m_Name));
m_pRecordset->Update(); //更新记录集中的内容
while (!m_pRecordset->adoEOF)
{
varValue = m_pRecordset->GetCollect(_variant_t(_T("SNAME")));
if (varValue.vt != VT_NULL)
{
strName = varValue.bstrVal;
}
else
{
strName = _T("");
}
//刷新ListCtrl
CString strText = _T("");
strText.Format(_T("%d"), n + 1);
pListCtrl->InsertItem(n, strText);//插入一行
strText.Format(_T("%s"), strName);
pListCtrl->SetItemText(n, 1, strText); //设置一行的第一个子项的值
n++;
m_pRecordset->MoveNext();
}
return TRUE;
对程序中可能出现的算法的介绍,以及算法的实现方法。注意格式,要活用格式刷。
ADO简介
微软公司的ADO (ActiveX Data Objects) 是一个用于存取数据源的COM组件。它提供了编程语言和统一数据访问方式OLE DB的一个中间层。允许开发人员编写访问数据的代码而不用关心数据库是如何实现的,而只用关心到数据库的连接。
使用ADO操作数据库的步骤:
(1)COM库的初始化
使用AfxOleInit()来初始化COM库,通常在CWinApp::InitInstance()的重载函数中完成。在InitInstance()函数中添加
if(!AfxOleInit()) //这就是初始化COM库
{
AfxMessageBox(L"OLE初始化出错!");
return FALSE;
}
(2)用#import指令引入ADO类型库
我们在stdafx.h中加入如下语句:
#import "c:\program files\common files\system\ado\msado15.dll"\
no_namespace\ rename("EOF", "adoEOF")
(3)创建Connection对象并连接数据库首先我们需要添加一个指向Connection对象的指针:
_ConnectionPtr m_pConnection;
_RecordsetPtr m_pRecordset;
_CommandPtr m_pCommand;
创建连接
if (!SUCCEEDED(m_pConnection.CreateInstance(__uuidof(Connection))))
{
m_pConnection = NULL;
TRACE(_T("Database CreateInstance failed"));
}
if (!SUCCEEDED(m_pRecordset.CreateInstance(__uuidof(Recordset))))
{
m_pRecordset = NULL;
TRACE(_T("Recordset CreateInstance Failed!"));
}
if (!SUCCEEDED(m_pCommand.CreateInstance(__uuidof(Command))))
{
m_pCommand = NULL;
TRACE(_T("Command CreateInstance Failed!"));
}
(4)打开记录集和数据库连接
为了方便打开数据库和记录集构造了两个函数
BOOL CMYDlg::OpenRecordset(LPCTSTR lpszSource, long nCursorType, long nLockType, long nOptions)
{
ASSERT(m_pConnection != NULL);
ASSERT(m_pRecordset != NULL);
ASSERT(lpszSource != NULL);
ASSERT(AfxIsValidString(lpszSource));
//打开记录集
try
{
return (SUCCEEDED(m_pRecordset->Open(_variant_t(lpszSource),
m_pConnection.GetInterfacePtr(),
(CursorTypeEnum)nCursorType,
(LockTypeEnum)nLockType,
nOptions)));
}
catch(_com_error e)
{
TRACE(_T("%s\n"), e.ErrorMessage());
return FALSE;
}
}
打开数据库
BOOL CMYDlg::OpenDatabase(LPCTSTR lpszConnect, long nOptions)
{
ASSERT(m_pConnection != NULL);
ASSERT(lpszConnect != NULL);
ASSERT(AfxIsValidString(lpszConnect));
//打开数据库连接
try
{
return SUCCEEDED(m_pConnection->Open(_bstr_t(lpszConnect),
_T(""), _T(""), nOptions));
}
catch (_com_error& e)
{
TRACE(_T("%s\n"), e.ErrorMessage());
return FALSE;
}
}
另外还有对应的关闭数据库和记录集的函数。
//打开数据库
CString strConnect = _T("Provider=Microsoft.Jet.OLEDB.4.0;Data Source= MY.mdb");
if (!OpenDatabase(strConnect))
{
AfxMessageBox(_T("数据库打开失败。"));
return;
}
//打开记录集
CString strSource = _T("SELECT * FROM MyTable");
if (!OpenRecordset(strSource))
{
AfxMessageBox(_T("记录集打开失败。"));
return;
}
(5)执行SQL命令
m_pCommand->put_ActiveConnection(_variant_t((IDispatch*)m_pConnection));
m_pCommand->ActiveConnection=m_pConnection;
CString strCommand= L"CREATE TABLE Persons(Id_P int,LastName varchar(255), FirstName varchar(255),Address varchar(255),City varchar(255))"; //使用sql命令创建新表
m_pCommand->CommandType=adCmdText;
m_pCommand->CommandText = _bstr_t(strCommand);
try
{
m_pCommand->Execute(NULL, NULL, adCmdUnknown);
}
catch (_com_error& e)
{
TRACE(_T("%s\n"), e.ErrorMessage());
return;
}
(1)Open函数
Open方法的原型是:
HRESULT Recordset15::Open ( const _variant_t & Source, const _variant_t & ActiveConnection, enum CursorTypeEnum CursorType, enum LockTypeEnum LockType, long Options )
其中:
①Source是数据查询字符串
②ActiveConnection是已经建立好的连接(我们需要用Connection对象指针来构造一个_variant_t对象)
③CursorType光标类型,它可以是以下值之一,请看这个枚举结构:
enum CursorTypeEnum
{
adOpenUnspecified = -1,
///不作特别指定
adOpenForwardOnly = 0,
///前滚静态光标。这种光标只能向前浏览记录集,比如用MoveNext向前滚动,这种方式可以提高浏览速度。但诸如BookMark,RecordCount,AbsolutePosition,AbsolutePage都不能使用
adOpenKeyset = 1,
///采用这种光标的记录集看不到其它用户的新增、删除操作,但对于更新原有记录的操作对你是可见的。
adOpenDynamic = 2,
///动态光标。所有数据库的操作都会立即在各用户记录集上反应出来。
adOpenStatic = 3
///静态光标。它为你的记录集产生一个静态备份,但其它用户的新增、删除、更新操作对你的记录集来说是不可见的。
};
④LockType锁定类型,它可以是以下值之一,请看如下枚举结构:
enum LockTypeEnum
{
adLockUnspecified = -1,
///未指定
adLockReadOnly = 1,
///只读记录集
adLockPessimistic = 2,
悲观锁定方式。数据在更新时锁定其它所有动作,这是最安全的锁定机制
adLockOptimistc = 3,
乐观锁定方式。只有在你调用Update方法时才锁定记录。在此之前仍然可以做数据的更新、插入、删除等动作
adLockBatchOptimistic = 4,
乐观分批更新。编辑时记录不会锁定,更改、插入及删除是在批处理模式下完成。
};
(2)Find函数
索 Recordset 中满足指定标准的记录。如果满足标准,则记录集位置设置在找到的记录上,否则位置将设置在记录集的末尾。
语法
Find (criteria, SkipRows, searchDirection, start)
参数
criteria 字符串,包含指定用于搜索的列名、比较操作符和值的语句。
SkipRows 可选,长整型值,其默认值为零,它指定当前行或 start 书签的位移以开始搜索。
searchDirection 可选的 SearchDirectionEnum 值,指定搜索应从当前行还是下一个有效行开始。其值可为 adSearchForward 或 adSearchBackward。搜索是在记录集的开始还是末尾结束由 searchDirection 值决定。
start 可选,变体型书签,用作搜索的开始位置。
criteria 中的“比较操作符”可以是“>”(大于)、“<”(小于)、“=”(等于)、“>=”(大于或等于)、“<=”(小于或等于)、“<>”(不等于)或“like”(模式匹配)。
criteria 中的值可以是字符串、浮点数或者日期。字符串值以单引号分界(如“state = 'WA'”)。日期值以“#”(数字记号)分界(如“start_date > #7/22/97#”)。
如“比较操作符”为“like”,则字符串“值”可以包含“*”(某字符可出现一次或多次)或者“_”(某字符只出现一次)。(如“state like M_*”与 Maine 和 Massachusetts 匹配。)
关于函数的使用可以在ADO使用手册里找到。这里也不再详细介绍。
这一部分可以是流程图,以及对流程图的说明。注意格式,要活用格式刷。
这里只列出两个比较关键的函数具体的函数代码比较繁琐,见文件源代码
显示所有记录的函数Read()
BOOL CMYDlg::Read()
{
CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST);
pListCtrl->DeleteAllItems(); //删除列表空间里面的所有记录
m_pRecordset->MoveFirst();
int n = 0;
while (!m_pRecordset->adoEOF)
{
_variant_t varValue;
CString strName = _T("");
int Age = 0 , LiangZi = 0 , ShuLi = 0 , MoDian = 0 , XinHao = 0 ,Number = 0;
varValue = m_pRecordset->GetCollect(_variant_t(_T("SNAME"))); //获取字段的值
if (varValue.vt != VT_NULL)
{
strName = varValue.bstrVal;
}
else
{
strName = _T("");
}
varValue = m_pRecordset->GetCollect(_variant_t(_T("AGE")));
if (varValue.vt != VT_NULL)
{
Age = varValue.intVal;
}
else
{
Age = 0;
}
varValue = m_pRecordset->GetCollect(_variant_t(_T("NUMBER")));
if (varValue.vt != VT_NULL)
{
Number = varValue.intVal;
}
else
{
Number = 0;
}
varValue = m_pRecordset->GetCollect(_variant_t(_T("LIANGZI")));
if (varValue.vt != VT_NULL)
{
LiangZi = varValue.intVal;
}
else
{
LiangZi = 0;
}
varValue = m_pRecordset->GetCollect(_variant_t(_T("SHULI")));
if (varValue.vt != VT_NULL)
{
ShuLi = varValue.intVal;
}
else
{
ShuLi = 0;
}
varValue = m_pRecordset->GetCollect(_variant_t(_T("MODIAN")));
if (varValue.vt != VT_NULL)
{
MoDian = varValue.intVal;
}
else
{
MoDian = 0;
}
varValue = m_pRecordset->GetCollect(_variant_t(_T("XINHAO")));
if (varValue.vt != VT_NULL)
{
XinHao = varValue.intVal;
}
else
{
XinHao = 0;
}
//刷新ListCtrl 在列表空间上显示记录
CString strText = _T("");
strText.Format(_T("%d"), n + 1);
pListCtrl->InsertItem(n, strText);
strText.Format(_T("%s"), strName);
pListCtrl->SetItemText(n, 1, strText);
strText.Format(_T("%d"), Age);
pListCtrl->SetItemText(n, 2, strText);
strText.Format(_T("%d"), Number);
pListCtrl->SetItemText(n, 3, strText);
strText.Format(_T("%d"), LiangZi);
pListCtrl->SetItemText(n, 4, strText);
strText.Format(_T("%d"), ShuLi);
pListCtrl->SetItemText(n, 5, strText);
strText.Format(_T("%d"), MoDian);
pListCtrl->SetItemText(n, 6, strText);
strText.Format(_T("%d"), XinHao);
pListCtrl->SetItemText(n, 7, strText);
n++;
m_pRecordset->MoveNext();
}
return TRUE;
}
按照学号查找
void CMYDlg::OnFindnumber()
{
// TODO: Add your command handler code here
CFindNumberDlg dlg;
if(dlg.DoModal()!=IDOK)
return ;
//查找条件
CString strCriteria = _T("");
strCriteria.Format(_T("NUMBER like '%d'"), dlg.m_Number);
// int number = dlg.m_Number;
//查找记录集
CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST);
pListCtrl->DeleteAllItems();
if (m_pRecordset->BOF && m_pRecordset->adoEOF)
{
return;
}
m_pRecordset->MoveFirst();
int n = 0;
m_pRecordset->Find(_bstr_t(strCriteria), 0, adSearchForward); //从当前行开始向下查找
while (!m_pRecordset->adoEOF)
{
_variant_t varValue;
CString strName = _T("");
int Number = 0, Age = 0 , LiangZi = 0 , ShuLi = 0 , MoDian = 0 , XinHao = 0 ;
varValue = m_pRecordset->GetCollect(_variant_t(_T("SNAME")));
if (varValue.vt != VT_NULL)
{
strName = varValue.bstrVal;
}
else
{
strName = _T("");
}
//获取记录集的内容
varValue = m_pRecordset->GetCollect(_variant_t(_T("AGE")));
if (varValue.vt != VT_NULL)
{
Age = varValue.intVal;
}
else
{
Age = 0;
}
varValue = m_pRecordset->GetCollect(_variant_t(_T("NUMBER")));
if (varValue.vt != VT_NULL)
{
Number = varValue.intVal;
}
else
{
Number = 0;
}
varValue = m_pRecordset->GetCollect(_variant_t(_T("LIANGZI")));
if (varValue.vt != VT_NULL)
{
LiangZi = varValue.intVal;
}
else
{
LiangZi = 0;
}
varValue = m_pRecordset->GetCollect(_variant_t(_T("SHULI")));
if (varValue.vt != VT_NULL)
{
ShuLi = varValue.intVal;
}
else
{
ShuLi = 0;
}
varValue = m_pRecordset->GetCollect(_variant_t(_T("MODIAN")));
if (varValue.vt != VT_NULL)
{
MoDian = varValue.intVal;
}
else
{
MoDian = 0;
}
varValue = m_pRecordset->GetCollect(_variant_t(_T("XINHAO")));
if (varValue.vt != VT_NULL)
{
XinHao = varValue.intVal;
}
else
{
XinHao = 0;
}
//刷新ListCtrl,在控件上显示查找得到的记录
CString strText = _T("");
strText.Format(_T("%d"), n + 1);
pListCtrl->InsertItem(n, strText);
strText.Format(_T("%s"), strName);
pListCtrl->SetItemText(n, 1, strText);
strText.Format(_T("%d"), Age);
pListCtrl->SetItemText(n, 2, strText);
strText.Format(_T("%d"), Number);
pListCtrl->SetItemText(n, 3, strText);
strText.Format(_T("%d"), LiangZi);
pListCtrl->SetItemText(n, 4, strText);
strText.Format(_T("%d"), ShuLi);
pListCtrl->SetItemText(n, 5, strText);
strText.Format(_T("%d"), MoDian);
pListCtrl->SetItemText(n, 6, strText);
strText.Format(_T("%d"), XinHao);
pListCtrl->SetItemText(n, 7, strText);
n++;
// m_pRecordset->MoveNext();
m_pRecordset->Find(_bstr_t(strCriteria), 1, adSearchForward); //跳过当前行向下查找
}
}
1,程序最初中需要的问题包括汉字使用导致的编译出现问题。
解决办法:在需要输入汉字的地方加上L,或者_T例如L"数据库未打开",_T("记录集已打开")
2,在Access中新建表格的时候输入字段错误,导致程序运行的时候出现问题。由于调试不能发现错误,所以需要自己检查,最后自己花费了很长时间检查才发现了这样的错误。
3,执行SQL语句出现错误,由于自己对于sql也不太熟悉,所以编写sql语句的时候出现了问题。后来通过自己简单学习了一点sql的知识,能够成功编写基本sql语句。
4,使用_CommandPtr指针执行sql的设置出现问题。由于我对ado的执行方法也是初次接触,所以到网上查询了好长时间最终找到了一种适合自己的方法。具体成功以后的代码见OnSortage()函数。
5,设计登录界面的时候遇到的一个很麻烦的问题,如何设置部分控件的背景为透明,使它们显示在背景图像上面而不会遮住背景。到网上搜索了并且自己尝试了很多种办法,最后发现除了需要使用SetBkMode(TRANSPARENT);设置以外还必须得勾选控件的sample才行。不过后来我自己也发现直接使用TextOut()函数也是很方便的。
中期实验的过程是项目的关键部分,虽然距离完成全部任务还是有一些距离,但却是项目的骨架。期间遇到了很多难以解决的困难,需要花费更多的时间到网上查找资料,阅读相关书籍。但是在调试程序的过程中,大家确实学习到了很多知识。比如说关于MFC的基本操作,比如使用ADO数据库,比如自己独立发现问题、检索资料和解决问题的能力。
其中有很多问题,在网上是可以找到答案的。但是关于MFC其实比较方便的就是msdn了,里面给出了每一个函数的十分详细具体的介绍。网上查到的资料大都是翻译的msdn。ado也有专门的说明。这些都对我们的项目进展有很大的帮助。
写代码的过程中经常会出现编译错误或者得不到自己预期的效果,其中很大部分都是由于粗心大意造成的。比如说变量名字拼写错误,把代码从一处复制粘贴的时候忘记了修改关键的部分等等。
另外,项目的大部分工作均由徐洁同学完成,小组其他成员应该多多向他学习,为项目的完成做出更多的贡献。
【1】http://blog.csdn.net/tangbo6/article/details/740814 vc下ado开发实践
【2】http://hi.baidu.com/pecoco/blog/item/e05ee5ccffd4d72bf8dc61f4.html VC中利用ADO共同实现数据库的操作
【3】《精通——Visual C++实效编程280例》作者:曲扬 编著 ,出 版 社:人民邮电出版社出版,时间:20##-5-1
【4】《VC++深入详解》作者:孙鑫,余安萍 编著,出 版 社:电子工业出版社出版,时间:20##-6-1
编号:()字号《软件课程设计》报告班级:姓名学号:指导老师:中国矿业大学计算机科学与技术学院年月软件课程设计任务书专业年级:计算机…
华中科技大学电子科学与技术系课程设计报告(20--xx年度第学期)名称:软件课程设计题目:院系:班级:学号:学生姓名:指导教师:设…
软件工程课程设计报告海天一色网上书店软件工程课程设计个人报告题目网上书店学院名称信息科学与工程学院专业班级计算机092班报告人姓名…
软件课程设计实验报告个人报告课班成员学一需求分析1引言32预期功能33设计目标4二界面方案设计说明1拟选用的开发平台和工具介绍42…
软件开发课程设计报告题目职工信息管理系统设计院系专业班级学生姓名学号指导教师2013年9月2日至2013年9月15华中科技大学武昌…
华中科技大学电子科学与技术系课程设计报告(20--xx年度第学期)名称:软件课程设计题目:院系:班级:学号:学生姓名:指导教师:设…
编号:()字号《软件课程设计》报告班级:姓名学号:指导老师:中国矿业大学计算机科学与技术学院年月软件课程设计任务书专业年级:计算机…
淮海工学院课程设计报告书题目集邮信息管理系统学院东港学院专业软件工程班级姓名学号20xx年07月3日软件工程A课程设计第1页共14…
软件工程课程设计报告海天一色网上书店软件工程课程设计个人报告题目网上书店学院名称信息科学与工程学院专业班级计算机092班报告人姓名…
企业工资管理系统目录引言2课题研发的背景2课题研发的目的与意义2第一章可行性研究211技术可行性分析212社会可行性分析313经济…
黑龙江科技学院经济管理学院本科学位论文中期报告姓名XXX班级国贸XXX班专业研究方向指导老师XXX论文题公司的影响及对策研究中期报…