news 2026/10/12 1:18:42

数据库课程设计实战:通讯录管理系统从E-R图到Java联调全链路拆解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数据库课程设计实战:通讯录管理系统从E-R图到Java联调全链路拆解

简介:这份《通讯录管理系统数据库课程设计报告》面向高校数据库原理与应用课程的选课学生,帮助完成从需求分析到运行维护的完整课程设计任务。资源包内含1个docx文档,压缩包约840KB,以课程设计报告为主体,涵盖摘要、绪论、需求分析、概念结构设计、逻辑设计、数据库实施、运行维护与总结等章节,并附任务分配表与参考文献。报告以SQL Server为存储、JAVA构建界面,完整呈现用户登录、联系人增删改查、分组管理等功能的设计思路,包含数据流图、数据字典、E-R图、关系模式优化及建库建表、视图、存储过程等SQL代码,还配有登录、联系人、分组与查询等界面设计说明。已有74人学习,适合需要参考数据库课程设计框架、撰写规范报告或梳理设计流程的读者,可据此理解数据库设计各阶段的方法与文档组织方式。

1. 从一份课设报告说起:通讯录管理系统到底能跑通什么

很多人看到「通讯录管理系统数据库课程设计报告.docx」这个文件名,第一反应是「又是一份交完就删的作业」。但我实际拆过这份报告之后发现,它完整走了一遍数据库应用开发的标准链路:需求分析、数据流图、数据字典、概念结构设计、E-R 图、逻辑设计、范式优化、建库建表、视图与存储过程、再到 Java 界面联调。这套流程放到今天任何一个 CRUD 类业务系统上,骨架都是一样的。

这份资源的核心价值不在于通讯录本身有多复杂,而在于它把「数据库原理与应用」这门课里那些抽象概念——函数依赖、第三范式、外键约束、视图、存储过程——全部落到了一个能编译、能建库、能点按钮的具体系统上。适合两类人:一是正在做数据库课程设计、需要一份可参照的完整实现思路的同学;二是工作后要补数据库设计基本功、想找一个体量适中的练手项目的开发者。它用的是 SQL Server + Java 这套经典组合,技术栈不新,但胜在链路完整、坑点真实。

2. 需求分析与概念设计:把通讯录拆成三张表和一组 E-R 图

2.1 功能边界怎么划:六个核心操作与三类分组

这份报告在需求分析阶段把系统功能收敛得很干净,一共六个操作:用户登录、添加联系人、修改联系人、删除联系人、查询联系人、浏览全部联系人。分组维度上,它把联系人分成朋友、同学、同事三类,每类有独立的查询界面。这个划分看起来简单,但它决定了后面表结构的设计方向。

我一般会建议在动手画 E-R 图之前,先把「谁操作什么数据」列清楚。这份报告的数据流图里,顶层是「用户登录系统」,0 层展开后分成联系人信息存储和分组信息存储两条线。这个拆法是对的,因为登录态和业务数据是两套生命周期——用户表管认证,联系人表管业务,分组表管分类。三者通过外键关联,而不是揉在一张宽表里。

数据字典部分给出了关键字段的类型和长度约定,我整理成表格方便对照:

数据项类型长度约束说明
用户编号Char10主键,非空登录唯一标识
密码Char10非空登录凭证
联系人编号Char50主键,非空联系人唯一标识
姓名Char10非空联系人姓名
性别Char10可空联系人性别
电话号码Char50可空联系方式
出生日期Char50可空日期字段
E-mailChar10可空电子邮箱
分组类别Char10可空朋友/同学/同事

这里有个细节值得注意:出生日期用的是 Char 类型而不是 Date。这在课设里很常见,好处是省去了日期格式转换的麻烦,坏处是无法做日期范围查询和排序。如果你要在这个基础上做扩展,比如「查询本月过生日的联系人」,就得先把字段改成 Date 或者 Datetime,否则字符串比较会出玄学问题。

