LambdaQueryWrapper遇上@Async

作者:京东科技 张新磊

文章背景

最近在测试业务需求时通读了研发指定需求的代码,发现研发大佬们用到了如下的内容,有些内容我还不是十分的清楚,比如下述真实代码;作为后端大佬肯定炉火纯青,但是我刚刚看到这段代码时确实有点懵;

快速理解的方式

直接借助joycoder解释代码的能力就可以快速理解

于是乎有了下述的探索

但是我为了理解的透彻点还是又去翻找了一些其它资料做一个记录吧,后续万一在遗忘了也方便快速查找

MyBatis-Plus的LambdaQueryWrapper简介

LambdaQueryWrapper是MyBatis-Plus提供的一种类型安全的查询条件构造器,它利用Java 8的Lambda表达式特性,避免了硬编码字段名,提高了代码的可读性和可维护性。

基本用法示例

ini 复制代码
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getName, "张三")
           .ge(User::getAge, 18)
           .orderByDesc(User::getCreateTime);
List userList = userMapper.selectList(queryWrapper);

LambdaQueryWrapper的优势

类型安全:通过方法引用而非字符串指定字段,编译器可检查类型

代码可读性高:链式调用,语义清晰

防止SQL注入:自动处理参数绑定

智能提示:IDE可自动补全字段名

Spring Boot的@Async异步处理

@Async是Spring框架提供的注解,用于标记方法为异步执行。被@Async注解的方法会在调用时立即返回,而实际执行将发生在单独的线程中。

基本配置

首先需要在Spring Boot启动类或配置类上添加@EnableAsync注解:

less 复制代码
@SpringBootApplication
@EnableAsync
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

简单使用示例

typescript 复制代码
@Service
public class AsyncService {

    @Async
    public void asyncMethod() {
        // 这个方法将在单独的线程中执行
        System.out.println("执行异步方法: " + Thread.currentThread().getName());
    }
}

LambdaQueryWrapper与@Async的结合实践

将两者结合使用可以实现高效的异步数据库操作,特别适合那些不需要立即返回结果的复杂查询或批量操作。

示例1:异步查询用户列表

less 复制代码
@Service
@RequiredArgsConstructor
public class UserService {
    
    private final UserMapper userMapper;
    
    @Async
    public CompletableFuture> asyncFindUsers(String name, Integer minAge) {
        LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(name), User::getName, name)
                   .ge(minAge != null, User::getAge, minAge);
        
        List users = userMapper.selectList(queryWrapper);
        return CompletableFuture.completedFuture(users);
    }
}

示例2:异步统计与保存

ini 复制代码
@Async
public void asyncStatAndSave(Long departmentId) {
    // 统计部门人数
    LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(User::getDepartmentId, departmentId);
    long count = userMapper.selectCount(queryWrapper);
    
    // 更新统计结果
    Department department = new Department();
    department.setId(departmentId);
    department.setUserCount(count);
    departmentMapper.updateById(department);
    
    // 记录统计日志
    StatLog statLog = new StatLog();
    statLog.setDepartmentId(departmentId);
    statLog.setCount(count);
    statLog.setStatTime(LocalDateTime.now());
    statLogMapper.insert(statLog);
}

高级应用与优化

自定义线程池配置

默认情况下,@Async使用SimpleAsyncTaskExecutor,这不是生产环境的最佳选择。我们可以自定义线程池:

java 复制代码
@Configuration
public class AsyncConfig implements AsyncConfigurer {
    
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(50);
        executor.setQueueCapacity(100);
        executor.setThreadNamePrefix("AsyncExecutor-");
        executor.initialize();
        return executor;
    }
}

异常处理

异步方法的异常不会传播到调用线程,需要特别处理:

typescript 复制代码
@Async
public CompletableFuture> asyncFindUsersWithExceptionHandling(String name) {
    try {
        LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(User::getName, name);
        List users = userMapper.selectList(queryWrapper);
        return CompletableFuture.completedFuture(users);
    } catch (Exception e) {
        // 记录日志
        log.error("异步查询用户失败", e);
        // 返回空列表或抛出CompletionException
        return CompletableFuture.completedFuture(Collections.emptyList());
    }
}

事务处理

@Async方法的事务需要特别注意,默认情况下异步方法的事务不会传播:

less 复制代码
@Async
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void asyncUpdateWithTransaction(User user) {
    // 这个更新操作将在新事务中执行
    userMapper.updateById(user);
}

实际应用场景

后台报表生成

scss 复制代码
@Async
public void asyncGenerateUserReport(LocalDate startDate, LocalDate endDate, String reportPath) {
    LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.between(User::getCreateTime, startDate.atStartOfDay(), endDate.atTime(23, 59, 59))
               .orderByAsc(User::getCreateTime);
    
    List users = userMapper.selectList(queryWrapper);
    
    // 生成报表文件
    generateExcelReport(users, reportPath);
    
    // 发送通知
    sendReportReadyNotification(reportPath);
}

批量数据处理

ini 复制代码
@Async
public CompletableFuture asyncBatchProcessUsers(List userIds) {
    LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.in(User::getId, userIds);
    
    List users = userMapper.selectList(queryWrapper);
    
    int processedCount = 0;
    for (User user : users) {
        if (processUser(user)) {
            processedCount++;
        }
    }
    return CompletableFuture.completedFuture(processedCount);
}

性能考量与最佳实践

合理使用异步:不是所有数据库操作都适合异步,简单查询同步执行可能更高效

控制并发量:避免过多并发数据库连接导致系统资源耗尽

批量操作优化:考虑使用MyBatis-Plus的批量操作方法

结果处理:使用CompletableFuture可以方便地处理异步结果

监控:监控异步任务的执行情况和线程池状态

总结

MyBatis-Plus的LambdaQueryWrapper与Spring Boot的@Async注解的结合,为Java后端开发提供了强大的工具组合。LambdaQueryWrapper提供了类型安全、优雅的查询构建方式,而@Async则让异步编程变得简单。合理使用这两者可以显著提高应用程序的响应速度和处理能力,特别是在处理复杂查询、批量操作和后台任务时。

在实际项目中,开发者应根据具体场景选择合适的技术组合,并注意线程池配置、异常处理和事务管理等关键点,以确保系统的稳定性和可靠性。

相关推荐
摆烂工程师2 小时前
是的!wildcard野卡复活了,已经恢复了服务!(wildcard野卡无法充值的原因)
程序员
AI大模型6 小时前
拒绝“纸上谈兵”!这本《图解大模型:生成式 AI 原理与实战》太适合入门大模型了
程序员·llm·deepseek
CodeSheep10 小时前
学硕停招!研究生重大变革,来了
前端·后端·程序员
redreamSo12 小时前
AI Daily | AI日报:谷歌24亿挖角,Windsurf员工血本无归; Grok多次‘发疯’,还被塞进特斯拉?; Grok 4陷「希特勒门」,安全引争议
程序员·aigc·资讯
袁煦丞14 小时前
ONLYOFFICE让局域网外也能实时轻松协作?cpolar内网穿透实验室第510个成功挑战
前端·程序员·远程工作
SimonKing15 小时前
告别System.currentTimeMillis()!Java高精度计时最佳实践
java·后端·程序员
亚雷15 小时前
深入浅出 MySQL Buffer Pool
后端·面试·程序员
redreamSo16 小时前
拥有自由的人,会想什么是自由吗?
程序员