软件工程课程设计:C++ Builder+Access选课系统与ODBC事务避坑

发布时间:2026/11/3 19:53:45
软件工程课程设计:C++ Builder+Access选课系统与ODBC事务避坑

简介:《软件工程课程设计指导书》面向计算机科学与技术等专业的软件工程初学者,服务于课程设计实践环节,帮助读者把面向对象方法、C++语言与数据库知识落到一个小型软件系统的完整开发过程中。文档以选课系统为案例,串联需求分析、软件设计、软件测试到源码提交的全流程,明确登录、选课、退课、成绩查询等功能要求,以及用例图、序列图、对象模型、状态图等文档产出规范,并给出书面报告的结构与成绩考核构成。附录补充了用 ODBC 建立数据源、C++ Builder 6.0 窗体搭建与 ADO 连接数据库的操作指引,可视为一份从建库到连库的起步路线。资源包共 1 个 doc 文件,约 539KB,轻量易取,适合在 10 学时上机中对照执行或作为报告撰写参照。目前已有 99 人学习,可供刚接触软件工程规范、需要按步骤完成课程设计的学生参考。

1. 先看清这份软件工程课程设计指导书:10 学时里真正要交付什么

很多同学拿到《软件工程课程设计指导书》的第一反应是照着附录拖窗体,真正让课程设计卡住的往往不是控件,而是需求字段、数据源和测试序列三件事对不上。这份软件工程指导书围绕一个选课系统,把 10 学时上机拆成需求、设计、编码、测试和报告五段,技术栈锁定 C++ Builder 6.0 加 Access 2000,先修课要求一门高级语言、C++ 和数据库系统概论。它适合计算机相关专业做课程设计的学生,也适合需要快速复盘传统 C/S 开发流程的初级开发者。资源不是可以直接交差的完整源码,而是一份带操作指南的实践任务书:字段至少覆盖哪些、功能至少做哪几个、报告里必须出现哪些图,都写在任务里。把它当成验收清单用,比当成教程从头读到尾更有效。

2. 需求与数据模型:把选课系统拆成 5 张表和 4 类图

这一章先把“画图”往后放,先冻结对象和字段。指导书里的功能要求看起来只有登录、查询开课、查询选课、查询成绩、选课、退课,但字段清单很长,学生、教师、课程、开课、成绩混在一起。如果不先拆表,后面画用例图时会把“选课”画成一次数据库插入,序列图也会变成一堆没有生命线的箭头。常见的翻车点不是代码写不出来,而是报告里的对象模型和数据库表对不上,答辩时被追问一句“选课记录存在哪张表”就答不圆。

我一般会先做名词筛选:学生、教师、课程、开课计划、选课记录,这五个名词能覆盖指导书列出的全部字段。学生和教师是人员对象,课程是教学计划对象,开课计划是某学期某教师在某教室开某门课,选课记录是学生和开课计划之间的关联对象。成绩不单独建表,平时成绩、考试成绩、总评成绩挂在选课记录上,这样查询成绩时不用跨三张表。开课计划里的“选课人数”是冗余字段,写入时维护,查询时可以直接显示,不必每次 count。这个取舍在单机 Access 场景下很实用,代价是必须用事务保证人数和选课记录一致。

2.1 先冻结五个对象:学生、教师、课程、开课、选课记录

对象模型不需要画得很花,但每个对象要有主键和关键属性。学生对象的主键用学号,教师对象用教师号,课程对象用课号,开课计划用自动编号的开课号,选课记录用自动编号的选课号。选课记录上保留学生学号和开课号作为外键,再加平时成绩、考试成绩、总评成绩。这样对象模型和后面的 Access 表可以一一对应,报告里的类图不会变成“学生类里有课程列表、课程类里又有学生列表”的循环。

对象主键关键属性说明
学生学号姓名、密码、性别、出生年月、籍贯、地址、电话、家庭地址登录和选课的主体
教师教师号姓名、教研室、职称、性别、电话开课和录入成绩的主体
课程课号课名、学分、先行课号、课时课程目录,先行课号自关联
开课计划开课号课号、教师号、开课教室、人数限制、选课人数某次具体开课
选课记录选课号学号、开课号、平时成绩、考试成绩、总评成绩学生与开课计划的关联

