mybatis-plus多租户情况下使用定时任务出现未绑定线程问题

1、启动类加上@EnableScheduling、@EnableAsync

2、创建定时任务类

@Component
public class updateContractHistory {

    private final ContractHistoryService contractHistoryService;

    @Autowired
    public updateContractHistory(ContractHistoryService contractHistoryService) {
        this.contractHistoryService = contractHistoryService;
    }

    @Scheduled(cron = "0 0 0  * * ? ")
    public void executeUpdateCountdownDays() {
		//定时任务业务逻辑
    }
}

3、多线程环境下配置多线程配置

package com.jsh.erp.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
@Configuration
public class TimerTaskConfig {
    @Bean
    public Executor taskTimeOutExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(30);
        executor.setThreadNamePrefix("MyTaskExecutor-");
        executor.initialize();
        return executor;
    }
}

4、以下是一个示例代码,展示了如何在Mybatis-Plus中配置租户解析器,以便在定时任务中使用:

@Service
public class TenantConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor(HttpServletRequest request) {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        List<ISqlParser> sqlParserList = new ArrayList<>();
        TenantSqlParser tenantSqlParser = new TenantSqlParser();
        tenantSqlParser.setTenantHandler(new TenantHandler() {
            @Override
            public Expression getTenantId() {
                String token = request.getHeader("X-Access-Token");
                Long tenantId = Tools.getTenantIdByToken(token);
                if (tenantId != 0L) {
                    return new LongValue(tenantId);
                } else {
                    //超管
                    return null;
                }
            }

            @Override
            public String getTenantIdColumn() {  //租户字段
                return "tenant_id";
            }

            @Override
            public boolean doTableFilter(String tableName) {
                //获取开启状态
                Boolean res = true;
                String token = request.getHeader("X-Access-Token");
                Long tenantId = Tools.getTenantIdByToken(token);
                if (tenantId != 0L) {
                    // 这里可以判断是否过滤表
                    if ("tyjt_employee_detail".equals(tableName) || "tyjt_form_template".equals(tableName)) {
                        res = true;
                    } else {
                        res = false;
                    }
                }
                return res;
            }
        });

        sqlParserList.add(tenantSqlParser);
        paginationInterceptor.setSqlParserList(sqlParserList);
        paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
            @Override
            public boolean doFilter(MetaObject metaObject) {
                MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);
                // 过滤自定义查询此时无租户信息约束出现
        });
        return paginationInterceptor;
    }

    /**
     * 相当于顶部的:
     * {@code @MapperScan("com.tyjt.erp.datasource.mappers*")}
     * 这里可以扩展,比如使用配置文件来配置扫描Mapper的路径
     */
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
        scannerConfigurer.setBasePackage("com.xxx.xxx.xxx.mappers*");
        return scannerConfigurer;
    }

    /**
     * 性能分析拦截器,不建议生产使用
     */
//    @Bean
//    public PerformanceInterceptor performanceInterceptor(){
//        return new PerformanceInterceptor();
//    }


}

其中,getTenantId()方法从请求中获取租户ID,并返回一个LongValue对象;getTenantIdColumn()方法返回租户ID所在的列名;doTableFilter()方法用于过滤不需要使用租户ID的表。

5、如果报没有线程绑定错误,添加一个线程绑定就可以了。

@Component
public class updateContractHistory {

    private final ContractHistoryService contractHistoryService;

    @Autowired
    public updateContractHistory(ContractHistoryService contractHistoryService) {
        this.contractHistoryService = contractHistoryService;
    }

    @Scheduled(cron = "0 0 0  * * ? ")
    public void executeUpdateCountdownDays() {

        // 获取当前线程绑定的RequestAttributes,如果不存在则创建一个新的
        RequestAttributes obj = RequestContextHolder.getRequestAttributes();

        // 检查获取的RequestAttributes是否为空
        if (obj == null) {
            // 创建一个新的ServletRequestAttributes对象,并传入一个NonWebRequestAttributes实例作为参数
            // 这里NonWebRequestAttributes是一个自定义类,用于处理非Web请求的场景,实现HttpServletRequest接口并重写所有方法即可(重写的方法不进行逻辑编写)
            obj = new ServletRequestAttributes(new NonWebRequestAttributes());
        }
        // 将创建或获取的RequestAttributes重新绑定到当前线程
        RequestContextHolder.setRequestAttributes(obj);

        //接下来写对于的定时任务的业务即可

    }
}

大部分内容转自如下
原文链接:https://blog.csdn.net/m0_48114733/article/details/131631351

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值