2.2 E-R 图到关系模式:三个实体、两组联系

概念结构设计部分定义了三个实体:用户登录信息、联系人详细信息、分组信息。实体间的联系有两组:用户与联系人是多对多(一个用户可以有多个联系人,一个联系人理论上可以被多个用户引用),联系人与分组是一对多(一个联系人属于一个分组,一个分组包含多个联系人)。

E-R 图转关系模式的时候,报告给出的结果是:

  • 联系人信息(编号,姓名,性别,出生日期,电话号码,E-mail,地址,分组类别),主键为编号
  • 朋友分组信息(朋友编号,姓名),外键为朋友编号
  • 同学分组信息(同学编号,姓名),外键为朋友编号

这里有个明显的设计问题:朋友分组表和同学分组表的结构完全一样,都是「编号 + 姓名」,完全可以用一张分组表加一个「分组类型」字段来替代。报告里把它们拆成两张独立表,好处是查询时不用过滤类型字段,坏处是每加一种分组就要新建一张表,扩展性很差。如果你要复现这个项目,我建议合并成一张ContactGroup表,字段为group_id、group_name、group_type,用group_type区分朋友、同学、同事。

范式优化部分,报告论证了三个关系模式都满足第三范式:所有属性都是简单属性(第一范式),不存在部分函数依赖(第二范式),不存在传递依赖(第三范式)。这个论证过程是标准的课设写法,逻辑上没问题。但实际开发中,有时候为了查询性能会故意反范式,比如在联系人表里冗余一个「分组名称」字段,避免每次查询都 join 分组表。课设追求范式,工程追求平衡,这个边界要清楚。

3. 建库建表与存储过程:SQL Server 脚本逐段拆解

3.1 数据库创建:文件组、初始大小与增长策略

报告第五章给出了完整的建库脚本,我把它整理成可直接执行的版本,并补上注释:

-- 创建通讯录管理系统数据库 CREATE DATABASE 通讯录管理系统 ON PRIMARY ( NAME = '通讯录管理系统', FILENAME = 'd:\数据库\通讯管理系统.mdf', SIZE = 10MB, MAXSIZE = 100MB, FILEGROWTH = 20% ) LOG ON ( NAME = '通讯录管理系统_log', FILENAME = 'd:\数据库\通讯录管理系统.ldf', SIZE = 20MB, MAXSIZE = 100MB, FILEGROWTH = 20% );

这段脚本的关键参数有三个。SIZE = 10MB是数据文件初始大小,课设数据量小,10MB 足够;MAXSIZE = 100MB是上限,防止文件无限增长占满磁盘;FILEGROWTH = 20%是每次自动增长的比例,按百分比增长比按固定 MB 增长更适应数据量变化。日志文件初始给 20MB,比数据文件大,因为插入和修改操作会先写日志。

有个坑要注意:FILENAME里的路径d:\数据库\必须提前手动创建,SQL Server 不会自动建目录。如果路径不存在,建库语句会直接报错「操作系统错误 3(系统找不到指定的路径)」。另外,如果 SQL Server 服务账户没有 D 盘的写权限,也会失败。我一般会先把路径改成 SQL Server 默认的数据目录,或者提前用mkdir建好并确认权限。

3.2 三张核心表的建表语句与约束设计

报告里给出了用户表、联系人表、分组表的建表代码。我按它的字段设计整理成可执行版本:

-- 用户表:存储登录凭证 CREATE TABLE 用户表 ( 用户编号 CHAR(10) NOT NULL PRIMARY KEY, 密码 CHAR(10) NOT NULL ); -- 联系人表:存储联系人详细信息 CREATE TABLE 联系人表 ( 联系人编号 CHAR(50) NOT NULL PRIMARY KEY, 姓名 CHAR(10) NOT NULL, 性别 CHAR(10) NULL, 电话号码 CHAR(50) NULL, 出生日期 CHAR(50) NULL, Email CHAR(50) NULL, 地址 CHAR(50) NULL, 分组类别 CHAR(10) NULL ); -- 分组表:存储分组信息 CREATE TABLE 分组表 ( 分组编号 CHAR(10) NOT NULL PRIMARY KEY, 分组名称 CHAR(20) NOT NULL, 分组类型 CHAR(10) NOT NULL );