这张表的作用是让报告里的对象模型有落点。画用例图时,学生参与者触发登录、查询开课、选课、退课、查询成绩;教师参与者触发录入成绩、查询开课名单;教务管理员参与者维护课程和开课计划。指导书没有明确写管理员,但开课数据总得有人维护,常见做法是教师兼维护,或者单独设一个管理角色。角色数量不要求多,关键是每个用例都能在对象模型里找到承载表。

2.2 字段清单落到 Access 表:类型、主键和唯一索引

Access 2000 的字段类型不多,文本、数字、日期/时间、货币、备注、是/否够用。学号用文本 12 位,不要用数字,否则前导零会丢;教师号文本 8 位;课号文本 8 位;姓名文本 20 位;密码文本 32 位,课程设计可以存明文,但报告里要写清风险,合格做法是客户端先做一次哈希再入库;电话文本 20 位;地址和家庭地址用文本 50 位或备注;出生年月用日期/时间;学分、课时、人数限制、选课人数用数字,字段大小选整型;成绩用数字,字段大小选单精度或双精度,保留一位小数。

选课表要加唯一索引,索引字段是学号加开课号。这个索引不是可选项,它是防止同一学生重复选同一门课的最后一道闸。开课表的选课人数要有默认值 0,人数限制要有验证规则>0。课程表的先行课号允许为空,表示没有先行课;如果不为空,插入前要检查先行课是否已经修过,课程设计可以只做提示,不必强制阻断。总评成绩建议存下来,Access 2000 没有计算列,查询时再算会让报表和界面各写一套公式,后面改权重很容易漏改。

表字段示例类型建议约束
学生表学号、姓名、密码、性别、出生年月、籍贯、地址、电话、家庭地址文本/日期学号主键,密码非空
教师表教师号、姓名、教研室、职称、性别、电话文本教师号主键
课程表课号、课名、学分、先行课号、课时文本/数字课号主键,先行课号可空
开课表开课号、课号、教师号、开课教室、人数限制、选课人数自动编号/文本/数字课号、教师号外键,人数限制大于 0
选课表选课号、学号、开课号、平时成绩、考试成绩、总评成绩自动编号/文本/数字学号加开课号唯一索引

字段名可以用英文缩写,也可以用拼音,报告里要有一张字段对照表。不要用Name、Password这种保留字或过于通用的名字,Access 查询里容易加方括号。密码字段不要叫Pwd又在界面里显示成“密码”,报告里写清楚是登录密码。成绩字段允许为空,表示还没录入,查询时要区分“0 分”和“未录入”,否则总评统计会把未录入当零分。

2.3 用例图与序列图:把“选课成功”画成可检查的步骤

用例图不是把功能名堆成椭圆就完事。学生参与者连到登录、查询开课、选课、退课、查询成绩;教师参与者连到录入成绩、查询开课名单;管理员参与者连到维护课程、维护开课计划。每个用例下面可以用备注写前置条件,比如选课的前置条件是已登录、未重复选课、人数未满。用例图里不要出现“数据库”这种系统内部对象,参与者只写人或者外部系统。

序列图至少画两条:登录成功和选课成功。登录序列的生命线是登录界面、登录控制器、学生表;消息顺序是输入学号密码、参数化查询、返回学生记录、打开主界面。选课序列的生命线是选课界面、选课服务、开课表、选课表;消息顺序是提交学号和开课号、检查重复、检查容量、插入选课记录、更新选课人数、返回结果。序列图里要把“事务开始”和“事务提交”画进去,不画的话报告看起来像没有并发控制的单机玩具。

画序列图时,每个消息名用动词加名词,比如checkCapacity(offeringId)、insertEnrollment(sno, offeringId)、updateSelected(offeringId)。不要写处理选课这种大颗粒消息,评审时看不出步骤。指导书要求报告里有序列图,实际检查时最容易被问的是“失败分支在哪”,所以选课序列要补一个容量已满的返回箭头,登录序列要补一个密码错误的返回箭头。

2.4 状态图选谁:开课计划或选课记录的状态迁移

