
简介这是一套面向高校计算机相关专业学生的J2EE课程设计/期末大作业完整工程包以「爱狗之家」宠物主题业务为背景适合正在准备课程设计、毕业设计或工程实训的学习者参考复刻。项目采用Java Web技术栈涵盖JSP页面、Java源码、Hibernate持久化配置及数据库访问层等典型分层结构可帮助读者理解MVC模式下的业务实现思路。资源包共197个文件包含19个java源文件、19个class编译文件、18个jsp页面、17个xml配置、30个jar依赖包以及jpeg、jpg、png等图片素材与css、js、ttf等前端资源压缩包约22.22MB目录结构完整便于按模块查阅。目前已有37人学习关注。拿到资料后可对照源码复现项目运行效果也可在此基础上修改扩展出新的功能模块设计报告同样可借鉴其组织方式适合作为课程作业、大创项目或初期练手的参考工程。1. 爱狗之家这个 J2ee 期末大作业到底在考什么每年到了期末周总有一批人对着“J2ee 期末大作业 爱狗之家.zip”这种标题发愁。有人以为它就是个宠物领养信息展示页随便套个模板就能交差也有人一看到 J2ee 三个字就头皮发麻觉得必须上 EJB、分布式、消息队列才够格。这两种想法都偏了。爱狗之家这类题目的本质是让你用 Java Web 技术栈完整走一遍“数据建模 → 后端接口 → 页面渲染 → 权限控制”的闭环它考的不是你会不会背 Servlet 生命周期而是你能不能把一个真实业务场景拆成可运行的增删改查加状态流转。适合谁看适合已经学过 JSP/Servlet 或 Spring Boot 基础、但第一次独立做完整项目、不知道从哪下手的人。下面我按一个能直接跑起来的方案把选型、建表、接口、页面和踩坑点讲清楚。2. 技术选型为什么我建议用 Spring Boot MyBatis 而不是纯 Servlet2.1 纯 Servlet/JSP 方案的真实成本很多学校的 J2ee 课程还在教 Servlet JSP JDBC于是不少人第一反应就是照着课本写。我当年也这么干过结果一个爱狗之家写了三十多个 Servlet每个里面重复着几乎一样的try-catch-finally和Connection关闭逻辑。纯 Servlet 方案不是不能做而是它会把你的时间大量消耗在样板代码上而不是业务逻辑上。比如“查询待领养狗狗列表”这个功能用 Servlet 你要手动解析请求参数、手动拼 SQL、手动把 ResultSet 映射成对象、手动转发到 JSP。一旦需求改成“按品种筛选”你又得回去改 SQL 和参数解析。期末大作业通常只有两到三周把时间花在这些地方最后页面和交互往往做得很粗糙分数反而上不去。2.2 Spring Boot MyBatis 的最小依赖组合我一般会选 Spring Boot 2.7.x 加 MyBatis Starter数据库用 MySQL 8。这套组合的好处是内嵌 Tomcat 不用配服务器MyBatis 的 XML 或注解能让你把 SQL 和 Java 代码分开改查询条件时不用动接口层。下面是一个能直接用的pom.xml关键依赖片段注意版本号我写的是当时能稳定跑通的组合你本地如果已经有其他版本优先保持项目内一致。!-- pom.xml 关键依赖Spring Boot 2.7.x MyBatis MySQL -- dependencies !-- Web 场景启动器自带 Tomcat 和 Spring MVC -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis 整合 Spring Boot省去手动配置 SqlSessionFactory -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version2.3.1/version /dependency !-- MySQL 驱动注意 8.x 驱动类名带 cj -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId version8.0.33/version scoperuntime/scope /dependency !-- 如果要用 JSP 而不是 Thymeleaf需要额外加 jasper -- dependency groupIdorg.apache.tomcat.embed/groupId artifactIdtomcat-embed-jasper/artifactId scopeprovided/scope /dependency /dependencies这段依赖里spring-boot-starter-web负责 MVC 和 JSON 序列化mybatis-spring-boot-starter负责把 Mapper 接口自动注册成 BeanMySQL 驱动用com.mysql.cj.jdbc.Driver。如果你打算用 JSP 做页面tomcat-embed-jasper必须加否则 Spring Boot 内嵌 Tomcat 不会编译 JSP。参数上唯一要注意的是 MySQL 驱动版本要和本地数据库大版本匹配8.0 的库用 5.x 驱动会报时区或 SSL 错误。2.3 数据库选 MySQL 还是 H2看你要不要演示有些同学电脑上没装 MySQL或者机房环境不让装这时候可以换 H2 内存数据库。H2 的好处是随项目启动不用额外安装演示时数据初始化脚本一跑就有数据。坏处是每次重启数据清空如果你需要“领养申请记录”这种要保留状态的数据H2 的文件模式配置起来又比 MySQL 麻烦。我的建议是本地开发用 MySQL提交前如果担心老师环境没有 MySQL再补一个 H2 的 profile。下面这段application.yml是 MySQL 的典型配置重点看url里的参数。# application.ymlMySQL 连接配置 spring: datasource: url: jdbc:mysql://localhost:3306/dog_home?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.doghome.entityserverTimezoneAsia/Shanghai不加的话插入时间字段可能差 8 小时useSSLfalse在本地开发时避免证书警告。mapper-locations告诉 MyBatis 去哪里找 XML 映射文件如果你全用注解写 SQL这行可以去掉但期末项目通常 SQL 较多XML 更清晰。3. 从建表到接口爱狗之家核心业务的最小实现3.1 四张表撑起领养、申请、用户和品种爱狗之家的业务不复杂但表设计不合理后面会反复改。我一般会建四张表user存注册用户和管理员dog存狗狗信息breed存品种字典adoption_apply存领养申请记录。关键字段和关系如下表。表名关键字段说明userid, username, password, rolerole 区分 admin 和 normalbreedid, name品种字典避免 dog 表里写死字符串dogid, name, breed_id, age, status, photo_urlstatus 表示待领养/已领养/审核中adoption_applyid, dog_id, user_id, apply_time, statestate 表示待审核/通过/拒绝建表时最容易翻车的是dog.status和adoption_apply.state这两个状态字段。有人用中文直接存“待领养”有人用 0/1/2 但不写注释过两周自己都忘了。我的习惯是用 tinyint 加注释Java 里用枚举对应。下面这段 SQL 是建表脚本的核心部分注意外键和索引。-- 狗狗表breed_id 关联品种status 用 0 待领养 1 审核中 2 已领养 CREATE TABLE dog ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, breed_id INT, age INT, status TINYINT DEFAULT 0 COMMENT 0待领养 1审核中 2已领养, photo_url VARCHAR(255), intro TEXT, FOREIGN KEY (breed_id) REFERENCES breed(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 领养申请表state 用 0 待审核 1 通过 2 拒绝 CREATE TABLE adoption_apply ( id INT PRIMARY KEY AUTO_INCREMENT, dog_id INT NOT NULL, user_id INT NOT NULL, apply_time DATETIME DEFAULT CURRENT_TIMESTAMP, state TINYINT DEFAULT 0 COMMENT 0待审核 1通过 2拒绝, FOREIGN KEY (dog_id) REFERENCES dog(id), FOREIGN KEY (user_id) REFERENCES user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;utf8mb4而不是utf8因为狗狗名字里可能有 emoji 或者生僻字。外键在期末项目里建议加上虽然有人说不利于性能但能帮你避免插入不存在的 breed_id 这种低级错误。apply_time用CURRENT_TIMESTAMP默认值插入时不用手动传时间。3.2 用 MyBatis 写一个带条件筛选的狗狗列表查询列表查询是使用频率最高的接口。需求通常是显示所有待领养的狗可以按品种筛选按年龄排序。下面这个 Mapper XML 写法我用了很多次动态 SQL 部分能帮你省掉大量 if-else。!-- DogMapper.xml 片段动态条件查询 -- select idselectDogs resultTypecom.example.doghome.entity.Dog SELECT d.*, b.name AS breedName FROM dog d LEFT JOIN breed b ON d.breed_id b.id where if testbreedId ! null AND d.breed_id #{breedId} /if if teststatus ! null AND d.status #{status} /if /where ORDER BY d.age ASC /selectwhere标签会自动处理第一个AND不用你写WHERE 11。resultType里如果 Dog 实体没有breedName字段需要加一个 transient 属性或者用resultMap。我一般直接在 Dog 类里加private String breedName;并生成 getter/setter这样前端能直接显示品种名而不是 breed_id。参数breedId和status从 Controller 传进来如果前端没传就是 null动态 SQL 会自动忽略。3.3 Controller 层接收参数、调用 Service、返回 JSONController 的职责是薄薄一层不要把业务逻辑写进来。下面这个DogController的列表方法展示了典型写法。RestController RequestMapping(/api/dog) public class DogController { Autowired private DogService dogService; // 查询狗狗列表支持按品种和状态筛选 GetMapping(/list) public Result list(RequestParam(required false) Integer breedId, RequestParam(required false) Integer status) { ListDog dogs dogService.selectDogs(breedId, status); return Result.success(dogs); } // 提交领养申请dogId 和 userId 必传 PostMapping(/apply) public Result apply(RequestBody AdoptionApply apply) { // 先检查这只狗是否还是待领养状态 Dog dog dogService.getById(apply.getDogId()); if (dog.getStatus() ! 0) { return Result.error(该狗狗已被领养或审核中); } dogService.addApply(apply); return Result.success(申请已提交); } }RequestParam(required false)让筛选参数可选不传就查全部。RequestBody接收前端 JSON适合提交申请这种带多个字段的场景。注意apply方法里先查狗的状态再插入申请这一步是防止并发下同一只狗被多人同时申请。虽然期末项目并发量几乎为零但写上去能体现你对状态一致性的考虑答辩时是个加分点。Result类是自己封装的统一返回格式包含code、msg、data三个字段。4. 页面渲染与权限JSP 和 Thymeleaf 怎么选登录拦截怎么做4.1 JSP 在 Spring Boot 里的配置坑如果你的课程明确要求用 JSP那就在application.yml里加spring.mvc.view.prefix: /WEB-INF/jsp/和suffix: .jsp然后把 JSP 文件放在src/main/webapp/WEB-INF/jsp/下。注意 Spring Boot 内嵌 Tomcat 默认不支持 JSP 编译必须加tomcat-embed-jasper依赖并且打包方式要是 war 而不是 jar否则 JSP 找不到。我见过有人用 jar 打包后访问页面 404排查半天才发现是打包方式的问题。Thymeleaf 就没这些事模板放resources/templates/下加个 starter 就能用语法也更像 HTML。如果老师不强制 JSP我建议用 Thymeleaf省下来的时间可以打磨页面样式。4.2 用拦截器做登录校验和角色区分爱狗之家通常有两种角色普通用户只能申请领养和查看自己的申请记录管理员可以增删改狗狗信息、审核申请。用 Spring MVC 的HandlerInterceptor做统一拦截最省事。下面是一个登录拦截器的核心代码。public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); User user (User) session.getAttribute(loginUser); // 未登录则跳转到登录页 if (user null) { response.sendRedirect(/login); return false; } // 管理员接口校验角色 String uri request.getRequestURI(); if (uri.startsWith(/admin) !admin.equals(user.getRole())) { response.sendRedirect(/noAuth); return false; } return true; } }注册拦截器时用addPathPatterns(/**)拦截所有请求再用excludePathPatterns放行登录页、注册页和静态资源。session.getAttribute(loginUser)里的对象在登录成功时存入。角色判断用 URI 前缀区分/admin开头的只有管理员能访问。这种写法简单直接期末项目足够用。如果你用 Spring Security配置会更复杂但功能也更全看你的时间预算。4.3 领养申请的状态流转怎么在页面上体现用户提交申请后狗狗状态应该从“待领养”变成“审核中”管理员审核通过后变成“已领养”同时拒绝其他申请。这个流转在页面上要给出明确反馈。我的做法是在狗狗详情页根据status显示不同按钮status 为 0 显示“申请领养”为 1 显示“审核中”且按钮禁用为 2 显示“已领养”。管理员后台则列出所有 state 为 0 的申请每条后面有“通过”和“拒绝”两个按钮。点击通过时后端要在一个事务里做两件事更新申请状态为 1更新狗狗状态为 2。下面这段 Service 方法展示了事务注解的用法。Transactional public void approveApply(Integer applyId) { AdoptionApply apply applyMapper.selectById(applyId); // 更新申请状态为通过 applyMapper.updateState(applyId, 1); // 更新狗狗状态为已领养 dogMapper.updateStatus(apply.getDogId(), 2); // 把该狗狗的其他待审核申请自动拒绝 applyMapper.rejectOthers(apply.getDogId(), applyId); }Transactional保证两个更新要么都成功要么都回滚。rejectOthers是一条 update 语句把同一只狗的其他 state0 的申请改成 2。这一步很多人会漏掉导致一只狗被多次“通过”数据就乱了。5. 避坑与排查爱狗之家项目里最容易翻车的五个地方5.1 中文乱码从数据库到页面全链路排查现象狗狗名字在数据库里看是正常的但页面上显示问号。原因通常是某一环的字符集不是 utf8mb4。解决顺序是先确认数据库和表的字符集用SHOW CREATE TABLE dog;看是不是CHARSETutf8mb4再确认连接 URL 里有characterEncodingutf8最后检查 JSP 页面头部有没有% page contentTypetext/html;charsetUTF-8 %。三处都对了基本不会乱码。如果还不行看 Tomcat 的server.xml里 Connector 有没有URIEncodingUTF-8不过 Spring Boot 内嵌 Tomcat 默认就是 UTF-8一般不用改。5.2 静态资源 404拦截器把 css 和 js 也拦了现象登录页样式全丢F12 看 css 文件返回 302 跳转到登录页。原因是你注册拦截器时用了addPathPatterns(/**)但没放行静态资源。解决是在excludePathPatterns里加上/css/**、/js/**、/images/**。如果你把静态资源放在resources/static/下Spring Boot 默认映射路径就是根路径所以放行这几个模式即可。另一个可能是你用了 JSP 但静态资源放在了webapp下路径写法不对检查一下引用路径是绝对路径还是相对路径。5.3 领养申请重复提交前端按钮没防抖后端也没唯一约束现象用户快速点两次“申请领养”数据库里出现两条相同 dog_id 和 user_id 的待审核记录。原因有两个前端按钮没禁用后端没做唯一性校验。解决分两层前端点击后立即disabled按钮后端在插入前先查SELECT COUNT(*) FROM adoption_apply WHERE dog_id? AND user_id? AND state0大于 0 就返回“已申请过”。更彻底的做法是在表上加唯一索引UNIQUE KEY uk_dog_user (dog_id, user_id, state)但 state 会变所以这个索引要配合业务逻辑用。期末项目里做前两层就够了。5.4 时间字段差 8 小时时区配置漏了现象申请提交后数据库里apply_time比实际时间早 8 小时。原因是 MySQL 驱动和数据库时区不一致。解决是在 JDBC URL 里加serverTimezoneAsia/Shanghai并且确认 MySQL 全局时区是08:00。如果用的是 Docker 里的 MySQL还要检查容器时区。另一个容易忽略的点是 Java 8 的LocalDateTime和 MySQL 的DATETIME映射MyBatis 从 3.4.5 开始支持低版本需要加mybatis-typehandlers-jsr310依赖。5.5 管理员删狗导致外键报错先处理关联申请现象管理员删除一只狗控制台报Cannot delete or update a parent row: a foreign key constraint fails。原因是adoption_apply表里有这只狗的申请记录。解决有两种一是先删关联申请再删狗用事务包起来二是把外键改成ON DELETE CASCADE删狗时自动删申请。我一般选第一种因为申请记录有时需要留档。在 Service 里先applyMapper.deleteByDogId(dogId)再dogMapper.deleteById(dogId)加Transactional保证一致性。6. 让爱狗之家从“能跑”到“能拿高分”的两个技巧第一个技巧是给列表加分页。期末项目数据量不大但分页能体现你对性能的考虑。用 MyBatis 的LIMIT加PageHelper插件最省事引入pagehelper-spring-boot-starter后在查询前调PageHelper.startPage(pageNum, pageSize)返回的PageInfo对象直接带总页数和总记录数。前端只需要传pageNum和pageSize两个参数。注意PageHelper只对紧接着的那一条查询生效不要在多条查询之间共用。第二个技巧是给关键操作加日志。不用上 Logback 复杂配置直接在 Service 方法里用System.out.println或者LoggerFactory打一行“用户 X 申请领养狗狗 Y”。答辩时老师问“你怎么知道谁申请了哪只狗”你能指着日志说出来比空口说“数据库里有”更有说服力。我自己的习惯是在申请、审核、删除三个方法里各加一条日志记录操作人、操作对象和时间。最后说一个我踩过的坑别等到最后一天才把项目打成 zip。我当年就是最后一天才发现 JSP 在 jar 包里不编译临时改成 war 重新打包差点错过提交时间。提前一周把项目在另一台电脑上跑一遍确认数据库脚本、依赖版本、打包方式都没问题比多写两个页面重要得多。希望帮到你。本文还有配套的精品资源点击获取