建表时有几个参数选择需要说明。CHAR是定长字符串,存不足长度会用空格补齐,查询时可能遇到「明明值一样但比较不相等」的情况。比如CHAR(10)存「张三」会变成「张三 」,如果和VARCHAR的「张三」比较就会出问题。课设里统一用CHAR问题不大,但生产环境建议姓名、地址这类变长字段用VARCHAR。

主键选择上,联系人编号用CHAR(50)偏长,如果编号是自增数字,用INT IDENTITY(1,1)更省空间且插入性能更好。报告里用CHAR(50)可能是为了兼容学号或手机号作为编号,这个要看具体业务。外键约束在报告里没有显式写出,但分组表和联系人表之间应该加一个:

ALTER TABLE 联系人表 ADD CONSTRAINT FK_联系人_分组 FOREIGN KEY (分组类别) REFERENCES 分组表(分组类型);

加上外键之后,删除分组时会检查是否有联系人引用该分组,避免出现「孤儿联系人」。这是数据完整性的一道保险,课设里经常被忽略,但实际项目里必须加。

3.3 视图与存储过程:把查询逻辑封装进数据库

报告提到了视图和存储过程的创建,但正文里没有给出完整代码。按这个系统的功能,我补全两个最常用的:

-- 视图:按分组查询联系人摘要 CREATE VIEW v_联系人分组摘要 AS SELECT c.分组类别, c.联系人编号, c.姓名, c.电话号码 FROM 联系人表 c WHERE c.分组类别 IS NOT NULL; -- 存储过程:按姓名模糊查询联系人 CREATE PROCEDURE sp_查询联系人 @关键字 NVARCHAR(20) AS BEGIN SELECT 联系人编号, 姓名, 性别, 电话号码, Email, 分组类别 FROM 联系人表 WHERE 姓名 LIKE '%' + @关键字 + '%'; END;

视图的作用是把常用的查询逻辑固化下来,Java 端直接SELECT * FROM v_联系人分组摘要就行,不用每次写 join。存储过程sp_查询联系人接收一个关键字参数,用LIKE做模糊匹配。调用方式是EXEC sp_查询联系人 @关键字 = '张'。

参数@关键字用NVARCHAR而不是VARCHAR,是为了支持中文。如果用VARCHAR,中文可能变成乱码。LIKE '%' + @关键字 + '%'的写法要注意 SQL 注入风险,不过在存储过程里参数是强类型的,风险比拼接字符串低很多。如果要做分页查询,可以再加OFFSET和FETCH NEXT,但 SQL Server 2012 才支持这个语法,2008 需要用ROW_NUMBER()绕。

4. Java 界面与数据库联调:登录、增删改查的落地细节

4.1 JDBC 连接配置:驱动、URL 与登录验证

Java 端连接 SQL Server 需要 JDBC 驱动。报告里没有写具体版本,常见做法是用sqljdbc4.jar或mssql-jdbc。连接代码如下:

// 数据库连接工具类 public class DBUtil { private static final String DRIVER = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; private static final String URL = "jdbc:sqlserver://localhost:1433;databaseName=通讯录管理系统"; private static final String USER = "sa"; private static final String PASSWORD = "你的密码"; public static Connection getConnection() throws Exception { Class.forName(DRIVER); return DriverManager.getConnection(URL, USER, PASSWORD); } }

URL里的databaseName必须和建库时的数据库名完全一致,包括中文。localhost:1433是默认端口,如果 SQL Server 配置里改了端口,这里要同步改。sa是超级管理员账户,课设里常用,但生产环境应该用独立的应用账户并限制权限。

登录验证的 SQL 是:

String sql = "SELECT * FROM 用户表 WHERE 用户编号 = ? AND 密码 = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, userId); ps.setString(2, password); ResultSet rs = ps.executeQuery(); if (rs.next()) { // 登录成功,跳转主界面 }

用PreparedStatement而不是Statement拼接字符串,是为了防 SQL 注入。?是占位符,setString按顺序填充。如果用户输入' OR '1'='1,拼接写法会直接绕过验证,而PreparedStatement会把它当成普通字符串处理。

4.2 增删改查的 SQL 实现与界面绑定

添加联系人的核心 SQL:

String sql = "INSERT INTO 联系人表 (联系人编号, 姓名, 性别, 电话号码, 出生日期, Email, 地址, 分组类别) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, contactId); ps.setString(2, name); ps.setString(3, gender); ps.setString(4, phone); ps.setString(5, birthday); ps.setString(6, email); ps.setString(7, address); ps.setString(8, groupType); ps.executeUpdate();

修改和删除类似,把INSERT换成UPDATE或DELETE,WHERE条件用联系人编号。查询全部联系人的 SQL 是SELECT * FROM 联系人表,按分组查询是SELECT * FROM 联系人表 WHERE 分组类别 = ?。

界面绑定上,报告里分了登录模块、联系人界面、朋友分组界面、同学分组界面、同事分组界面、查询界面、增加界面、修改界面、删除界面。这个拆分粒度偏细,实际开发中可以用一个主界面加 Tab 页或者下拉筛选来替代多个独立窗口。但课设要求展示完整功能,拆细一点反而方便演示每个操作。

有个容易翻车的地方:Java 的JTextField获取输入后,如果用户没填某个可选字段,getText()返回空字符串"",直接传给setString会插入空串而不是NULL。如果数据库字段允许NULL且业务上要区分「未填」和「填了空」,需要在 Java 端做判断,空串时用ps.setNull(index, Types.CHAR)。

5. 避坑与排查:课设里最容易翻车的五个点

5.1 中文乱码:数据库排序规则与 JDBC 编码不一致

现象:插入中文联系人姓名后,查询出来显示成???或者乱码。

原因:SQL Server 数据库的排序规则(Collation)默认可能是SQL_Latin1_General_CP1_CI_AS,不支持中文存储。或者 JDBC URL 没有指定编码,Java 字符串传到数据库时编码转换出错。

解决:建库时指定排序规则COLLATE Chinese_PRC_CI_AS,或者在连接 URL 后加;sendStringParametersAsUnicode=true。字段类型用NVARCHAR而不是VARCHAR,Java 端用setNString代替setString。

5.2 端口不通:SQL Server 配置管理器里 TCP/IP 没启用

现象:Java 程序报Connection refused或The TCP/IP connection to the host localhost, port 1433 has failed。

原因:SQL Server 默认安装后,TCP/IP 协议可能是禁用状态,只启用了共享内存或命名管道。Java JDBC 走的是 TCP/IP。

解决:打开 SQL Server 配置管理器,展开「SQL Server 网络配置」→「实例的协议」,把 TCP/IP 状态改为「已启用」,然后重启 SQL Server 服务。如果端口不是 1433,在 TCP/IP 属性里查看 IP 地址页签的 TCP 端口。

5.3 主键冲突:联系人编号重复导致插入失败

现象:添加联系人时抛出Violation of PRIMARY KEY constraint。

原因:联系人编号是主键,必须唯一。如果用户手动输入编号,很容易重复。或者程序里用时间戳生成编号,高并发下可能撞车。

解决:课设场景下,可以在插入前先SELECT COUNT(*) FROM 联系人表 WHERE 联系人编号 = ?检查是否存在。更稳妥的做法是用IDENTITY自增列作为主键,编号由数据库生成,Java 端插入时不传该字段。

5.4 外键约束:删除分组时被联系人表引用

现象:删除分组时报The DELETE statement conflicted with the REFERENCE constraint。