至少一个重要对象的状态图,我一般选开课计划。状态可以定义为待选、可选、满员、结课。待选是开课计划刚建立、还没开放选课;可选是开放选课且选课人数小于人数限制;满员是选课人数等于人数限制;结课是成绩录入完成。事件分别是开放选课、选课成功、退课成功、录入成绩。状态图不用画成 UML 教科书,但每个状态要有进入条件,比如“满员”状态的进入条件是Selected >= Capacity,退出条件是退课成功。

选课记录也可以画状态图:已选、已退、已出成绩。已选到已退是退课事件,已选到已出成绩是录入成绩事件,已退不能直接到已出成绩。这个状态图更贴近代码里的业务约束,报告里放一个就够。状态图的价值在于逼你想清楚哪些操作允许、哪些操作要拦截,比如已退的选课记录不能再次退课,已出成绩的记录不能随便删除。

3. 连接 Access 2000:ODBC 数据源、BDE 入口与 ADOConnection 的参数表

旧工具链里最容易让人怀疑人生的是连接数据库。指导书附录给了 13 步建数据源,又在 C++ Builder 里用 ADOConnection 连 ODBC 数据源。很多同学照着做,BDE Administrator 里 Open 之后字体变黑,以为成功了,回到程序里还是报“未发现数据源名称”。这里要把三个东西分开看:Access 文件是数据,ODBC 数据源是名字,ADOConnection 是程序里的连接对象。BDE Administrator 只是旧版入口,真正生效的是 ODBC 数据源管理器里那条 DSN。

3.1 为什么旧项目绕不开 ODBC:DSN 是应用和 Access 之间的合同

C++ Builder 6.0 里的 ADO 组件通过 OLE DB 访问数据,Access 2000 的提供程序常见走MSDASQL,也就是 OLE DB 到 ODBC 的桥。DSN 里保存了驱动名、数据库路径、登录方式,程序只认数据源名。这样做的好处是换数据库路径时只改 DSN,不用改代码;坏处是 DSN 建错、位数不对、名字多一个空格,程序就找不到。课程设计是单机模式,用用户 DSN 就够,换 Windows 账户后如果还要用,建系统 DSN 更稳。

ODBC 数据源管理器有 32 位和 64 位两个入口。C++ Builder 6.0 编出来的是 32 位程序,必须用 32 位管理器建 DSN。常见现象是“我明明建了数据源,测试连接也成功,程序就是找不到”,原因往往是打开的是 64 位管理器。可以在开始菜单搜索“ODBC”,看有没有“32 位”字样,或者直接运行%WINDIR%\SysWOW64\odbcad32.exe。这个细节不写进报告也行,但排错时必须先查。

提示:数据源名建议用英文加下划线,比如CourseDSN,不要用带全角空格的中文名。中文名不是不能用,而是排错时肉眼很难看出全角空格和半角空格的区别。

3.2 建 DSN 的 13 步要留下证据:数据源名、路径、驱动三件套

指导书附录的步骤可以照做,但要在报告里留下三样证据:数据源名、Access 文件绝对路径、驱动名称。步骤大致是打开 ODBC 数据源管理器,添加Microsoft Access Driver (*.mdb),数据源名填CourseDSN,数据库选择C:\CourseDesign\选课.mdb,确定后回到管理器能看到这条 DSN。如果系统里没有(*.mdb)驱动,说明只装了新版 Access 驱动,可以选(*.mdb, *.accdb),但报告里要写实际使用的驱动名,不要照抄指导书。

BDE Administrator 那一段可以保留作为过程记录,但不要把它当成唯一路径。指导书里 Object→New→Microsoft Access Driver→odbc1→ODBC Administrator→添加数据源→Apply→选 ODBC DSN→Open,这一串操作的本质还是在 ODBC 管理器里建 DSN。BDE 里 Open 成功只证明 BDE 别名能打开,不证明 ADOConnection 能连。实际做课程设计时,我一般直接建 ODBC DSN,然后到 C++ Builder 里用 ADOConnection 的 Build 按钮选数据源,少绕一层。

参数建议值说明
数据源名CourseDSN程序连接串里必须完全一致
驱动Microsoft Access Driver (*.mdb)没有就选带 accdb 的版本
数据库路径C:\CourseDesign\选课.mdb路径不要带全角字符
登录名空Access 文件没有数据库用户时留空
密码空如果加了数据库密码才填
只读不勾选课和退课要写入
DSN 类型用户 DSN 或系统 DSN单机当前用户用用户 DSN 即可

