ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java课设实战:宠物领养系统中Swing与MySQL的整合开发

Java课设实战:宠物领养系统中Swing与MySQL的整合开发 简介这套宠物领养系统属于Java课程设计级别的完整实践项目基于Swing图形界面与MySQL数据库开发面向高校Java初学者或需要完成课设的学生可用于理解桌面应用从界面搭建到数据持久化的全过程。资源包共84个文件涵盖java源码、class编译产物、png界面截图、xml配置、sql建表脚本、docx课设报告及mp4演示视频等整体33.13MB目录结构直观便于导入开发环境直接运行和查阅。当前已有502人学习下载。除可运行的完整系统外数据库脚本能帮助快速还原表结构课设报告则覆盖系统设计思路、功能实现与测试说明演示视频清晰展示运行效果与主要操作流程对课程设计答辩准备、项目二次开发以及Java桌面应用入门学习都具有实用参考价值。整体内容贴合Java课设的实际需求能够覆盖从代码实现到报告撰写的核心环节。1. 这门Java课设为什么值得认真做宠物领养系统几乎是Java课程设计里出镜率最高的题目之一但它远没有表面看起来那么“模板化”。这个题目要求你同时处理Swing GUI的事件模型、MySQL的数据持久化、JDBC的事务边界以及最容易被忽略的——领养业务本身的状态流转。很多人的课设止步于“能增删改查”但答辩时老师真正追问的往往是“领养申请被拒绝后宠物状态怎么恢复”“两个人同时申请同一只宠物怎么办”。这两个问题背后恰好是Java课设里最能拉开分差的设计点。本文按照“技术选型 → 数据库建模 → GUI实现 → 联调部署”的顺序展开每一章都会给出可以直接运行的代码和可以照抄的参数配置。适合正在做课设的在校生也适合想快速回顾SwingMySQL这套经典组合的开发者。标题里的Swing和MySQL是两条主线我会把这两条线讲透而不是停留在“用JDBC连了个库”的层面。2. Swing与MySQL的选型逻辑和开发环境搭建2.1 为什么课设场景下Swing依然是最稳的选择如果你在犹豫要不要用JavaFX替代Swing我的建议是课设场景下Swing的稳定性是压倒性的优势。JavaFX虽然界面更现代但它在模块化JDK下的配置过程相对繁琐而且很多学校的实验环境仍然停留在JDK 8。Swing从JDK 1.2开始就是标准组件在任何JDK版本下都能直接运行不存在额外的依赖注入问题。另一个被忽视的优点是Swing的调试成本低。Swing的UI组件是纯Java实现不像JavaFX需要理解场景图Scene Graph和属性绑定机制。对于课设这种“功能正确优先”的场景Swing的事件监听模型EventListener反而更直观按钮被点击就触发actionPerformed表格选中行就触发valueChanged逻辑链路清晰答辩时也更容易讲明白。换成JavaFX当然也可以但你需要额外承担CSS样式调试、并发线程更新UI的限制Platform.runLater等学习成本。这些成本对于课设来说属于“非必要支出”。如果你以后找工作面试被问到桌面端技术Swing的经历同样能证明你对事件分发、UI线程模型有基本认知。2.2 环境变量和JDBC驱动版本匹配开发环境推荐JDK 8 MySQL 5.7或8.0的组合。JDK 8是Swing最成熟的运行环境MySQL 8.0的驱动类名和5.7有差异这个细节决定了你的Class.forName能不能写对。# 检查JDK版本 java -version # 检查MySQL版本 mysql --version如果你是JDK 8使用mysql-connector-java 5.1.49版本驱动驱动类名是com.mysql.jdbc.Driver。如果是JDK 11及以上配MySQL 8.0使用mysql-connector-j 8.0.x驱动类是com.mysql.cj.jdbc.Driver。这个对应关系几乎是你联调时遇到ClassNotFoundException或SQLException: No suitable driver found的第一排查点。环境变量方面需要确认JAVA_HOME指向JDK安装根目录PATH里包含%JAVA_HOME%\bin。MySQL安装完成后需要把MySQL的bin目录例如C:\Program Files\MySQL\MySQL Server 8.0\bin加入PATH否则命令行里执行mysql -u root -p会提示找不到命令。驱动jar包不要放在JDK的lib目录下正确的做法是在IDE里作为项目库导入或者放在项目根目录的lib文件夹中手动引入classpath。2.3 创建项目骨架和依赖目录结构我一般会先建好目录结构再写代码这样后续无论是打包提交还是扩展功能都不会乱。下面是一个可以直接照用的Maven风格目录结构即使你不用Maven也建议保持这个分层习惯。pet-adoption/ ├── src/ │ ├── main/ │ │ ├── java/com/petadoption/ │ │ │ ├── view/ // Swing界面类 │ │ │ ├── controller/ // 事件处理 │ │ │ ├── dao/ // 数据访问层 │ │ │ ├── model/ // 实体类Pet.java, Adopter.java │ │ │ └── util/ // DBUtil.java, 常量类 │ │ └── resources/ │ │ └── db.properties // 数据库连接配置 │ └── test/ └── lib/ └── mysql-connector-java-8.0.33.jar如果你用IDEAFile → Project Structure → Libraries 里把lib目录下的驱动jar添加进去即可。db.properties的好处是数据库密码改版时不需要重新编译这也是答辩时可以主动提的一个“工程化细节”。3. MySQL数据库设计与建表语句3.1 核心业务表宠物表、领养人表、领养记录表数据库设计是整个系统的地基。很多课设只做了宠物表和领养人表把领养记录揉在业务逻辑里用List保存程序一重启数据就全丢了。正确的做法是至少三张表宠物表记录宠物自身的属性领养人表记录申请人信息领养记录表则是一张关联表记录“谁在什么时候申请了哪只宠物”。CREATE DATABASE IF NOT EXISTS pet_adoption DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE pet_adoption; CREATE TABLE pet ( pet_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 宠物ID, name VARCHAR(50) NOT NULL COMMENT 宠物名称, species VARCHAR(20) NOT NULL COMMENT 物种狗/猫/兔子等, breed VARCHAR(50) COMMENT 品种, age INT COMMENT 年龄单位月, gender ENUM(公, 母) DEFAULT 公, health_status VARCHAR(100) DEFAULT 健康 COMMENT 健康状况描述, status TINYINT DEFAULT 0 COMMENT 0-待领养 1-已预订 2-已领养, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;status字段我用TINYINT而不是VARCHAR来保存状态原因是数字比较比字符串快而且可以配合Java枚举做映射。update_time的ON UPDATE CURRENT_TIMESTAMP是MySQL 5.7的特性如果MySQL版本低于5.7这条语句会报语法错误直接去掉即可。领养人表要注意手机号字段用VARCHAR(20)而不是BIGINT因为手机号可能存在前导零或扩展位的场景一旦用数字类型这些信息会丢失。邮箱字段建议加唯一索引防止同一邮箱重复注册。CREATE TABLE adopter ( adopter_id INT AUTO_INCREMENT PRIMARY KEY, real_name VARCHAR(50) NOT NULL, phone VARCHAR(20) NOT NULL, email VARCHAR(100), address VARCHAR(200), id_card VARCHAR(18) COMMENT 身份证号用于资格验证, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE adoption_record ( record_id INT AUTO_INCREMENT PRIMARY KEY, pet_id INT NOT NULL, adopter_id INT NOT NULL, apply_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, status TINYINT DEFAULT 0 COMMENT 0-审核中 1-已通过 2-已拒绝 3-已完成, remark VARCHAR(255) COMMENT 审核意见, CONSTRAINT fk_record_pet FOREIGN KEY (pet_id) REFERENCES pet(pet_id), CONSTRAINT fk_record_adopter FOREIGN KEY (adopter_id) REFERENCES adopter(adopter_id), INDEX idx_record_pet_status (pet_id, status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;领养记录表里我建了一个联合索引(pet_id, status)这是为了支撑“查询某只宠物当前生效的记录”这个高频操作。外键约束FOREIGN KEY保证了引用完整性不会出现一条领养记录指向一只不存在的宠物。这里有一个问答点如果用户想删除一只宠物但它在领养记录表里存在相关记录外键会阻止DELETE操作需要在Java代码里先处理或捕获异常。3.2 关系模型设计的三个细节判断第一个细节是索引的取舍。pet表的species字段建议加普通索引因为按物种筛选是领养系统最常用的查询条件。你可以在Java代码里用ListPet getPetsBySpecies(String species)SQL里通过WHERE species ?走索引不加索引的话数据量上百条以后就会出现明显的全表扫描性能问题。第二个细节是状态字段要不要用ENUM。MySQL的ENUM类型看起来简洁但后续如果要增加状态值比如“已死亡”“已退回”需要执行ALTER TABLE修改枚举定义代价比TINYINT高。所以实际项目中我更推荐TINYINT 代码注释的方案。Java侧用switch或者枚举类来映射可读性就足够了。第三个细节是id_card这类敏感字段是否加密存储。课设阶段通常不强制但你可以用AES_ENCRYPT函数写入Java侧不直接用明文SQL查询。这个可以作为加分项在答辩时讲体现隐私保护意识。3.3 用db.properties管理连接参数数据库连接参数不应该硬编码在Java类里。常见做法是在项目的resources目录下放一个db.properties文件通过Properties类读取。这样换数据库环境时只需要改配置文件不需要改代码。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/pet_adoption?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4 jdbc.usernameroot jdbc.passwordyourpasswordURL参数里useSSLfalse是因为本地开发不会配置SSL证书serverTimezoneAsia/Shanghai是为8.0驱动而设的时区参数否则插入TIMESTAMP字段时会报The server time zone value Öйú±ê׼ʱ¼ä is unrecognized的错误。这个报错几乎每个用MySQL 8.0的人都会遇到一次记下来能省至少十分钟排查时间。4. Swing界面实现与事件绑定核心逻辑4.1 主窗口布局用CardLayout管理多页面切换课设的界面不需要花哨但要结构清晰。推荐采用左侧导航 右侧内容区的布局左侧放按钮宠物管理、领养申请、记录查询右侧用CardLayout切换对应的面板。这套结构比单窗口堆组件要规范得多答辩时也更好描述。// 主界面框架代码 public class MainFrame extends JFrame { private JPanel mainPanel; private CardLayout cardLayout; public MainFrame() { setTitle(宠物领养系统); setSize(1024, 768); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); // 左侧导航栏 JPanel navPanel new JPanel(); navPanel.setPreferredSize(new Dimension(180, 0)); navPanel.setLayout(new GridLayout(4, 1, 10, 10)); navPanel.setBorder(BorderFactory.createEmptyBorder(20, 10, 20, 10)); JButton btnPetManage new JButton(宠物管理); JButton btnAdoptApply new JButton(领养申请); JButton btnRecordQuery new JButton(记录查询); JButton btnQuit new JButton(退出系统); // 右侧内容区 cardLayout new CardLayout(); mainPanel new JPanel(cardLayout); mainPanel.add(new PetManagePanel(), petManage); mainPanel.add(new AdoptApplyPanel(), adoptApply); mainPanel.add(new RecordQueryPanel(), recordQuery); // 事件绑定 btnPetManage.addActionListener(e - cardLayout.show(mainPanel, petManage)); btnAdoptApply.addActionListener(e - cardLayout.show(mainPanel, adoptApply)); btnRecordQuery.addActionListener(e - cardLayout.show(mainPanel, recordQuery)); btnQuit.addActionListener(e - System.exit(0)); // 组装 JPanel root new JPanel(new BorderLayout()); root.add(navPanel, BorderLayout.WEST); root.add(mainPanel, BorderLayout.CENTER); setContentPane(root); } }CardLayout的show(Container, String)方法通过字符串标识切换面板这个标识需要与add(Component, String)时传入的字符串保持一致。IDE里直接跑main方法就能看到左侧按钮点击后右侧面板切换的效果。GridLayout在这里负责把四个按钮在导航栏里垂直均匀排列。4.2 表格展示宠物数据JTable与TableModel的正确用法展示宠物列表最常用的组件是JTable但直接用DefaultTableModel加数据的写法在刷新时会闪烁且无法精细化控制单元格类型。推荐继承AbstractTableModel实现自定义模型这样数据改变时只需要调用fireTableDataChanged()即可刷新界面。public class PetTableModel extends AbstractTableModel { private String[] columns {宠物ID, 名称, 物种, 品种, 年龄, 状态}; private ListPet pets new ArrayList(); Override public int getRowCount() { return pets.size(); } Override public int getColumnCount() { return columns.length; } Override public String getColumnName(int column) { return columns[column]; } Override public Object getValueAt(int rowIndex, int columnIndex) { Pet p pets.get(rowIndex); switch (columnIndex) { case 0: return p.getPetId(); case 1: return p.getName(); case 2: return p.getSpecies(); case 3: return p.getBreed(); case 4: return p.getAge(); case 5: return p.getStatusText(); default: return ; } } public void setPets(ListPet newPets) { this.pets newPets; fireTableDataChanged(); // 通知表格重绘 } }这里有一个关键点getValueAt方法里return p.getStatusText()意味着数据库里存的TINYINT必须在Java侧转换为用户可读的文本。常见做法是在Pet实体类里定义一个getStatusText()方法内部用switch映射“待领养”“已预订”“已领养”。表格模型和实体类分层避免了在界面层频繁写if-else。JTable默认不支持单击编辑但双击可以进入编辑态。如果课设要求“管理员审核领养申请”你需要重写isCellEditable(int row, int col)方法只允许状态列可编辑。4.3 宠物信息登记表单文本校验与下拉联动新增宠物时要处理表单校验年龄必须是正整数名称为空时应该给出弹窗提示而不是直接写入数据库。Swing的JOptionPane.showMessageDialog是最简单的方式校验逻辑放在“确认添加”按钮的事件处理器里。// 表单保存按钮事件 private void doSavePet() { String name txtName.getText().trim(); if (name.isEmpty()) { JOptionPane.showMessageDialog(this, 宠物名称不能为空, 输入错误, JOptionPane.ERROR_MESSAGE); return; } int age 0; try { age Integer.parseInt(txtAge.getText().trim()); if (age 0 || age 300) { throw new NumberFormatException(); } } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(this, 年龄必须是1-300的整数, 输入错误, JOptionPane.ERROR_MESSAGE); return; } String species (String) comboSpecies.getSelectedItem(); String breed txtBreed.getText().trim(); String gender radioMale.isSelected() ? 公 : 母; String health txtHealth.getText().trim(); Pet pet new Pet(name, species, breed, age, gender, health); boolean success petDao.insertPet(pet); if (success) { JOptionPane.showMessageDialog(this, 添加成功); refreshTable(); // 重新查询数据库 clearForm(); // 清空表单输入 } else { JOptionPane.showMessageDialog(this, 数据库写入失败请检查连接, 错误, JOptionPane.ERROR_MESSAGE); } }校验逻辑必须在doSave方法里完成而不是依赖数据库端约束因为数据库返回的错误信息对用户不友好。refreshTable()内部会重新执行SELECT语句并调用tableModel.setPets(list)这是Swing里“数据变更后刷新界面”的标准动作。5. JDBC数据访问层与事务处理的坑5.1 DAO层封装与预编译SQL数据访问层用DAO模式做隔离界面代码里不出现任何SQL字符串。这样做的意义是如果数据库从MySQL换到PostgreSQL只需要修改DAO实现类和驱动配置界面层完全不动。public class PetDao { public ListPet getPetsBySpecies(String species) { ListPet list new ArrayList(); String sql SELECT pet_id, name, species, breed, age, gender, health_status, status FROM pet WHERE species ? ORDER BY create_time DESC; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, species); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Pet p new Pet(); p.setPetId(rs.getInt(pet_id)); p.setName(rs.getString(name)); p.setSpecies(rs.getString(species)); p.setBreed(rs.getString(breed)); p.setAge(rs.getInt(age)); p.setGender(rs.getString(gender)); p.setHealthStatus(rs.getString(health_status)); p.setStatus(rs.getByte(status)); list.add(p); } } } catch (SQLException e) { e.printStackTrace(); } return list; } }PreparedStatement必须使用参数占位符?而不是字符串拼接这是防止SQL注入的底线。try-with-resources语句确保Connection、PreparedStatement、ResultSet在方法结束时自动关闭避免连接泄漏。注意关闭顺序ResultSet先关Statement次之Connection最后try-with-resources的声明顺序恰好是从右到左关闭所以只要按这个顺序编写即可。5.2 领养审核时的SQL事务边界真正体现功底的业务是“审核通过领养申请”。这个操作需要同时做两件事更新adoption_record表状态为“已通过”以及将pet表对应记录的status改为“已领养”。这两步必须在一个事务里完成否则会出现申请已通过但宠物状态没有更新的数据不一致问题。public boolean approveAdoption(int recordId, int petId) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 开启事务 // 步骤1: 更新领养记录状态 String sqlUpdateRecord UPDATE adoption_record SET status 1 WHERE record_id ?; try (PreparedStatement ps conn.prepareStatement(sqlUpdateRecord)) { ps.setInt(1, recordId); ps.executeUpdate(); } // 步骤2: 更新宠物状态为已领养 String sqlUpdatePet UPDATE pet SET status 2 WHERE pet_id ?; try (PreparedStatement ps conn.prepareStatement(sqlUpdatePet)) { ps.setInt(1, petId); ps.executeUpdate(); } conn.commit(); // 提交事务 return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); // 回滚事务 } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { if (conn ! null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }关键参数是conn.setAutoCommit(false)从这一行开始后续所有SQL都会进入同一个事务。commit()之后数据才真正落盘如果在第二个executeUpdate抛异常rollback()会把第一步已更新的数据还原。finally块里把autoCommit恢复为true是为了连接归还连接池时保持默认状态。5.3 连接池与并发处理思路课设项目一般不需要连接池但如果你做的系统要演示多人同时在线操作可以考虑引入HikariCP。HikariCP是Spring Boot默认连接池配置方式很直观HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/pet_adoption); config.setUsername(root); config.setPassword(yourpassword); config.setMaximumPoolSize(10); config.setMinimumIdle(2); HikariDataSource ds new HikariDataSource(config);使用连接池后DBUtil.getConnection()改为从ds.getConnection()获取即可其余DAO代码不需要改动。并发申请场景下“同一只宠物被两个人同时申请”是个典型问题解决方案是事务内用SELECT ... FOR UPDATE锁定行记录或者基于业务逻辑判断pet.status后再更新。提示课程设计阶段通常不需要处理高并发但建议在代码里体现“状态检查更新”的逻辑顺序这能体现你对并发一致性的理解。6. 集成部署、打包运行和答辩演示的技巧6.1 本地联调的三个常见环境问题从代码写完到能跑通演示通常会有三个高频问题。第一个是ClassNotFoundException这通常意味着驱动jar没有正确加入classpath在IDEA里右键项目 → Open Module Settings → Libraries确认jar已在列表里。第二个是Unknown database pet_adoption这是因为还没执行建库脚本或MySQL服务没启动。第三个是Access denied for user rootlocalhost检查db.properties里的密码和MySQL实际设置的密码是否一致。还有一个小细节如果MySQL里已经存在旧的表结构改完建表SQL后需要先DROP TABLE再重新执行一遍否则会因字段不匹配导致SQLException: Unknown column xxx in field list。这个报错在修改实体类字段后没同步数据库表时经常出现。6.2 用Fat Jar方式打包交付把项目打包成可执行jar可以方便老师直接在桌面环境运行。IDEA里可以通过Artifacts配置Fat Jar包含所有依赖步骤是File → Project Structure → Artifacts → 新增 JAR → From modules with dependencies → 选择主类。打包后生成的jar放在out/artifacts/目录下命令行里执行java -jar pet-adoption.jar如果双击jar没有反应通常是META-INF/MANIFEST.MF里的Main-Class没有正确指定。解压jar检查一下manifest文件内容确认主类路径写的是完整的com.petadoption.view.MainFrame。另外Swing程序建议指定-Dfile.encodingUTF-8启动参数否则窗口里中文可能显示为乱码。6.3 答辩演示时的两条话术建议演示时不要只点击按钮说“这是新增、这是修改”老师更想听到的是设计决策。第一个可以主动提的点是“宠物状态用TinyInt而不是Varchar存储是为了配合Java侧枚举类做类型安全的映射”同时按下拉框筛选说“这里走了数据库索引而不是在内存里全表过滤”。第二个可以提的是“审核操作开启了事务避免两边数据不一致”顺手把异常回滚的代码位置指给老师看。如果被问到“为什么不用MyBatis或Spring”答“课程设计阶段为了展示原生JDBC和SQL功底刻意没有引入ORM框架同时也减少了配置复杂度、保证了程序在一台普通电脑上直接运行”即可。这类回答比空泛地说“简单”更有说服力。6.4 ER图和数据库文档的导出技巧答辩材料里通常需要ER图MySQL Workbench的Database → Reverse Engineer功能可以自动从数据库反向生成ER模型图。选中所有表后导出为PNG即可。如果嫌生成的图布局乱可以在Workbench里手动调整表位置再导出。另外把建表SQL整理成带注释的init.sql文件一并打包会显得工程化程度更高——这份SQL文件本身就是答辩评分的依据之一。本文还有配套的精品资源点击获取
返回列表