简介:面向JavaWeb课程设计或毕业设计人群的体育竞赛管理系统完整项目包,基于jdk1.8、JSP/Servlet与MySQL 5.7实现,覆盖运动员在线报名与成绩查看、管理员用户及参赛流程管理、裁判员成绩录入与排名公示等核心业务。资源共2662个文件,含jsp/java/class等后端源码、html/css/js前端页面、sql数据库脚本及jar依赖库,压缩包约29.37MB,解压即可对照学习。系统通过自动化处理减少人工统计误差,并提供成绩留存与公示机制,适合需要快速理解JavaWeb分层架构、完成课程设计或论文支撑的读者。已有903人学习下载,项目目录结构完整,便于按模块定位代码与调试排错,是一份可直接运行与二次开发的实战参考资料。
1. JavaWeb体育竞赛管理系统,为什么是毕设里最稳妥的一题
毕设题“基于JavaWeb的体育竞赛管理系统”在每年的选题库里都不缺人,因为它把用户、赛事、报名、检录、成绩、公告连成一条完整业务链,能覆盖 JavaWeb 阶段绝大多数考点,也天然适合拿 MySQL 做数据支撑。很多同学第一次跑通整套项目,就是在做这类 JavaWeb 项目时完成的。题目本身不会太惊艳,但胜在“安全”:评审不会纠结工作量和原创性,答辩时也能把每一个表、每一个接口讲清楚。这篇按我实际配环境和改项目时的顺序来写,从技术选型、数据库设计、IDEA 运行配置到常见报错,给出一套可以直接照着配完并继续扩展的方案,适合 JSP + SSM 栈还没完全理清的新手,也适合答辩前想快速预演一遍的应届生。
2. 技术栈重量级决策:SSM + JSP + MySQL 8,配比和理由
2.1 为什么我用 SSM 而不是 Spring Boot:答辩尺度和部署成本的平衡
这个题目叫“JavaWeb”,而 JavaWeb 课程体系的核心是 Servlet、JSP、Filter、Listener 以及 JDBC。很多案例一上来就是 Spring Boot,结果答辩老师问“你讲讲 Servlet 生命周期”时,学生只能答“我用的是 Spring Boot,没直接写过 Servlet”。所以在毕设尺度下,我一般会选择 SSM 组合:Spring + SpringMVC + MyBatis,外面套 JSP。这套组合里 SpringMVC 的 DispatcherServlet 本身就是 Servlet,核心链路仍然要求你把 web.xml、Spring 容器、MyBatis 会话工厂串起来,既能对上课程知识点,又能体现项目规模。
版本选择上,建议这一套,看起来老但在 JDK 8 环境下最不容易翻车:
| 组件 | 推荐版本 | 理由 |
|---|---|---|
| JDK | 1.8.0_202 或 8u291 | 和 Tomcat 9 兼容,且大多数电脑已经装了 |
| Maven | 3.6.3 | 对中央仓库依赖解析最稳 |
| Tomcat | 9.0.65 左右 | Servlet 4.0,支持 JavaWeb 项目标准目录结构 |
| Spring / SpringMVC | 5.3.x | 兼容 JDK 8 与 Tomcat 9,别再升到 Spring 6 |
| MyBatis | 3.5.x | 配合 mybatis-spring 2.0.x |
| MySQL | 8.0.x | 比 5.7 多了一点“技术时髦度”,但要注意驱动类名变化 |
| mysql-connector-java | 8.0.33 | 驱动类名是 com.mysql.cj.jdbc.Driver 而不是旧版 com.mysql.jdbc.Driver |
注意,MySQL 8 的驱动类名和连接参数都和 5.7 不一样,后面第 4 章里我会给出可直接用的 jdbcUrl 配置,第 5 章会专门列一个因为驱动类名报错的案例。这套技术栈跑在毕设里,论“内存消耗”和“部署复杂度”都比 Spring Boot + MyBatis-Plus 低,也更好手写演示。
2.2 三层架构包结构与项目骨架清单
项目骨架如果建对了,后面每一层写起来都不会乱。我建议的包结构如下,只放业务相关的,不搞过度设计:
src/main/java └─ com.sports.contest ├─ controller # SpringMVC 控制器,负责接收请求和返回视图 ├─ service # 业务接口 ├─ service.impl # 业务实现 ├─ mapper # MyBatis 的 Mapper 接口,只写抽象方法 ├─ entity # 实体类,对应数据库表 ├─ common # 通用返回体、分页参数、常量 ├─ interceptor # 登录拦截、权限拦截 └─ util # 赛程编排工具、日期工具 src/main/resources ├─ spring-mvc.xml ├─ spring-mybatis.xml ├─ mybatis-config.xml ├─ jdbc.properties └─ mapper ├─ UserMapper.xml ├─ EventMapper.xml ├─ SignupMapper.xml ├─ ScheduleMapper.xml └─ ScoreMapper.xml src/main/webapp/WEB-INF ├─ web.xml ├─ views # JSP 页面 │ ├─ admin │ ├─ user │ └─ common └─ lib # 如果不用 Maven 时手动放 jar常见做法是使用 Maven 管理依赖时不在 WEB-INF/lib 下手动放 jar,所以我建议 pom.xml 一把梭,后面排查依赖冲突也方便。三层架构里最容易被忽略的是 service 接口和 impl 分离,很多同学图省事只写一个类,答辩时被问“业务层为什么要分接口和实现”容易答不上来,所以这个习惯最好留好。
SpringMVC 的 Controller 层只做参数接收和视图跳转,不写 SQL;Mapper 层只写一对一的 SQL 映射;业务编排包括事务、赛程生成、成绩计算都放 Service 层。这套职责划分在答辩时可以直接展开讲,本身也是 JavaWeb 项目完整案例里最吸引评审的细节。
3. 数据库设计与核心业务表:七张表怎么扛起一场运动会
3.1 体育竞赛系统的实体关系与建表细节
“体育竞赛管理系统”说到底是在管理一个完整的赛事流程:管理员发布赛事,运动员报名,系统编排赛程,裁判录入成绩,系统自动算排名。和电商系统比,它少了很多支付对账逻辑,但多了一个“赛程编排”和“成绩排序”的业务难点,这也是答辩时可以主动展示的部分。
数据库设计我建议用 7 张表,不多不少:用户表、角色表、赛事表、报名表、赛程表、成绩表、公告表。建表时优先考虑以下边界:项目只涉及一个学校或一个单位内的比赛,所以“代表队”可以做成用户表里的一个字段,而不单独建 team 表;如果一定要强调团体总分,再单独抽出 team 表,但那样就要加第三范式上的冗余设计,反而不适合毕设体量。
下面直接给出核心表的 MySQL 建表语句,选用 MySQL 8 语法。注意字段注释必须写清楚,方便答辩时对着表结构讲。
CREATE DATABASE sports_contest DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE sports_contest; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(30) NOT NULL UNIQUE COMMENT '登录名,建议用学号或工号', password VARCHAR(64) NOT NULL COMMENT 'MD5或BCrypt后的密文,不要存明文', real_name VARCHAR(30) NOT NULL COMMENT '姓名', role_id INT NOT NULL DEFAULT 2 COMMENT '1=管理员,2=运动员', team_name VARCHAR(50) DEFAULT NULL COMMENT '代表队/班级', phone VARCHAR(20) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_role (role_id) ) ENGINE=InnoDB COMMENT '用户表'; CREATE TABLE t_event ( id INT PRIMARY KEY AUTO_INCREMENT, event_name VARCHAR(50) NOT NULL COMMENT '赛事名称:男子100米预赛', event_type TINYINT NOT NULL COMMENT '1=单人项目,2=团体项目', category VARCHAR(20) NOT NULL DEFAULT '田径' COMMENT '项目分类', start_time DATETIME DEFAULT NULL COMMENT '计划开始时间', place VARCHAR(100) DEFAULT NULL COMMENT '比赛地点', max_players INT NOT NULL DEFAULT 8 COMMENT '每组人数或项目人数上限', status TINYINT NOT NULL DEFAULT 0 COMMENT '0=报名中,1=已截止,2=进行中,3=已结束' ) ENGINE=InnoDB COMMENT '赛事表'; CREATE TABLE t_signup ( id INT PRIMARY KEY AUTO_INCREMENT, event_id INT NOT NULL COMMENT '赛事ID', user_id INT NOT NULL COMMENT '运动员ID', group_no INT DEFAULT NULL COMMENT '预赛分组号,编排后写入', signup_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT NOT NULL DEFAULT 0 COMMENT '0=待审核,1=通过,2=拒绝', UNIQUE KEY uk_event_user (event_id, user_id) ) ENGINE=InnoDB COMMENT '报名表'; CREATE TABLE t_schedule ( id INT PRIMARY KEY AUTO_INCREMENT, event_id INT NOT NULL, group_no INT NOT NULL COMMENT '第几组/第几轮', lane_no INT DEFAULT NULL COMMENT '跑道号或出场顺序', user_id INT NOT NULL COMMENT '参赛人ID', round_no INT NOT NULL DEFAULT 1 COMMENT '第几轮:1=预赛,2=决赛', UNIQUE KEY uk_event_group_lane (event_id, group_no, lane_no) ) ENGINE=InnoDB COMMENT '赛程表'; CREATE TABLE t_score ( id INT PRIMARY KEY AUTO_INCREMENT, schedule_id INT NOT NULL COMMENT '关联赛程表的具体参赛记录', event_id INT NOT NULL, user_id INT NOT NULL, score DECIMAL(6,2) DEFAULT NULL COMMENT '成绩,如12.58秒', rank_no INT DEFAULT NULL COMMENT '本组名次', is_qualified TINYINT DEFAULT 0 COMMENT '是否晋级', remark VARCHAR(200) ) ENGINE=InnoDB COMMENT '成绩表';建表时有几个细节值得单讲。一是时间字段全部用 DATETIME,不用 TIMESTAMP,因为 TIMESTAMP 在 MySQL 8 里有时区转换问题,毕设项目部署在不同电脑上容易出现“少 8 小时”的鬼现象。二是成绩字段用 DECIMAL(6,2),因为体育成绩要精确到百分之一秒,而秒数最大也就是两位数加一个小数点,6 位总长度足够用。三是报名表上加了联合唯一键uk_event_user,这样同一赛事同一人不可能重复报名,省去在 Service 层做重复判断。
3.2 赛程编排算法:怎么用一次分组生成不冲突的赛程表
赛程编排是本项目里最值得拿出来讲讲的“业务算法”,比单纯的增删改查要有内容得多。常见做法是:首先把报名审核通过的人放到一个 List 里,然后按每组上限人数去分组,分组之后把每个运动员写进 t_schedule 表,再给 t_signup 表回填 group_no。这里最大的坑是“随机分组的可复现性”。
很多同学会直接用SELECT * FROM t_signup ORDER BY RAND(),然后循环分到各组。这在数据量小的时候没问题,但答辩老师常会问一句“你这个随机数是完全随机的吗?如果我想指定种子做实验怎么办”。所以我建议在 Service 层做一个简单的“轮转分组”工具,不依赖数据库随机:
public Map<Integer, List<Signup>> assignGroups(List<Signup> signups, int groupSize) { // 按报名时间排序,保证稳定顺序 signups.sort(Comparator.comparing(Signup::getSignupTime)); Map<Integer, List<Signup>> groups = new LinkedHashMap<>(); int groupNo = 1; for (int i = 0; i < signups.size(); i++) { if (i % groupSize == 0 && i > 0) { groupNo++; } groups.computeIfAbsent(groupNo, k -> new ArrayList<>()).add(signups.get(i)); } return groups; }逻辑说明:先用signupTime排序,这是报名顺序,不是数据表里的自增 ID。然后按groupSize切分,第 1 到 8 个人进第 1 组,第 9 到 16 个人进第 2 组。这样产生的结果在相同输入下是固定的,不会出现跑一次一个样的问题。需要调整时,只要在排序前加一个“种子参数”,比如按学号最后一位、抽签顺序号来预排序,就能手动控制编组结果。
参数说明:groupSize不要硬编码,读取 t_event 表的max_players字段;如果项目是团体赛,则按团队整体作为分组单位,字段含义变成“每队人数”。生成 t_schedule 写入时要注意lane_no,单人项目要交错分配跑道,避免同组同一单位运动员连续挨着,这个小细节在答辩时提出来会加分。
4. 在 IDEA 里跑通项目的完整过程:从 JDK 到 Tomcat 的一次成型
4.1 JDK、Tomcat、Maven、Spring 版本的一组兼容清单
热搜里“idea运行javaweb项目配置”是点击量很高的词,原因很简单:很多项目不是写不出来,是配环境配到怀疑人生。如果电脑里已经装了不同版本的 JDK,IDEA 会弹出“Project SDK 不匹配”的提示,这时候不要无脑选最新版。SSM 这套东西依赖的是 Java 8 的一些旧特性,比如javax.servlet包名,升到 JDK 17 后 JSP 和 Tomcat 9 都会出现兼容问题。所以先统一版本,再写代码,不然代码写完了也启动不了。
通用兼容原则可以按下面这张表对齐:
| 项目 | 版本 | 备注 |
|---|---|---|
| Project SDK | 1.8 | IDEA 里设置为 18 或 1.8 |
| Java Compiler | 8 | 在 Settings → Build → Compiler 里对应切换 |
| Maven | 3.6.3 | 避免 Maven 3.9 在解析旧依赖时行为变化 |
| Tomcat | 9.0.x | 支持 Servlet 4.0,配 IDEA 时选择 Tomcat Server |
| Spring | 5.3.x | spring-webmvc、spring-jdbc、spring-tx |
| MyBatis | 3.5.16 | mapper 注解和 XML 都支持 |
这里有个容易忽略的点:IDEA 里“Tomcat Server”配置中的 JRE 默认使用的是Default,如果你机器上同时有 JDK 8 和 JDK 17,一定要手动选择 JDK 8,否则 Tomcat 虽然启动了,报错信息却指向 JSP 编译器版本问题,非常难定位。
4.2 web.xml 与 spring-mybatis.xml:最容易被忽略的三处配置
DD 文件(web.xml)是 JavaWeb 项目的“门面”,很多毕设项目里 web.xml 版本号是 2.3,这会直接导致 EL 表达式不生效、JSP 里${}全部原样输出。我建议 web.xml 直接用 Servlet 3.1 版本声明,同时把 DispatcherServlet 和编码过滤器写好:
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>逻辑说明:CharacterEncodingFilter必须放在所有过滤器的最前面,放在后面会导致 request 参数在进入 Controller 前已经按 ISO-8859-1 解析了一次,后面再编码也没用,这是 JSP 中文乱码最常见的根因。load-on-startup设为 1 表示 Tomcat 启动时立刻初始化 Spring 容器,而不是等第一个请求进来才初始化,这样启动阶段就能看到 Spring 配置错误,而不是等在线用户触发。
连接数据库的配置放在 spring-mybatis.xml 里,这文件也是 404、500 错误的集中营:
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"> <constructor-arg> <bean class="com.zaxxer.hikari.HikariConfig"> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/sports_contest?useUnicode=true&characterEncoding=utf8mb4&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"/> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="username" value="root"/> <property name="password" value="你的密码"/> </bean> </constructor-arg> </bean>注意 jdbcUrl 有两个反直觉的点:一是useSSL=false一定要显式写,否则 MySQL 8 默认趋向 SSL 安全连接,本地环境经常会握手失败;二是allowPublicKeyRetrieval=true,这是 MySQL 8 新增的参数,不写就会报Public Key Retrieval is not allowed。把driverClassName写成com.mysql.cj.jdbc.Driver,注意“cj”两个字不能省,写旧驱动类名com.mysql.jdbc.Driver在 MySQL 8 下也会提示找不到类。
4.3 在 IDEA 里配置 Tomcat 运行的完整动作
项目准备好后,很多人卡在 IDEA 不知道怎么把它跑起来。这里按步骤给出我一般会走的路,按顺序操作基本不会翻车:
第一步,File → Project Structure → Artifacts,确认 Web Application 的 Exploded 包存在,并确认其 Output Layout 里有依赖包,如果没有,右键 Add Copy of Configuration Files 把 WEB-INF/classes 加进去。这一步不做,启动后控制台会报一大堆 ClassNotFoundException。
第二步,Run → Edit Configurations,点左上角加号选 Tomcat Server → Local。再切到 Deployment 页签,点加号选择 Artifact,关键是 Application Context 这一栏。我一般统一填/sports,这样项目访问路径是http://localhost:8080/sports,不会出现“项目根路径后面还带个随机后缀”的可访问性问题。
第三步,切到 Server 页签,把 On Update Action 设置为 Redeploy,这样修改代码后 IDEA 会重新部署,而不是 accumulate 旧 class。Open Browser 可以取消勾选,避免每次弹新页面。
第四步,启动。如果能在控制台看到以下输出,说明 JavaWeb 项目骨架已经通了:
Using CATALINA_BASE: ...\apache-tomcat-9.0.65 Starting ProtocolHandler ["http-nio-8080"] Deploying web application archive ... Context path: /sports到这里,访问http://localhost:8080/sports/login.jsp能打开登录页,就说明从 IDEA 到 Tomcat 再到 SpringMVC 的最小链路已经跑通。
5. 避坑排查:JavaWeb 体育竞赛系统的五个必踩之坑与对症解决
5.1 MySQL 8 连接时报 Public Key Retrieval is not allowed
现象:Tomcat 启动不报错,但一查数据库页面就抛异常,控制台出现Public Key Retrieval is not allowed。
原因:MySQL 8 默认使用 caching_sha2_password 加密方式,客户端第一次连接需要向服务器请求公钥做 RSA 加密,但 JDBC 驱动默认不允许自动获取公钥。
解决:在 jdbcUrl 中追加allowPublicKeyRetrieval=true&useSSL=false。同时确认 MySQL 用户是root@localhost,不要用root@'%'。
5.2 启动时 ClassNotFoundException: com.mysql.cj.jdbc.Driver
现象:IDEA 控制台直接抛java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver。
原因:pom.xml 里没有引 mysql-connector-java,或者引了旧版本 5.1.x。旧驱动类名是com.mysql.jdbc.Driver,新版是com.mysql.cj.jdbc.Driver,两个类名看着像但是两个不同的类。
解决:pom.xml 使用 8.0.33 版本依赖,并确认依赖是compile而非provided,如果是provided作用域,Tomcat 运行时也拿不到。
5.3 JSP 页面中文全部变成问号或乱码
现象:页面标题、查询结果里的中文都显示为???,或者打开页面是乱码,但正常启动。
原因:分三层断开。第一,Tomcat 的 server.xml 里 Connector 没有配置URIEncoding="UTF-8";第二,web.xml 里的 CharacterEncodingFilter 没有生效;第三,数据库连接串里没有指定characterEncoding=utf8mb4。三层里任何一层断掉都会出现。
解决:三个地方一起改。server.xml 的 8080 Connector 加URIEncoding="UTF-8";web.xml 里按 4.2 节配置过滤器;jdbcUrl 里显式写characterEncoding=utf8mb4。改完重启 Tomcat,刷新浏览器。这个坑我每年都能遇到,属于玄学式乱码,但对症下药就是这三处。
5.4 Invalid bound statement (not found): com.sports.contest.mapper.UserMapper.findByName
现象:Controller 能正常进入,但调用 Mapper 方法时抛org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)。
原因:MyBatis 的 Mapper 接口找到了,但 Mapper XML 文件没被识别。一般是两个原因:mapper XML 放在了src/main/java目录,Maven 默认只打包 resources 下的 XML;或者 spring-mybatis.xml 里mapper-locations指定了classpath:mapper/*.xml,但 XML 文件没在根路径。
解决:把 XML 文件移到src/main/resources/mapper/下;pom.xml 的<build><resources>里补充包含src/main/java下的 xml 文件也可以,但最省事的做法是统一放 resources。确认 spring-mybatis.xml 里设置了basePackage="com.sports.contest.mapper"和mapperLocations="classpath:mapper/*.xml"。
5.5 每次修改 JSP 都要重启 Tomcat,启动还报内存溢出
现象:改动一个 JSP 页面后刷新浏览器不生效,必须手动 Redeploy,频繁操作后 IDEA 弹OutOfMemoryError: PermGen space或Metaspace。
原因:Tomcat 默认部署模式没开热部署,JSP 改动没有触发编译;IDEA 分配给 Tomcat 的 JVM 内存太小,反复 restart 累积了旧 class。
解决:在 Run Configurations 的 Server 页签,设置 On Update Action 为 Redeploy,并在 VM options 里加一段-Xms256m -Xmx512m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m。如果是 Tomcat 9,只要改 JSP 文件本身,IDEA 会自动重新编译 JSP 而不用重启整个服务,这是 JSP 比 Servlet 在开发期的最大优势,别把热部署功能浪费了。
6. 上策在这:赛程编排和权限拦截器让“展示型毕设”变成“可运行系统”
6.1 用拦截器做粗粒度权限:不引入 Shiro 也够答辩用
权限这块很多毕设会选择集成 Spring Security 或 Shiro,但我觉得在体育竞赛管理系统里是杀鸡用牛刀。直接用 SpringMVC 的 HandlerInterceptor 做两个拦截就够:登录拦截和角色拦截。
做法是写一个LoginInterceptor,实现HandlerInterceptor接口的preHandle方法,检查 session 里是否存在登录用户;如果是去 admin 开头的路径,再检查用户的 role_id 是否为 1。在 spring-mvc.xml 里配置:
<mvc:interceptors> <interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/login.jsp"/> <mvc:exclude-mapping path="/user/login"/> <mvc:exclude-mapping path="/css/**"/> <mvc:exclude-mapping path="/js/**"/> </interceptor> </mvc:interceptors>逻辑说明:/**表示拦截全部请求,但要把登录页、登录接口和静态资源排除掉,否则用户还没登录就被重定向到登录页,形成死循环。这个配置在答辩时可以直接答出来:为什么不抽权限表?因为角色只有管理员和运动员两种,用拦截器做粗细粒度控制在设计上更简单,也符合最小实现原则。
6.2 验证业务代码的两种实用手段:事务回滚和成绩排序
系统跑通后,一定要做一次全链路自测。我通常用两个手段验证。第一,事务回滚测试:在报名 Service 方法里同时执行插入报名表和更新报名人数两个操作,故意让第二个操作失败,观察第一个操作是否回滚,如果回滚了说明@Transactional配置生效。第二,成绩排名验证:录入一组故意相同的小数成绩,确认ORDER BY score ASC, signup_time ASC能稳定给出名次。
最后收个尾。做毕设时我的一个习惯是每完成一个功能点,就把浏览器地址栏里对应的 URL 和关键报错贴进一个本地 README 里,这样答辩演示时不用临时回忆功能入口,而且出了问题能快速定位是哪个 Controller。这个题目本身不难,但把赛程编排、报名去重、权限拦截和事务管理这四处做好,就已经比大多数“仓库里只有增删改查”的 JavaWeb 项目完整案例更像一个真正能运转的管理系统。希望帮到你。
本文还有配套的精品资源,点击获取