Access 文件所在目录必须可写。把.mdb放在只读目录、光盘目录或者没有写权限的共享目录,查询能通,选课插入会失败。这个坑在机房环境很常见,现象是“查询开课正常,一选课就报错”,原因不是 SQL 写错,而是文件权限不对。把文件复制到本地工作目录,去掉只读属性,再测试一次写入。

3.3 BDE Administrator 只是旧入口:ADOConnection 真正吃的是 ODBC DSN

在 C++ Builder 6.0 里新建 Application,保存项目后,从 ADO 页拖一个TADOConnection到窗体上。双击它,点Build,选择“使用数据源名称”,下拉框里选CourseDSN,点“测试连接”。测试通过后确定,把LoginPrompt设为False,Connected设为True。LoginPrompt=False的作用是避免程序启动时弹登录框,单机 Access 没有数据库用户,弹框只会让演示中断。Connected=True表示窗体创建时就连接,调试阶段方便,发布时可以在登录成功后再打开连接。

连接串常见形式是Provider=MSDASQL.1;Persist Security Info=False;Data Source=CourseDSN。Data Source后面就是 DSN 名,不是 Access 文件路径。有些同学把路径直接写在这里,测试连接也能过,但换一台机器就失效。用 DSN 的好处是路径变了只改 DSN。Persist Security Info=False表示连接打开后不保留密码信息,单机无密码时影响不大,保留默认即可。

ADOConnection 属性建议值作用
ConnectionStringProvider=MSDASQL.1;Data Source=CourseDSN指定 ODBC 数据源
LoginPromptFalse不弹数据库登录框
Connected调试时 True,发布时按需控制连接时机
KeepConnectionTrue保持连接,减少反复打开
IsolationLevel默认单机 Access 不需要改

连接成功后,拖TADOQuery,把Connection指向ADOConnection;再拖TDataSource,把DataSet指向ADOQuery;最后拖TDBGrid,把DataSource指向TDataSource。这条链路是 C++ Builder 里最常用的数据感知组合。查询开课情况时,ADOQuery.SQL.Text写 SELECT,Open后表格自动显示。写操作时用ExecSQL,不要用Open,否则会报“查询没有返回结果集”。

3.4 连接串与登录参数:LoginPrompt、Connected、测试连接

测试连接要注意顺序:先确认 ODBC 管理器里 DSN 存在,再确认 C++ Builder 是 32 位程序,最后确认 Access 文件没有被其他程序独占打开。Access 2000 的.mdb被 Access 界面打开时,ODBC 仍然能读,但写入可能受锁影响。演示前把 Access 关掉,避免“选课偶发失败”的玄学问题。如果测试连接报“未发现数据源名称并且未指定默认驱动程序”,先查 DSN 名是否完全一致,再查 32/64 位,不要急着重装 C++ Builder。

登录功能的连接时机可以这样安排:主窗体创建时Connected=False,登录按钮里先ADOConnection->Connected=True,执行登录查询,成功后再打开主界面。这样做的好处是数据库文件路径不对时,程序启动不会直接崩,而是弹一个可读的错误提示。错误提示不要只写Connection failed,要带上 DSN 名和原始错误信息,排错时能省很多时间。

注意:如果程序在机房能跑、回到宿舍不能跑,优先检查 DSN 是不是建在当前用户下,以及 Access 文件路径是不是还指向机房目录。用户 DSN 不跟着项目文件走,换机器要重建。

4. C++ Builder 6.0 落地:登录、选课、退课与成绩查询的事件链

界面不需要做得像商业软件,但事件链要清楚。登录界面负责验证学号和密码,主界面负责导航,开课查询界面用表格显示课程、教师、教室、人数限制、已选人数,选课界面负责提交选课和退课,成绩界面负责按学号查成绩。每个界面不要直接拼 SQL 字符串,参数化查询能挡住大部分引号和特殊字符问题。课程设计的数据量小,但“O'Brien”这种姓名或课程名里的单引号足够让拼接 SQL 翻车。

4.1 窗体骨架:一个连接、三个查询、四个界面

