Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Employee Management in Spring Boot: Pagination, Status Toggle, Editing, and Automated Common Field Population

Tech 1
@GetMapping("/page")
@ApiOperation("Employee Pagination Query")
public Result<PageResult> listByPage(StaffQueryDTO queryParams) {
    log.info("Executing employee pagination query with params: {}", queryParams);
    PageResult paginationResult = staffService.getPagedList(queryParams);
    return Result.success(paginationResult);
}

PageHelper Dependency Configuration

Integrate the PageHelper starter for pagination support:

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>${pagehelper.version}</version>
</dependency>

Service Layer Pagination Implementation

@Override
public PageResult getPagedList(StaffPageQueryDTO pageQueryParams) {
    // Initialize pagination with requested page number and size
    PageHelper.startPage(pageQueryParams.getPageNum(), pageQueryParams.getPageSize());
    Page<Staff> page = staffMapper.queryPagedList(pageQueryParams);
    
    long totalRecords = page.getTotal();
    List<Staff> staffList = page.getResult();
    
    return new PageResult(totalRecords, staffList);
}

MyBatis Mapper Query

<select id="queryPagedList" resultType="com.sky.entity.Staff">
    SELECT * FROM staff
    <where>
        <if test="name != null and name != ''">
            <bind name="likeName" value="'%' + name + '%'"/>
            AND name LIKE #{likeName}
        </if>
    </where>
    ORDER BY create_time DESC
</select>

Global Date Formatting

To standardize date-time serialization between backend and frontend, two approaches exist:

  1. Annotation-based (not recommended) Apply @JsonFormat directly to entity fields for one-off formatting:
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
    
  2. Global message converter extension Configure a centralized converter to handle all JSON date formatting:
    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("Configuring custom JSON message converter for date formatting");
        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        jsonConverter.setObjectMapper(new CustomJacksonObjectMapper());
        // Prioritize this converter over default implementations
        converters.add(0, jsonConverter);
    }
    

Enable/Disable Employee Status

For state modification operations, return a non-generic Result since no data payload is needed. Use dynamic SQL and partial entity objects to avoid overwriting existing fields:

Staff staff = Staff.builder()
    .id(staffId)
    .status(targetStatus)
    .build();
staffService.updateStatus(staff);

MyBatis dynamic SQL skips null values in UDPATE clauses, so unpopulated entity fields won't overwrite valid database data.

Edit Employee Functionality

The edit feature relies on two core APIs:

  1. Retrieve employee details by ID to populate the edit form.
  2. Udpate employee records with submitted changes.

When extending the system with related modules (e.g., category management), follow this dependency-aware order:

  1. Copy Mapper interfaces and XML files.
  2. Implement Service interfaces and their concrete classes.
  3. Add Controller endpoints.
  4. Validate API definitions via Swagger or documentation tools.

Automated Common Field Population

Eliminate repetitive code for setting audit fields (create_time, create_user, update_time, update_user) using AOP. This workflow includes three key steps:

1. Custom Annotation for Target Methods

Create an annotation to identify methods reuqiring auto-population:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoPopulate {
    DbOperationType value();
}

Where DbOperationType is an enum defining INSERT and UPDATE operations.

2. AOP Aspect for Field Injection

Implement an aspect to intercept annotated methods and populate fields via reflection:

@Aspect
@Component
@Slf4j
public class AutoPopulateAspect {

    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoPopulate)")
    public void autoPopulatePointcut() {}

    @Before("autoPopulatePointcut()")
    public void handleAutoPopulate(JoinPoint joinPoint) {
        log.info("Starting common field auto-population");
        // Extract operation type from method annotation
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        AutoPopulate autoPopulate = methodSignature.getMethod().getAnnotation(AutoPopulate.class);
        DbOperationType operationType = autoPopulate.value();

        // Retrieve target entity (assumed to be the first method parameter)
        Object[] args = joinPoint.getArgs();
        if (args == null || args.length == 0) return;
        Object entity = args[0];

        // Prepare common field values
        LocalDateTime currentTimestamp = LocalDateTime.now();
        Long currentUserId = BaseContext.getCurrentUserId();

        try {
            if (operationType == DbOperationType.INSERT) {
                // Populate all four audit fields for INSERT operations
                Method setCreateTime = entity.getClass().getDeclaredMethod(AutoPopulateConstants.SET_CREATE_TIME, LocalDateTime.class);
                Method setCreateUser = entity.getClass().getDeclaredMethod(AutoPopulateConstants.SET_CREATE_USER, Long.class);
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoPopulateConstants.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoPopulateConstants.SET_UPDATE_USER, Long.class);

                setCreateTime.invoke(entity, currentTimestamp);
                setCreateUser.invoke(entity, currentUserId);
                setUpdateTime.invoke(entity, currentTimestamp);
                setUpdateUser.invoke(entity, currentUserId);
            } else if (operationType == DbOperationType.UPDATE) {
                // Populate only update-related fields
                Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoPopulateConstants.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoPopulateConstants.SET_UPDATE_USER, Long.class);

                setUpdateTime.invoke(entity, currentTimestamp);
                setUpdateUser.invoke(entity, currentUserId);
            }
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            log.error("Failed to auto-populate common fields: {}", e.getMessage(), e);
            throw new RuntimeException("Field population failed", e);
        }
    }
}
3. Annotate Mapper Methods

Mark relevant Mapper methods to trigger auto-population:

@AutoPopulate(value = DbOperationType.UPDATE)
void updateStaff(Staff staff);

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.