1. 为什么选择Java Swing和MVC架构做学生管理系统?
十年前我刚入行时接到的第一个正式项目,就是给本地一所职业院校开发学生信息管理系统。当时Node.js还没兴起,Python在桌面应用领域生态薄弱,而Java Swing凭借其跨平台特性和成熟的组件库,成为教育领域桌面开发的实际标准选择。
MVC(Model-View-Controller)架构的引入则解决了早期学生管理系统常见的代码混乱问题。记得2015年我接手维护过一个VB6写的学生管理系统,所有业务逻辑、界面更新和数据库操作都混在Form模块里,新增一个"班级人数统计"功能居然要修改7个不同位置的代码。采用MVC后,数据管理、界面展示和业务控制彻底解耦,现在给系统添加新功能模块平均只需新增3个标准类文件。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统核心模块设计
2.1 模型层(Model)的领域建模
学生管理系统的核心模型类包括:
- Student类:包含学号、姓名、性别等基础字段
- 特别注意学号生成规则:我们采用"入学年份(4位)+院系代码(2位)+序号(4位)"的10位编码方案,在构造函数中通过SimpleDateFormat自动生成年份部分
java复制// 学号生成示例代码
public Student(String departmentCode) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy");
this.admissionYear = sdf.format(new Date());
this.studentId = admissionYear + departmentCode + String.format("%04d", generateSequence());
}
- Course类:使用Enum定义课程类型(必修/选修)
- Score类:处理成绩与学生的关联关系,这里采用组合模式而非继承
2.2 视图层(View)的Swing实现技巧
主界面采用JTabbedPane组织不同功能模块,每个标签页对应一个JPanel。关键技巧包括:
- 表格数据显示:继承AbstractTableModel实现自定义Model,比直接使用DefaultTableModel更灵活
java复制class StudentTableModel extends AbstractTableModel {
private List<Student> data;
private String[] columnNames = {"学号", "姓名", "性别"};
@Override
public int getRowCount() {
return data.size();
}
// 其他必须实现的方法...
}
- 表单验证:对JTextField使用DocumentListener实现实时校验
java复制nameField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
validateName();
}
// 其他事件方法...
});
- 使用MigLayout替代默认布局管理器,解决复杂表单对齐难题
2.3 控制层(Controller)的业务逻辑
采用命令模式封装各类操作,例如AddStudentCommand包含:
- execute()方法:调用Model层保存数据
- undo()方法:支持操作回滚
- 通过CommandManager实现操作历史记录
特别要注意事务处理:
java复制public class AddStudentCommand implements Command {
public void execute() {
try {
connection.setAutoCommit(false);
studentDAO.save(student);
logDAO.recordOperation("add");
connection.commit();
} catch (SQLException e) {
connection.rollback();
}
}
}
3. 数据库设计与优化
3.1 表结构设计
学生表(student)核心字段:
sql复制CREATE TABLE student (
id VARCHAR(10) PRIMARY KEY,
name VARCHAR(20) NOT NULL,
gender ENUM('M','F') DEFAULT 'M',
birth_date DATE,
INDEX idx_name (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
成绩表(score)特别注意:
- 使用decimal(5,2)存储分数
- 建立(student_id, course_id)联合主键
- 添加外键约束
3.2 连接池配置
推荐使用HikariCP而非DriverManager直接获取连接:
java复制HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/student_db");
config.setUsername("root");
config.setPassword("123456");
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
HikariDataSource ds = new HikariDataSource(config);
4. 典型功能实现详解
4.1 学生信息导出Excel
使用Apache POI时要注意:
- 创建Workbook时区分.xls和.xlsx格式
- 使用SXSSFWorkbook处理大数据量
- 设置正确的单元格格式(特别是学号这类长数字)
java复制SXSSFWorkbook workbook = new SXSSFWorkbook(100); // 保持100行在内存中
Sheet sheet = workbook.createSheet("学生名单");
// 设置标题行样式
CellStyle headerStyle = workbook.createCellStyle();
Font headerFont = workbook.createFont();
headerFont.setBold(true);
headerStyle.setFont(headerFont);
4.2 统计图表生成
使用JFreeChart实现时要注意:
- 在Swing线程外生成图表图像
- 使用ChartPanel的setMaximumDrawWidth/Height控制显示尺寸
- 缓存已生成的图表对象
java复制private void updateChart() {
SwingWorker<JFreeChart, Void> worker = new SwingWorker<>() {
protected JFreeChart doInBackground() {
return ChartFactory.createBarChart(...);
}
protected void done() {
chartPanel.setChart(get());
}
};
worker.execute();
}
5. 开发中的坑与解决方案
5.1 Swing线程问题
典型错误:在事件监听器中直接执行耗时操作导致界面冻结
java复制// 错误示例
searchButton.addActionListener(e -> {
List<Student> results = dao.search(nameField.getText()); // 阻塞EDT
updateTable(results);
});
正确做法:
java复制searchButton.addActionListener(e -> {
new SwingWorker<List<Student>, Void>() {
protected List<Student> doInBackground() {
return dao.search(nameField.getText());
}
protected void done() {
updateTable(get());
}
}.execute();
});
5.2 数据库连接泄露
务必使用try-with-resources确保关闭资源:
java复制try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(sql)) {
// 操作数据库...
} // 自动关闭conn和stmt
6. 项目部署与打包
6.1 使用Maven Assembly插件打包
在pom.xml中配置:
xml复制<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>com.example.StudentSystemMain</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
6.2 处理跨平台字体问题
在启动脚本中添加字体配置:
java复制public static void setUIFont() {
Font font = new Font("Microsoft YaHei", Font.PLAIN, 12);
Enumeration<Object> keys = UIManager.getDefaults().keys();
while (keys.hasMoreElements()) {
Object key = keys.nextElement();
if (UIManager.get(key) instanceof Font) {
UIManager.put(key, font);
}
}
}
在实际开发中,我发现使用JFrame的setDefaultLookAndFeel()并不能完全解决Linux下的显示异常,最终采用直接替换所有UI默认字体的方案才彻底解决问题。另外,将JDK与JRE版本锁定为1.8可以避免高版本Java在Swing渲染上的兼容性问题。