我一般放一个TADOConnection在主窗体上,其他窗体通过主窗体访问,避免每个窗体各建一个连接。查询组件按用途分:登录查询、开课查询、选课查询、成绩查询。数据源和表格按界面配。登录界面用两个TEdit和一个TButton,密码框设PasswordChar='*'。主界面用按钮或菜单跳转。开课查询界面用TDBGrid显示结果,再加课程名、教师名两个查询条件框。选课界面显示当前选中的开课号和课程名,提供“选课”和“退课”按钮。成绩界面用TDBGrid显示课名、学分、平时成绩、考试成绩、总评成绩。

界面主要控件绑定的查询
登录Edit、Button按学号和密码查学生
开课查询DBGrid、Edit、Button按课程名或教师名查开课
选课/退课DBGrid、Button、Label查已选课程、插入或删除选课记录
成绩查询DBGrid、Edit、Button按学号查成绩和总评

控件命名要能看懂,edtSno、edtPwd、btnLogin、qryLogin比Edit1、Button1好排错。报告里贴界面图时,控件名不用出现,但代码里能对应上。主窗体关闭时把ADOConnection->Connected设为False,避免 Access 文件被占用。

4.2 登录与查询:参数化 SQL 避免拼接

登录查询的 SQL 可以写成SELECT Sno, Sname FROM Student WHERE Sno=:Sno AND Pwd=:Pwd。参数用ParamByName赋值,不要用Format拼字符串。下面是 C++ Builder 事件里的写法,关键在Parameters->ParamByName和Eof判断。

// 登录按钮事件:参数化查询,避免拼接 SQL qryLogin->Close(); qryLogin->SQL->Clear(); qryLogin->SQL->Add("SELECT Sno, Sname FROM Student WHERE Sno=:Sno AND Pwd=:Pwd"); qryLogin->Parameters->ParamByName("Sno")->Value = Trim(edtSno->Text); qryLogin->Parameters->ParamByName("Pwd")->Value = Trim(edtPwd->Text); qryLogin->Open(); if (!qryLogin->Eof) { CurrentSno = qryLogin->FieldByName("Sno")->AsString; // 打开主界面,记录当前登录学生 } else { ShowMessage("学号或密码不正确"); }

这段代码的逻辑是先关查询、清 SQL、加 SQL、赋参数、打开结果集。Trim去掉首尾空格,避免用户复制学号时带空格。Eof为假表示查到记录,登录成功;为真表示没有匹配。密码字段在课程设计里可以存明文,但报告里要写“生产环境应存哈希值”,不要把明文密码当成正确设计。查询开课情况的 SQL 用连接查询,把课程名、教师名、教室、人数限制、已选人数一起查出来:

SELECT o.OfferingID, c.Cname, t.Tname, o.Classroom, o.Capacity, o.Selected FROM Offering o INNER JOIN Course c ON o.Cno = c.Cno INNER JOIN Teacher t ON o.Tno = t.Tno WHERE c.Cname LIKE '%' + :Cname + '%' ORDER BY c.Cname

LIKE里的参数写法在 Access ODBC 下可以用%通配符。如果参数为空,可以在代码里判断后换成LIKE '*'或去掉条件。开课查询的结果集绑定到TDBGrid,用户选中一行后把OfferingID存到变量里,供选课按钮使用。不要用表格的行号当开课号,排序一变就行号错位。

4.3 选课与退课:事务里做条件更新

选课是整套系统里最值得写进报告的部分。正确顺序是开事务、检查重复、插入选课记录、条件更新选课人数、判断更新是否成功、提交或回滚。不要在插入前只做一次SELECT COUNT(*)判断容量,因为检查和插入之间可能被别人插队;单机场景虽然并发少,但演示时开两个程序就能复现超选。更稳的做法是把容量条件写进 UPDATE:WHERE OfferingID=:Oid AND Selected < Capacity,更新影响行数为 0 就回滚。

