Implementing Department and Employee Management Modules in Spring Boot
Application Architecture Overview
To build a maintainable enterprise application, the codebase is separated into distinct layers. The standard architecture for Spring Boot applications consists of the following tiers:
- Controller Layer: Handles incoming HTTP requests, processes input parameters, invokes the service layer, and returns the response to the client. It acts as the entry point for the application.
- Service Layer: Contains the business logic. It orchestrates data flow and transforms entities. This layer communicates with the data access layer and ensures transactional integrity.
- Data Access Layer (Mapper/DAO): Responsible for interacting with the database. It performs CRUD operations using MyBatis or similar ORM tools.
- Entity Layer (Model/POJO): Plain Old Java Objects that map directly to database tables. Each field in the class typically corresponds to a column in the respective table.
RESTful API Design Standards
The application adheres to RESTful principles for resource management. HTTP methods are mapped to specific actions:
- GET: Retrieve resources.
- POST: Create a new resource.
- PUT: Update an existing resource.
- DELETE: Remove a resource.
Resource endpoints are defined using plural nouns (e.g., /departments, /employees). All API responses are wrapped in a unified ResponseDTO structure to ensure consistent error handling and data formatting across the client-side.
Department Management Implementation
Retrieving Department List
The flow begins at the controller, which handles the GET request. Logging is implemented using the Slf4j annotation to track execution.
@RestController
@RequestMapping("/api/departments")
@RequiredArgsConstructor
public class DepartmentController {
private final DepartmentService departmentService;
@GetMapping
public ResponseDTO<List<Department>> listAll() {
log.info("Retrieving all departments");
List<Department> deptList = departmentService.getAllDepartments();
return ResponseDTO.success(deptList);
}
}
The Service layer delegates the database call to the Mapper interface.
@Service
@RequiredArgsConstructor
public class DepartmentServiceImpl implements DepartmentService {
private final DepartmentMapper departmentMapper;
@Override
public List<Department> getAllDepartments() {
return departmentMapper.selectList();
}
}
The Mapper uses MyBatis annotations to execute the SQL query.
@Mapper
public interface DepartmentMapper {
@Select("SELECT id, name, create_time, update_time FROM tlias_dept")
List<Department> selectList();
}
Deleting a Department
Deletion is triggered via a DELETE request. The controller accepts the department ID as a path variable.
@DeleteMapping("/{id}")
public ResponseDTO<Void> delete(@PathVariable Integer id) {
log.info("Deleting department with id: {}", id);
departmentService.removeDepartment(id);
return ResponseDTO.success();
}
Adding a New Department
When creating a resource, the client sends a POST request with a JSON payload. The @RequestBody annotation binds the JSON body to a Java object. The Service layer is responsible for populating audit fields like createTime and updateTime before persistence.
@PostMapping
public ResponseDTO<Void> add(@RequestBody Department department) {
log.info("Adding new department: {}", department.getName());
departmentService.createDepartment(department);
return ResponseDTO.success();
}
Updating a Department
Updates utilize the PUT method. The controller receives the full object, and the Mapper executes an update statement. The Controller path can be simplified using @RequestMapping at the class level, with specific mappings at the method level.
@PutMapping
public ResponseDTO<Void> update(@RequestBody Department department) {
departmentService.modifyDepartment(department);
return ResponseDTO.success();
}
Employee Management Implementation
The employee module introduces complexity through pagination and conditional filtering.
Pagination with PageHelper
Manual pagination requires calculating offsets and limits manually. To streamline this, the PageHelper plugin is integrated. It intercepts SQL queries and automatically appends pagination logic.
@GetMapping
public ResponseDTO<PageResult<Employee>> getPage(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize) {
PageHelper.startPage(page, pageSize);
List<Employee> list = employeeMapper.selectList();
PageInfo<Employee> info = new PageInfo<>(list);
PageResult<Employee> result = new PageResult<>(info.getTotal(), info.getList());
return ResponseDTO.success(result);
}
Conditional Dynamic Queries
Searching requires filtering by name, gender, and date range. This is implemented using MyBatis Dynamic SQL in XML configuration. The Service layer sets the pagination parameters before invoking the mapper.
@GetMapping("/search")
public ResponseDTO<PageResult<Employee>> search(
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
String name,
Short gender,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {
PageHelper.startPage(page, pageSize);
List<Employee> list = employeeMapper.selectByCondition(name, gender, begin, end);
Page<Employee> p = (Page<Employee>) list;
return ResponseDTO.success(new PageResult<>(p.getTotal(), p.getResult()));
}
The corresponding Mapper XML uses <if> tags to conditionally append SQL clauses.
<select id="selectByCondition" resultType="Employee">
SELECT * FROM tlias_emp
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="gender != null">
AND gender = #{gender}
</if>
<if test="begin != null and end != null">
AND entrydate BETWEEN #{begin} AND #{end}
</if>
</where>
</select>
Bulk Operations
Bulk deletion accepts a list of IDs. The Mapper uses the <foreach> tag to generate the IN clause dynamically.
@DeleteMapping("/{ids}")
public ResponseDTO<Void> deleteBatch(@PathVariable List<Integer> ids) {
employeeMapper.deleteBatch(ids);
return ResponseDTO.success();
}