This article presents a practical implementation of a student information management system built using Spring Boot, Vue.js, and MySQL. The system provides RESTful APIs for mangaing student records, including CRUD operations, search, and pagination. The frontend is developed with Vue 3 and Element Plus, while the backend uses Spring Boot 2.x with MyBatis-Plus for data access.
System Architecture Overview
The project follows a standard separation of concerns:
- Backend: Spring Boot, MyBatis-Plus, MySQL (or optional SQL Server)
- Frontend: Vue 3, Vue Router, Axios, Element Plus
- Communication: RESTful JSON APIs
Key Technical Decisions
- Spring Boot Auto-Configuration: Reduces boilerplate configuration. Application properties control data source, file upload limits, and static resource locations.
- MyBatis-Plus: Extends MyBatis with pagination plugins, code generator, and Lambda wrapper for type-safe queries.
- Vue 3 + Composition API: Uses reactive refs and computed properties for state management, Axios for HTTP calls.
- MySQL: InnoDB engine with UTF-8 encoding supports Chinese characters without issues.
Backend Core Code Example
The following code demonstrates an EmployeeController that handles employee (teacher) management. Compared to the original code, it uses a different entity (Employee), adds service layer abstraction, and implements paginated queries with Lambda conditions.
@RestController
@RequestMapping("/api/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@GetMapping("/page")
public Result<Page<Employee>> getPage(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String name) {
LambdaQueryWrapper<Employee> wrapper = Wrappers.lambdaQuery();
if (StrUtil.isNotBlank(name)) {
wrapper.like(Employee::getEmployeeName, name);
}
wrapper.orderByDesc(Employee::getCreateTime);
Page<Employee> p = employeeService.page(new Page<>(page, size), wrapper);
return Result.success(p);
}
@PostMapping
public Result<String> add(@RequestBody Employee employee) {
employee.setCreateTime(new Date());
boolean saved = employeeService.save(employee);
return saved ? Result.success("Added") : Result.error("Failed");
}
@PutMapping
public Result<String> update(@RequestBody Employee employee) {
boolean updated = employeeService.updateById(employee);
return updated ? Result.success("Updated") : Result.error("Failed");
}
@DeleteMapping("/{id}")
public Result<String> delete(@PathVariable Long id) {
boolean removed = employeeService.removeById(id);
return removed ? Result.success("Deleted") : Result.error("Failed");
}
}
The Employee entity is defined with fields like employeeId, employeeName, gender, position, phone, and email, all mapped via annotations. The mapper XML (not shown) contains similar result mapping but uses different table and column names.
Database Configuration (application.yml)
Below is a revised configuration file. It retains the core datasource settings but uses a different database name and removes unnecessary SQL Server leftovers.
server:
port: 8080
servlet:
context-path: /sms
spring:
datasource:
url: jdbc:mysql://localhost:3306/student_management?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: yourpassword
driver-class-name: com.mysql.cj.jdbc.Driver
servlet:
multipart:
max-file-size: 50MB
max-request-size: 50MB
resources:
static-locations: classpath:static/,file:static/
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
type-aliases-package: com.example.entity
global-config:
db-config:
id-type: auto
logic-delete-field: deleted
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
Frontend Vue Component (EmployeeList.vue)
The following script uses Composition API to fetch paginated data and handle delete. The template uses Element Plus tables and dialogs.
<script setup>
import { ref, onMounted } from 'vue'
import { getEmployeePage, deleteEmployee } from '@/api/employee'
const tableData = ref([])
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(10)
const searchName = ref('')
const loadData = async () => {
const res = await getEmployeePage({
page: currentPage.value,
size: pageSize.value,
name: searchName.value
})
tableData.value = res.data.records
total.value = res.data.total
}
const handleDelete = async (id) => {
await deleteEmployee(id)
loadData()
}
onMounted(() => {
loadData()
})
</script>
Summary
This project demonstrates a clean separation of frontend and backend with modern tools like MyBatis-Plus and Vue 3. The code can serve as a foundation for more complex school management systems, such as adding course scheduling or grade modules.