// 选课:事务 + 条件更新,防止超选 conn->BeginTrans(); try { // 先插入选课记录,唯一索引兜底重复选课 qryEnroll->Close(); qryEnroll->SQL->Clear(); qryEnroll->SQL->Add("INSERT INTO Enrollment(Sno, OfferingID) VALUES(:Sno, :Oid)"); qryEnroll->Parameters->ParamByName("Sno")->Value = CurrentSno; qryEnroll->Parameters->ParamByName("Oid")->Value = CurrentOfferingId; qryEnroll->ExecSQL(); // 只有未满员才把已选人数加一 qryEnroll->SQL->Clear(); qryEnroll->SQL->Add("UPDATE Offering SET Selected=Selected+1 " "WHERE OfferingID=:Oid AND Selected < Capacity"); qryEnroll->Parameters->ParamByName("Oid")->Value = CurrentOfferingId; qryEnroll->ExecSQL(); if (qryEnroll->RowsAffected == 1) { conn->CommitTrans(); } else { conn->RollbackTrans(); ShowMessage("人数已满,选课失败"); } } catch (...) { conn->RollbackTrans(); ShowMessage("选课失败,可能重复选课或数据被占用"); }

BeginTrans、CommitTrans、RollbackTrans是 ADOConnection 的事务方法。插入和更新在同一个事务里,任何一步失败都回滚。RowsAffected表示上一条语句影响的行数,等于 1 说明容量条件满足;等于 0 说明已经满员。部分 Access ODBC 驱动下RowsAffected可能返回 -1,这时可以在更新后再查一次Selected和Capacity对比,或者用SELECT COUNT(*)验证。报告里要写清楚你用的判断方式,不要只写“如果满了就提示”。

选课记录表上必须有(Sno, OfferingID)唯一索引,重复选课时 INSERT 会抛异常,进入catch回滚。退课的顺序反过来:先删选课记录,删除影响行数为 1 再把已选人数减一,条件加Selected > 0,防止人数减成负数。

// 退课:先删记录,再减人数,删除失败就不减 conn->BeginTrans(); try { qryEnroll->SQL->Clear(); qryEnroll->SQL->Add("DELETE FROM Enrollment WHERE Sno=:Sno AND OfferingID=:Oid"); qryEnroll->Parameters->ParamByName("Sno")->Value = CurrentSno; qryEnroll->Parameters->ParamByName("Oid")->Value = CurrentOfferingId; qryEnroll->ExecSQL(); if (qryEnroll->RowsAffected == 1) { qryEnroll->SQL->Clear(); qryEnroll->SQL->Add("UPDATE Offering SET Selected=Selected-1 " "WHERE OfferingID=:Oid AND Selected > 0"); qryEnroll->Parameters->ParamByName("Oid")->Value = CurrentOfferingId; qryEnroll->ExecSQL(); conn->CommitTrans(); } else { conn->RollbackTrans(); ShowMessage("没有找到对应选课记录"); } } catch (...) { conn->RollbackTrans(); ShowMessage("退课失败"); }

退课的业务规则要补一条:已经录入成绩的选课记录不允许退课。可以在 DELETE 前查成绩字段是否为空,或者在 SQL 里加AND FinalScore IS NULL。课程设计不要求做权限系统,但报告里写清这条规则,状态图里的“已出成绩不能退课”就有依据。

4.4 成绩与总评:权重参数写进配置而不是硬编码

成绩查询按学号查,SQL 连接选课表、开课表、课程表,显示课名、学分、平时成绩、考试成绩、总评成绩。总评成绩的计算权重常见做法是平时 30%、考试 70%,但指导书没有写死,报告里要写明采用的权重。不要把0.3和0.7散落在三个地方,定义一个常量或配置项,录入成绩和查询成绩共用。

SELECT c.Cname, c.Credit, e.UsualScore, e.ExamScore, e.FinalScore FROM Enrollment e INNER JOIN Offering o ON e.OfferingID = o.OfferingID INNER JOIN Course c ON o.Cno = c.Cno WHERE e.Sno = :Sno ORDER BY c.Cname

录入成绩时先更新平时成绩和考试成绩,再计算总评写回。总评可以写成UsualScore * 0.3 + ExamScore * 0.7,保留一位小数。如果平时成绩或考试成绩为空,总评也置空,不要按 0 计算。查询成绩界面里空值显示为“未录入”,比显示 0 更符合教学场景。成绩录入权限只给教师角色,学生界面只读,报告里的用例图要把这条权限边界画出来。

5. 避坑与排查:旧工具链里最容易翻车的 5 个现场

