1. 项目概述与技术栈选型
这个基于SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0的学生选课系统,是一个典型的现代化全栈Web应用。我在实际开发中发现,这种技术组合特别适合中小型教育机构的管理系统开发,既能保证后端服务的稳定性,又能提供流畅的前端交互体验。
核心架构采用前后端分离模式,后端基于SpringBoot2构建RESTful API,前端使用Vue3实现响应式界面,数据持久层采用MyBatis-Plus简化数据库操作,MySQL8.0作为关系型数据库存储业务数据。这种技术组合在当前Java Web开发领域非常主流,我在多个教育类项目中都有成功应用经验。
提示:MySQL8.0相比5.7版本在性能上有显著提升,特别是对JSON数据类型的支持更加完善,这在处理选课系统中的复杂业务数据时非常有用。
2. 开发环境准备与项目初始化
2.1 后端环境配置
首先需要安装JDK 1.8或以上版本(推荐JDK11),配置好JAVA_HOME环境变量。SpringBoot2.x对JDK版本有较好的向下兼容性,但建议使用LTS版本以获得长期支持。
bash复制# 验证Java环境
java -version
javac -version
接着安装Maven 3.6+用于依赖管理。在pom.xml中需要配置以下核心依赖:
xml复制<dependencies>
<!-- SpringBoot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis-Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- MySQL Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
</dependencies>
2.2 前端环境搭建
前端需要安装Node.js 16+和Vue CLI 5.x。Vue3相比Vue2在性能上有显著提升,特别是Composition API让代码组织更加灵活。
bash复制# 安装Vue CLI
npm install -g @vue/cli
# 创建Vue3项目
vue create student-course-frontend
在项目初始化时,建议选择手动配置,勾选Router、Vuex、TypeScript等核心功能。我在实际项目中发现,TypeScript能显著提高大型前端项目的可维护性。
3. 数据库设计与实现
3.1 MySQL8.0表结构设计
学生选课系统的核心表包括:
- 学生表(student)
- 课程表(course)
- 教师表(teacher)
- 选课记录表(selection)
- 院系表(department)
以下是学生表的DDL示例:
sql复制CREATE TABLE `student` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`student_no` varchar(20) NOT NULL COMMENT '学号',
`name` varchar(50) NOT NULL COMMENT '姓名',
`gender` tinyint DEFAULT '0' COMMENT '性别(0-未知,1-男,2-女)',
`birthday` date DEFAULT NULL COMMENT '出生日期',
`department_id` bigint DEFAULT NULL COMMENT '所属院系ID',
`status` tinyint DEFAULT '1' COMMENT '状态(0-禁用,1-正常)',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_student_no` (`student_no`),
KEY `idx_department` (`department_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生信息表';
3.2 MyBatis-Plus实体类映射
使用MyBatis-Plus可以大大简化数据访问层的开发。对应的Student实体类如下:
java复制@Data
@TableName("student")
public class Student {
@TableId(type = IdType.AUTO)
private Long id;
private String studentNo;
private String name;
private Integer gender;
private LocalDate birthday;
private Long departmentId;
private Integer status;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
注意:MySQL8.0的默认字符集是utf8mb4,可以完整支持emoji等特殊字符,建议在连接字符串中明确指定:
jdbc:mysql://localhost:3306/course_system?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
4. 核心业务功能实现
4.1 选课逻辑实现
选课业务需要考虑并发选课的情况,避免超选。我采用乐观锁+数据库事务的方式保证数据一致性:
java复制@Service
@Transactional(rollbackFor = Exception.class)
public class CourseSelectionServiceImpl implements CourseSelectionService {
@Autowired
private CourseMapper courseMapper;
@Autowired
private SelectionMapper selectionMapper;
@Override
public boolean selectCourse(Long studentId, Long courseId) {
// 检查课程是否可选
Course course = courseMapper.selectById(courseId);
if (course == null || course.getStatus() != 1) {
throw new BusinessException("课程不存在或不可选");
}
// 检查是否已选
Integer count = selectionMapper.selectCount(
new QueryWrapper<Selection>()
.eq("student_id", studentId)
.eq("course_id", courseId)
);
if (count > 0) {
throw new BusinessException("不能重复选课");
}
// 检查名额是否已满(乐观锁)
int updated = courseMapper.updateCourseCapacity(
courseId,
course.getSelectedNumber(),
course.getCapacity()
);
if (updated == 0) {
throw new BusinessException("选课人数已满");
}
// 创建选课记录
Selection selection = new Selection();
selection.setStudentId(studentId);
selection.setCourseId(courseId);
selection.setSelectionTime(LocalDateTime.now());
selection.setStatus(1);
return selectionMapper.insert(selection) > 0;
}
}
4.2 Vue3前端页面开发
使用Vue3的Composition API开发选课页面:
vue复制<template>
<div class="course-selection">
<el-table :data="courses" style="width: 100%">
<el-table-column prop="name" label="课程名称" />
<el-table-column prop="teacherName" label="授课教师" />
<el-table-column prop="credit" label="学分" />
<el-table-column prop="selectedNumber" label="已选人数" />
<el-table-column prop="capacity" label="容量" />
<el-table-column label="操作">
<template #default="scope">
<el-button
size="small"
@click="handleSelect(scope.row)"
:disabled="scope.row.selectedNumber >= scope.row.capacity"
>
选课
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { getCourses, selectCourse } from '@/api/course'
const courses = ref([])
const loadCourses = async () => {
const res = await getCourses()
courses.value = res.data
}
const handleSelect = async (course) => {
try {
await selectCourse(course.id)
ElMessage.success('选课成功')
loadCourses()
} catch (error) {
ElMessage.error(error.message)
}
}
onMounted(() => {
loadCourses()
})
</script>
5. 系统安全与性能优化
5.1 接口安全防护
在SpringBoot中配置安全防护措施:
java复制@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // 前后端分离项目可以禁用CSRF
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
}
5.2 缓存优化
使用Redis缓存热门课程数据:
java复制@Service
public class CourseServiceImpl implements CourseService {
@Autowired
private CourseMapper courseMapper;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String CACHE_KEY = "course:popular";
@Override
@Cacheable(value = CACHE_KEY, key = "'list'")
public List<CourseVO> getPopularCourses() {
return courseMapper.selectPopularCourses();
}
@Override
@CacheEvict(value = CACHE_KEY, allEntries = true)
public void clearCourseCache() {
// 缓存清除由注解自动完成
}
}
6. 常见问题与解决方案
6.1 MyBatis-Plus分页查询
配置分页插件并实现分页查询:
java复制@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
// 使用分页查询
public PageResult<CourseVO> getCoursePage(PageParam param) {
Page<Course> page = new Page<>(param.getPageNum(), param.getPageSize());
IPage<CourseVO> result = courseMapper.selectCoursePage(page, param.getKeywords());
return new PageResult<>(result.getRecords(), result.getTotal());
}
6.2 Vue3与SpringBoot跨域问题
在SpringBoot中配置跨域支持:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.maxAge(3600);
}
}
7. 项目部署与运维
7.1 后端打包与运行
使用Maven打包SpringBoot应用:
bash复制mvn clean package -DskipTests
java -jar target/student-course-system.jar
7.2 前端打包与部署
构建生产环境的前端资源:
bash复制npm run build
将生成的dist目录内容部署到Nginx:
nginx复制server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
8. 开发经验分享
在实际开发这个选课系统的过程中,我积累了一些有价值的经验:
- 枚举类型处理:MyBatis-Plus中处理枚举类型时,建议实现IEnum接口并配置类型处理器:
java复制@Getter
public enum GenderEnum implements IEnum<Integer> {
UNKNOWN(0, "未知"),
MALE(1, "男"),
FEMALE(2, "女");
private final int value;
private final String desc;
GenderEnum(int value, String desc) {
this.value = value;
this.desc = desc;
}
@Override
public Integer getValue() {
return this.value;
}
}
- 前端性能优化:Vue3中大量使用v-for渲染列表时,务必为每项设置唯一的key,我通常使用数据库ID而不是数组索引:
vue复制<template>
<div v-for="course in courses" :key="course.id">
<!-- 课程项内容 -->
</div>
</template>
- 数据库连接池配置:生产环境建议使用HikariCP并合理配置连接池参数:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 30000
max-lifetime: 1800000
connection-timeout: 30000
这个选课系统虽然功能不算复杂,但涵盖了现代Java Web开发的完整技术栈。我在实现过程中特别注重代码的可维护性和系统的扩展性,为后续可能增加的功能如成绩管理、课表生成等预留了接口。
