分类:
2008-10-13 16:42:17
学生考试管理系统ADO版
作者:
以前在Delphi下做数据处理,对VC中ADO类的感觉比较麻烦,于是就试着参考别人的ado类封装了两个类,原来的类为 Carlos Antollini
的两个ADO类,版本1.2(上有下载),修改了一下,然后继承了一个CADOStorage类,又看过刘永超老师,想重新用自己的想法设计一下,然后就写了这个小系统:界面结构采用现在数据处理软件常用的MDI形式,如图一:
图一 程序运行画面
我是按照需求分析,数据库设计和一表一类的思想设计了这个系统,下面就从三个主要方面(数据库设计,类设计,编程实现)分析我的设计思路:
一.数据库关系图:
我采用的是ACCESS数据库,对每个表都基本上按照 E-R 关系模式设计,表结构及关系图如图二所示:
图二 数据库关系图
二. 类结构图
采用 Rose 设计了一部分,然后再编程完善后,用 Rose 的逆向工程生成的,基本上包括了现在使用的所有类及其之间的关联关系,如图三:
图三 类之间的关联关系
三. 编程实现部分
主要考虑了以下几个方面:
1.基本表数据的录入
只有输入班级信息,年级信息等一些基础信息后,系统才可以进行其它,如考试信息的管理,那么怎样录入这些基础数据,如果一表建一类的话(这样做最好),如果基础表太多,这样系统编程工作量就会增加,所以想了想,能不能让系统根据数据库的关联关系去选择外键字段然后生成输入界面,再根据用户操作生成sql语句,去更新数据库,如添加记录。如图四所示:
图四 添加记录 | 图五 添加记录 |
图四中的年级编号在年级表中索引得到;图五中的班级编号在班级表中索引得到,课程编号在课程表中索引得到,该怎么做?
(1)、数据库结构方面:参考了用友数据库的一些表的设计,然后就想把数据表<表说明>中增加了字段:外键个数,字段0,外键0,外部表0,显示字段0…..字段n:(现在数据库预设计为一个表可以最多有4个外键)。
(2)、程序类方面:设计了一个记录字段信息(CFieldRecord)以及一个基本表类(CBaseTbl):
//记录信息量 struct CFieldRecord { char FieldName[20];//字段名(如果为外键字段,则为显示字段) char Value[255]; //值 bool IsBool; //是否布尔 bool IsStrType; //是否字符串 bool IsVisible; //是否显示 char DisplayName[20]; //对应ID值(用于外键) char FKtbl[20]; //外键表名或基本表名 CFieldRecord* pFK; //外键信息 }; //基本表的处理 class CBaseTbl : public CObject { public: CBaseTbl(); virtual ~CBaseTbl(); CList(3)、如在基本表视中点击添加数据过程:m_TblList;//名称列表 void GetTblnames(); //单表记录数组,默认记录最后一条 bool GetFieldRecord(CPtrArray& FieldArray,CString ctblname,CString constr=""); bool RemoveRecord(int CurRecordPos); protected: CADOStorage m_Storage; //单表记录数组,默认记录最后一条 void AddExtraFieldRecord(CPtrArray& FieldArray,CString ctblname); private: CString m_TblName; public: //选择联合表 bool SelectUnionTbl(CString StrTblname,CString& Realtbl,bool ByRealTblName=false); void RefreshList(CListCtrl& List1); void ReQuery(CString TblName); void ExecSql(CString SqlStr); };
//选择添加记录 void CStudentScoreView::OnADDRecord() { ASSERT(this->m_hWnd); CBMDialog dialog1; CStudentScoreDoc* pDoc=this->GetDocument(); pDoc->m_BaseTblList.GetFieldRecord(dialog1.RecordArray,this->m_CurTblName); dialog1.ISADD=true; dialog1.m_hParent=this->m_hWnd; dialog1.m_OprTblName=this->m_CurTblName; if(dialog1.DoModal()==IDOK) { AfxMessageBox("添加记录!"); //ASSERT(dialog1.m_hWnd); } }申明为:文档类中 CBaseTbl m_BaseTblList;//表信息实例
//单表记录数组 bool CBaseTbl::GetFieldRecord(CPtrArray& FieldArray,CString ctblname,CString constr) { ASSERT(ctblname.Trim()!=""); //1.判断及清理工作 …省略 CFieldRecord* fldrec; CADOFieldInfo fldinfo; CString fldvalue; //2.判断记录位置 bool IsZeroRecord;//记录数为0 …省略 int count=this->m_Storage.GetFieldCount(); //3.添加字段记录到指针数组 for(int i=0;i基本过程就是两个:m_Storage.GetFieldValue(i,fldvalue); } this->m_Storage.GetFieldInfo(i,&fldinfo); strcpy(fldrec->FKtbl,ctblname); strcpy(fldrec->FieldName,fldinfo.m_strName); strcpy(fldrec->Value,fldvalue); switch(fldinfo.m_nType) { case VT_DATE: fldrec->IsStrType=true; fldrec->IsBool =false; break; … default: fldrec->IsStrType=false; fldrec->IsBool =false; break; } fldrec->pFK=NULL; fldrec->IsVisible=true; FieldArray.Add(fldrec); } //添加外键信息 this->AddExtraFieldRecord(FieldArray,ctblname); return true; }
//动态创建组件 CFieldRecord* pRecord; int top=0; for(int i=1;i<=this->RecordArray.GetCount();i++) { top=(i-1)*22; pRecord=(CFieldRecord*)RecordArray.GetAt(i-1); this->CreateStatic(pRecord,top,70+i); //布尔或有关联外键 if((pRecord->IsBool)||(pRecord->pFK!=NULL)) this->CreateCombo(pRecord,top,10+i); else this->CreateEdit(pRecord,top,10+i); }注:有关联外件就读取关联表显示字段的索引信息,然后添加到 ComboBox 中,
//发送修改或添加消息 void CBMDialog::SendChangeMsg(void) { CWnd* pWnd; CString value,Msg; CFieldRecord* pRecord; for(int i=1;i<=this->RecordArray.GetCount();i++) { pWnd=this->GetDlgItem(10+i); if(pWnd) { pRecord=(CFieldRecord*)RecordArray.GetAt(i-1); if ((!pRecord->IsBool)&&pWnd->IsKindOf(RUNTIME_CLASS(CComboBox))) { //非布尔类型,且有关联字段时 CComboBox* pCombo=(CComboBox*) pWnd; CFldValue* p; int index=pCombo->GetCurSel(); p=(CFldValue*)pCombo->GetItemDataPtr(index); value=p->FieldValue; } else pWnd->GetWindowText(value); if(value.Trim()=="") { Msg=(CString)pRecord->FieldName+"不能为空!"; AfxMessageBox(Msg); pWnd->SetFocus(); return; } else { if((i==1)&&(!this->ISADD))//如果是修改的话,则不对第一字段操作 {} else strcpy(pRecord->Value,value); } } } //发送字符串 CString cSend; if(this->ISADD) { cSend=this->GenerateInsertSql(); } else { cSend=this->GenerateUpdateSql(); } LPARAM lparam=(LPARAM)&cSend; ::SendMessage((HWND)this->m_hParent,WM_USER+51,0,lparam); }这是基本的信息,根据显示内容生成sql语句,发送消息给基本表视,完成数据库更新操作。
LRESULT CStudentScoreView::OnExecSql(WPARAM wParam,LPARAM lParam) { CString c; c=*((CString*)lParam); CStudentScoreDoc* pDoc=this->GetDocument(); pDoc->m_BaseTblList.ExecSql(c); this->RefreshShow(); //AfxMessageBox(c); return 0; }2.关于考试过程的编码
class CExam : public CObject { public: CExam(); virtual ~CExam(); CString m_No; //考试编号 COleDateTime m_Date; //考试日期 CString m_TermNo; //学期编号 CStringList& GetTerms(); CStringList* GetNos(); protected: CADOStorage m_Storage;//数据库连接 CString m_TblName; //表名称 private: CStringList m_TermList;//学期列表 CStringList m_NoList; //编号列表 public: void ClearTermList(void); void ClearNoList(void); void GetExamByNo(CString No);//获取考试信息 bool IsNoExisted(CString No);//编号是否存在 void AddExam(); void DeleteExamByNo(CString No); };(1)、在文档类中申明CExam m_Exam.添加记录
void CExamView::OnBnClickedButton2() { CEdit* pEdit=(CEdit*) this->GetDlgItem(IDC_EDIT1); CString ExamNo; pEdit->GetWindowText(ExamNo); CExamDoc* pDoc=this->GetDocument(); if(pDoc->m_Exam.IsNoExisted(ExamNo)) { AfxMessageBox("不能添加重复记录!"); return; } else { //添加记录 pDoc->m_Exam.m_No=ExamNo; CDateTimeCtrl* pPicker= (CDateTimeCtrl*)this->GetDlgItem(IDC_DATETIMEPICKER1); pPicker->GetTime(pDoc->m_Exam.m_Date); CComboBox* pComb=(CComboBox*)this->GetDlgItem(IDC_COMBO1); pComb->GetLBText(pComb->GetCurSel(),pDoc->m_Exam.m_TermNo); pDoc->m_Exam.AddExam(); } }(2)、然后看: pDoc->m_Exam.AddExam();
void CExam::AddExam() { CString FldList,ValueList; FldList="考试编号,考试日期,学期编号"; ValueList="''''"+this->m_No+"'''',''''"+this->m_Date.Format()+"'''',''''"+this->m_TermNo+"''''"; this->m_Storage.ExecInsertSql(this->m_TblName,FldList,ValueList); }这是一个与数据库交互的过程。