一. 功能需求:
1. 能进行对数据库的连接(后台):
这是查询管理信息的基础。
2. 能进行增、删、改、查等基本功能:
这是学生成绩管理系统最基本的功能,可以在这个基础上进行扩展其他功能。
二. 总体设计:
1. 对数据库的连接(后台):
这是所有功能的基础,此项的实现可在后台进行。
我使用的是ADO方式连接数据库。(应避免让用户手动连接数据库)
这项功能写在一个类中最好。
2. 对窗体控件及相应的函数设计:
A.控件的添加:
(1).要进进行增、删、改、查,必须要添加相应的按钮。
在对话框的的特定区域添加相应的按钮,而且,对于增、删、改等操作只能由管理员进行,其他用户无权操作;对于查询,则可有按学号或按姓名等多种查询方式。
(2).要能显示查询结果,必须有显示控件(我使用List Contral控件)。
(3).要能有菜单就更好了。
B.相应控件响应函数的添加:
为每个相应的控件添加响应函数。
三. 详细设计:
1. 对数据库的连接(后台):
(1).引入ADO类库:
A.添加一个ADO连接类。
B.添加动态链接库:
在ADOConn.h的头文件中加入以下语句:
(2).封装ADO数据库对象:
A.在ADOConn.h头文件中添加成员变量:
_ConnectionPtr m_pConnection;
_RecordsetPtr m_pRecordset;
(3).添加函数:
A.添加初始化OLE/COM库环境函数(OnInitADOConn())。
B.添加断开数据库连接函数(ExitConnect())。
C.添加打开获得记录集函数(GetRecordSet())。
D. 添加执行SQL语句的ExecuteSQL()函数。
相关代码如下:
voidADOConn::OnInitADOConn()
{
::CoInitialize(NULL); //初始化OLE/COM环境
m_pConnection.CreateInstance(__uuidof(Connection)); //创建connection对象
// 在ADO操作中建议语句中要常用try...catch()来捕获错误信息,
// 因为它有时会经常出现一些意想不到的错误。
try
{
// 打开本地Access库student.mdb
m_pConnection->Open("Provider=Microsoft.Jet.OLEDB.4.0;DataSource=student.mdb","","",adModeUnknown);
}
//捕获异常
catch(_com_error e)
{
AfxMessageBox(e.Description());
}
}
void ADOConn::ExitConnect()
{
//关闭记录集和连接
if(m_pRecordset!=NULL)
m_pRecordset->Close();
m_pConnection->Close();
//释放环境
::CoUninitialize();
}
_RecordsetPtr& ADOConn::GetRecordSet(_bstr_t bstrSQL)
{
try
{
//连接数据库,如果conection对象为空,则重新连接数据库
if(m_pConnection==NULL)
OnInitADOConn();
//创建记录集对象
m_pRecordset.CreateInstance(__uuidof(Recordset));
//取得表中的记录
m_pRecordset->Open(bstrSQL,m_pConnection.GetInterfacePtr(),adOpenDynamic,adLockOptimistic,adCmdText);
}
catch(_com_error e)
{
e.Description();
}
//返回记录集
return m_pRecordset;
}
BOOL ADOConn::ExecuteSQL(_bstr_t _bstrSQL)
{
_variant_tRecordsAffected;
try
{
//是否已连接数据库
if(m_pConnection==NULL)
OnInitADOConn();
m_pConnection->Execute(_bstrSQL,NULL,adCmdText);
return true;
}
catch(_com_error e)
{
e.Description();
return false;
}
}
2. 对窗体控件及相应的函数设计:
(1). 添加相应的控件:
(1).要进进行增、删、改、查,必须要添加相应的按钮。
将增、删、改操作的按钮放在管理员区,将查询操作单独放在查询区。
(2).要能显示查询结果,必须有显示控件(我使用List Contral控件)。
在较大的区域显示,则加入ListContral的列表控件。
(3).要能有菜单就更好了。
添加菜单资源,设计相应的菜单。
窗口设计截图如下:
(2). 相应的响应函数的添加:
A.管理员操作区:
a.管理员登陆按钮的响应函数:
当点击增、删、改按钮时,若未登录,则不能进行相应的操作。
必须先登录才可以。
点击登录按钮后,弹出登陆的对话框,用户输入管理员姓名与密码,若正确则编制变量为真,可以进行增、删、改操作。
相关代码如下:
创建登录对话框:
voidCBaseADODlg::OnManagerEnter() //管理员登陆
{
//TODO: Add your control notification handler code here
CManagerEnterDlgenterdlg;
if(enterdlg.DoModal()==IDOK)
{
Flag_ManagerEnter=TRUE;
MessageBox("管理员登陆成功!");
}
}
点击确认后,核对输入的信息是否正确
void CManagerEnterDlg::OnOK() //输入信息后,按确定
{
//TODO: Add extra validation here
// CBaseADODlgdlg;
password="123";
ManagerName="123";
UpdateData(true);
if(m_ManagerName.IsEmpty())
{
MessageBox("管理员姓名不能为空!");
return;
}
if(m_Enterpassword.IsEmpty())
{
MessageBox("登录密码不能为空!");
return;
}
if(m_ManagerName!=ManagerName||m_Enterpassword!=password) //判断输入的信息是否正确
{
MessageBox("您输入的信息有误!");
return;
}
CDialog::OnOK();
}
b.添加按钮的响应函数:
单击该按钮后,弹出添加对话框,输入数据后点击确定,则保存数据到数据库。
单击后,创建添加对话框并显示,将相应的控件提前加好,
单击提交按钮后,保存数据。
【1】.判断是否进行管理员登陆,若登录则创建相应的对话框:
void CBaseADODlg::OnAdddata() //添加信息
{
if(Flag_ManagerEnter) //判断是否进行管理员登陆
{
CAddInfoaddlg;
if(addlg.DoModal()==IDOK) //输入信息后是否提交
{
MessageBox("提交成功!");
m_list.DeleteAllItems(); //删除list表原有的信息
AddToGrid(); //将新的数据库内容再次添加到表中
}
}
else
MessageBox("您目前没有权限添加,请先进行管理员登录!");
}
【2】.单击提交按钮后,将数据保存到数据库:
void CAddInfo::OnOK() //确定添加
{
//TODO: Add extra validation here
UpdateData(true);
if(m_addnum.IsEmpty()) //判断学号是否为空
{
MessageBox("学号不能为空");
return;
}
if(m_addname.IsEmpty())
{
MessageBox("姓名不能为空");
return;
}
if(m_addnorscore.IsEmpty())
{
MessageBox("平时成绩不能为空");
return;
}
if(m_addmidscore.IsEmpty())
{
MessageBox("期中成绩不能为空");
return;
}
if(m_addfinalscore.IsEmpty())
{
MessageBox("期末成绩不能为空");
return;
}
ADOConnm_AdoConn;
m_AdoConn.OnInitADOConn();
_bstr_tsql;
sql="SELECT*FROM score";
m_pRecordset=m_AdoConn.GetRecordSet(sql);
double norscore,midscore,finalscore,sum;
CStringsumscore;
norscore=atof(m_addnorscore);
midscore=atof(m_addmidscore);
finalscore=atof(m_addfinalscore);
sum=norscore*0.1+midscore+0.2+finalscore*0.7;
sumscore.Format("%g",sum);
try
{
m_pRecordset->AddNew();
m_pRecordset->PutCollect("Num",_variant_t(m_addnum));
m_pRecordset->PutCollect("Name",_variant_t(m_addname));
m_pRecordset->PutCollect("NormalScore",_variant_t(m_addnorscore));
m_pRecordset->PutCollect("MidScore",_variant_t(m_addmidscore));
m_pRecordset->PutCollect("FinalScore",_variant_t(m_addfinalscore));
m_pRecordset->PutCollect("SumScore",_variant_t(sumscore));
m_pRecordset->Update();
m_AdoConn.ExitConnect();
}
catch(...)
{
MessageBox("操作失败!");
}
CDialog::OnOK();
}
c.删除按钮的响应函数:
单击该按钮后,创建删除对话框并显示,,输入要删除的数据后点击确定,在数据库中查找该数据,若找到则删除并保存数据到数据库。
【1】 创建对话框与添加相同。
【2】 输入要删除的数据后点击确定后,在数据库中查找该数据,若找到则删除并保存数据到数据库。其他连接数据库等操作与添加相同,当查找到要删除的数据后,用 m_pRecordset->Delete(adAffectCurrent)函数实现。
部分代码如下:
try
{
if(!m_pRecordset->adoBOF) //表中数据不为空,则现将记录集指针移到第一条
m_pRecordset->MoveFirst();
else
{
AfxMessageBox("表内数据为空");
return ;
}
var =m_pRecordset->GetCollect("Num");
if(var.vt != VT_NULL)
strNum = (LPCSTR)_bstr_t(var);
while(strNum!=m_delnum&&!m_pRecordset->adoEOF)
{
m_pRecordset->MoveNext();
var =m_pRecordset->GetCollect("Num");
if(var.vt != VT_NULL)
strNum = (LPCSTR)_bstr_t(var);
}
if(m_pRecordset->adoEOF) //判断是否查询到要删除的信息
MessageBox("没有查询到您要删除的信息!");
else
{
m_pRecordset->Delete(adAffectCurrent); //删除当前信息
m_pRecordset->Update(); //更新数据库
m_AdoConn.ExitConnect(); //断开与数据库的连接
}
}
catch(...)
{
MessageBox("操作失败!");
}
d.修改按钮的响应函数:
单击该按钮后,创建修改对话框并显示,,输入要修改的数据后点击确定,在数据库中查找该数据,若找到则获得用户重新输入的数据并保存数据到数据库。
【1】 创建对话框与删除相同。
【2】 输入要修改的数据后点击确定后,在数据库中查找该数据,若找到则获得用户重新输入的数据并保存数据到数据库。
其他连接数据库等操作与删除相同,当查找到要修改的数据后用m_pRecordset->PutCollect("Num",_variant_t(m_modnum));函数实现。
部分代码如:
try
{
if(!m_pRecordset->adoBOF) //表中数据不为空,则现将记录集指针移到第一条
m_pRecordset->MoveFirst();
else
{
AfxMessageBox("表内数据为空");
return ;
}
var =m_pRecordset->GetCollect("Num");
if(var.vt != VT_NULL)
strNum = (LPCSTR)_bstr_t(var);
while(strNum!=m_modfyNum)
{
m_pRecordset->MoveNext();
var= m_pRecordset->GetCollect("Num");
if(var.vt != VT_NULL)
strNum = (LPCSTR)_bstr_t(var);
}
if(m_pRecordset->adoEOF&&!m_pRecordset->adoEOF)//当查到的每一条记录的字段信息与所查询信息不符合且
MessageBox("没有查询到您要找的信息!");
else
{
m_pRecordset->PutCollect("Num",_variant_t(m_modnum)); //将修改后的信息输入数据库
m_pRecordset->PutCollect("Name",_variant_t(m_modname));
m_pRecordset->PutCollect("NormalScore",_variant_t(m_modnor));
m_pRecordset->PutCollect("MidScore",_variant_t(m_modmid));
m_pRecordset->PutCollect("FinalScore",_variant_t(m_modfinal));
m_pRecordset->PutCollect("SumScore",_variant_t(sumscore));
m_pRecordset->Update();
m_AdoConn.ExitConnect();
}
}
catch(...)
{
MessageBox("操作失败!");
B.查询区:
可以有两种查询方式,按学号查询与按姓名查询。
此处,在查询区我将两中查询方式的相应按钮放在一起,选择一种方式时,显示这一种而隐藏另一种。
a.按学号查询:
选择按学号查询,则显示相应的控件。用户输入要查询的信息后单击查询,从第一条记录遍历整个表,若找到则显示相应的信息,否则提示未找到。
代码如下:
void CBaseADODlg::OnSearchAll() //查询
{
//TODO: Add your control notification handler code here
UpdateData(true); //更新获得控件的值
m_list.DeleteAllItems(); //删除list表原有的信息
if(!SelSearchFlag) //判断查询方式
SearchNum(m_searchnum); //按学号查询
else
SearchName(m_searchname); //按姓名查询
}
按学号查询:
void CBaseADODlg::SearchNum(CStringstr) //按学号查询的实现函数
{
ADOConnm_AdoConn;
m_AdoConn.OnInitADOConn();
CStringsql;
sql.Format("SELECT*FROM score");
m_pRecordset=m_AdoConn.GetRecordSet((_bstr_t)sql);
m_pConnection.CreateInstance(__uuidof(Connection));
_variant_tvar;
CStringstrNum,strName,strNor,strMid,strFinal,strSum;
try
{ if(!m_pRecordset->adoBOF) //表中第一条数据不为空,则现将记录集指针移到第一条
m_pRecordset->MoveFirst();
else
{
AfxMessageBox("表内数据为空");
return;
}
var= m_pRecordset->GetCollect("Num");
if(var.vt!= VT_NULL)
strNum= (LPCSTR)_bstr_t(var);
while(strNum!=str&&!m_pRecordset->adoEOF)//当查到的每一条记录的字段信息与所查询信息不符合且当前记录集不为空时循环
{
m_pRecordset->MoveNext();
var= m_pRecordset->GetCollect("Num");
if(var.vt!= VT_NULL)
strNum= (LPCSTR)_bstr_t(var);
}
if(m_pRecordset->adoEOF) //判断是否查询到
{
MessageBox("没有查询到您要找的信息!");
AddToGrid();
}
else
{
inti=0;
m_list.InsertItem(i,"");
var= m_pRecordset->GetCollect("Name"); //得到信息
if(var.vt!= VT_NULL)
strName=(LPCSTR)_bstr_t(var);
var =m_pRecordset->GetCollect("NormalScore");
if(var.vt!= VT_NULL)
strNor=(LPCSTR)_bstr_t(var);
var= m_pRecordset->GetCollect("MidScore");
if(var.vt!= VT_NULL)
strMid=(LPCSTR)_bstr_t(var);
var= m_pRecordset->GetCollect("FinalScore");
if(var.vt!= VT_NULL)
strFinal=(LPCSTR)_bstr_t(var);
var= m_pRecordset->GetCollect("SumScore");
if(var.vt!= VT_NULL)
strSum=(LPCSTR)_bstr_t(var);
m_list.SetItemText(i,0,strNum); //插入list表中
m_list.SetItemText(i,1,strName);
m_list.SetItemText(i,2,strNor);
m_list.SetItemText(i,3,strMid);
m_list.SetItemText(i,4,strFinal);
m_list.SetItemText(i,5,strSum);
}
}
catch(_com_error*e)
{
AfxMessageBox(e->ErrorMessage());
}
m_AdoConn.ExitConnect(); //断开与数据库的连接
}
b.按姓名查询:与按学号查询类似,不做赘述。
四. 测试与实现:
五. 总结:
通过本次学生管理系统的开发,使我对数据库编程产生了更大的兴趣,以前在dos环境下编写的学生管理系统与此相比,能够充分体现出可视化编程的优势,界面比dos下的好多了。而且直接连接数据库,可以开发出很有用的软件。
不前几乎所有的软件都要进行数据存储,而且有的数据存储量非常大,必须写好的查询代码,才能实现数据的快速存取,这样的软件才有人要。
这次的开发过程同样充满曲折,但当实现所需的功能时,这种成就感将以前的所有痛苦、bug都化为乌有。就是在不断的出现问题,解决问题的过程中学习。
很多人都知难而退,也许只有坚持到底,你才会解决解决所遇到的困难。这次编程中,我对此深有体会,我遇到了一个BUG,没有语法错误,但老是运行不了。
上周末,我两天就在解决这个问题,但由于我的坚持,最终解决了这个问题,在此过程中我学到了很多东西。
本程序的优点:实现了所要求的增、删、改、查等基本功能。具有较好的界面,
而且使用列表控件能显示所有学生的信息。
本程序的缺点:对于特殊查询、成绩排名等未实现(时间不够,所以为实现),
界面设计还不够友好,还有很多有待提高的地方。