旧工具链的问题有很强的现场性,代码没错、配置没错,结果就是连不上或者写不进。下面 5 条是课程设计里出现频率很高的现场,按“现象→原因→解决”记录,答辩前照着排一遍,比重新装环境省时间。

5.1 数据源连不上:先查位数和名称,不要重装 C++ Builder

现象:ODBC 管理器里测试连接成功,C++ Builder 的 ADOConnection 点测试连接报“未发现数据源名称并且未指定默认驱动程序”。原因通常有两个:一是 C++ Builder 6.0 是 32 位程序,DSN 却建在 64 位 ODBC 管理器里;二是 DSN 名带全角空格或大小写不一致,连接串里的Data Source和实际名称不完全匹配。解决:打开 32 位 ODBC 管理器重建同名 DSN,复制数据源名到连接串,不要手打;在 ADOConnection 的 Build 对话框里直接下拉选择 DSN,让 IDE 生成连接串。

另一个现象是 BDE Administrator 里 Open 字体变黑,程序仍然连不上。原因是 BDE 别名和 ADOConnection 用的 ODBC DSN 是两套东西,BDE 能打开不代表 ADO 能打开。解决:把 BDE 那一步当作过程记录,真正连接以 ADOConnection 的测试连接为准;LoginPrompt设为False,避免弹框被取消后连接处于半开状态。这类问题看着玄学,查位数和名称基本能定位。

5.2 选课超限与重复记录:唯一索引和事务缺一不可

现象:人数限制 30,选课人数显示 31;或者同一学生同一门课出现两条选课记录。原因:先SELECT COUNT(*)判断人数,再 INSERT,检查和写入之间没有锁;选课表没有唯一索引,重复提交按钮就插两条。解决:给选课表建(Sno, OfferingID)唯一索引;选课放进事务,用UPDATE Offering SET Selected=Selected+1 WHERE OfferingID=:Oid AND Selected<Capacity做条件更新;更新影响行数不为 1 就回滚。按钮点击后先禁用,等事务结束再恢复,防止用户连点。

还有一个现象是退课以后人数不减,或者减成负数。原因是删除选课记录和更新人数不在一个事务里,或者更新条件没加Selected>0。解决:删除影响行数为 1 才执行减一,减一条件加Selected>0,两步放在同一事务。选课人数是冗余字段,所有写入路径都必须维护它,报告里要写清哪些操作会改这个字段。

5.3 中文、日期、空值:三类字段最容易把数据写坏

现象:学生姓名显示成问号,出生年月变成 1900-01-01,成绩为空时总评变成 0。原因:Access 字段文本长度不够,姓名被截断;日期用字符串拼接,区域格式和数据库格式不一致;空值参与算术运算被当成 0。解决:姓名字段至少 20 位,地址 50 位;日期用参数化查询,参数类型设为日期,不要拼yyyy/mm/dd字符串;总评计算前判断平时成绩和考试成绩是否为空,为空就保持空值。

中文乱码还可能来自连接串或控件字体,但课程设计里更常见的是字段长度和字符集。Access 2000 的文本字段默认长度 50,姓名一般够用,课程名和教室名要检查。导入数据时先用少量中文测试,确认写入后查询、表格显示、报告截图都正常。日期字段在 Access 里显示格式可以设成yyyy-mm-dd,但底层存储是日期值,参数化写入最稳。

5.4 报告与代码两张皮:测试序列要带前置状态

现象:报告用例图里有“管理员审核选课”,代码里根本没有这个功能;序列图画了五个生命线,程序里只有一个窗体。原因:先写报告后改代码,或者照着模板抄图,没有按实际实现回填。解决:以数据库表和代码为准回填图,删掉没实现的用例,补上实际存在的失败分支;报告里的每个图下面写一句“对应代码位置/表名”,答辩时能指出来。测试部分不要只写输入输出,要写前置状态、输入对象、执行动作、预期状态、清理动作。

现象:测试数据只有“输入正确密码登录成功”和“输入错误密码登录失败”两条。原因是把测试当成功能演示。解决:按对象状态迁移设计 20 条不同对象测试序列,登录、查询、选课、退课、成绩各覆盖成功和失败分支,选课要覆盖重复选课、容量边界、退课后重选。测试序列编号和报告里的截图对应,评审时

本文还有配套的精品资源,点击获取