原因:联系人表的分组类别字段引用了分组表,直接删除分组会导致联系人失去分类。

解决:先删除或更新引用该分组的所有联系人,再删除分组。或者在建外键时加ON DELETE SET NULL,删除分组时自动把联系人的分组类别置空。但SET NULL要求外键字段允许为空。

5.5 日期格式:Char 类型存储导致的查询异常

现象:按出生日期范围查询时,结果不符合预期,比如'1990-01-01'到'1999-12-31'查不到'1995-6-15'。

原因:出生日期用CHAR(50)存储,格式不统一。'1995-6-15'和'1995-06-15'字符串比较时,'6'大于'0',排序和范围判断都会出错。

解决:把出生日期字段改成DATE或DATETIME类型,Java 端用setDate传入java.sql.Date。如果必须用字符串,强制统一格式为yyyy-MM-dd,并在插入前用SimpleDateFormat格式化。

6. 从课设到可复用:把通讯录系统改造成通用 CRUD 模板

这份课设最大的价值不是通讯录本身,而是它验证了一套「数据库设计 → SQL 实现 → Java 联调」的完整流程。如果你把这套流程抽象出来,换掉实体和字段,就能快速套到其他管理系统上,比如图书管理、学生成绩管理、库存管理。

我一般会做三个改造。第一,把三张表的分组设计合并成一张Group表加一个type字段,这样加新分组不用改表结构。第二,把增删改查的 SQL 封装成一个泛型 DAO 基类,用反射获取实体字段,自动生成INSERT、UPDATE、DELETE语句,子类只需要定义表名和字段映射。第三,把登录验证从明文密码改成 MD5 或 BCrypt 哈希,数据库里存哈希值而不是明文。

验证改造是否成功的方法很简单:新建一个「图书管理」实体,继承 DAO 基类,看能不能在不写新 SQL 的情况下完成增删改查。如果能跑通,说明抽象层是有效的。如果报错,检查字段名和表名的映射关系,以及主键生成策略是否兼容。

// 泛型 DAO 基类示意 public abstract class BaseDao<T> { protected abstract String getTableName(); protected abstract Map<String, Object> toMap(T entity); public void insert(T entity) throws Exception { Map<String, Object> map = toMap(entity); String columns = String.join(",", map.keySet()); String placeholders = map.keySet().stream().map(k -> "?").collect(Collectors.joining(",")); String sql = "INSERT INTO " + getTableName() + " (" + columns + ") VALUES (" + placeholders + ")"; // 执行插入... } }

这个基类的关键是toMap方法,子类负责把实体字段转成「列名 → 值」的映射。getTableName返回表名。插入时动态拼 SQL,占位符数量由字段数量决定。这样加新实体只需要写一个子类,不用重复写 JDBC 模板代码。

从那以后我每次做课设或者小工具,都会先把数据库设计跑一遍范式检查,再写一个最小可用的 DAO 基类,最后才做界面。这样即使界面翻车,底层数据操作也是稳的。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 1:17:36

springboot毕设选题露营地管理系统设计与实现

&#x1f345;选题推荐——以防找不到我们&#xff0c;点击上方订阅专栏✌✌\ Java毕设实战项目 Python毕设项目源代码 asp.net毕业设计项目 Uniapp安卓毕业设计项目 node.js毕业设计项目 python毕业设计 微信小程序毕业设计项目 php毕业设计 &#x1f447;&#x1f3fb;&#…

作者头像 李华
网站建设 2026/10/12 1:17:18

时间轴+流程图PPT模板:逻辑可视化与高效修改指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:17:12

CATIA参考元素本质:点线面是空间坐标锚点而非绘图标记

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:16:09

天龙八部私服MySQL 5.0.45源码编译实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:15:45

数据库系统概论期末试卷解析:从三级模式到BCNF分解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 1:15:31

深度强化学习驱动AUV三维动态避障:从PPO训练到实机部署实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华