mybatisplus 流式查询

Dao.class 方法

    @Select("select ${ew.sqlSelect} from t_app_usermlq t ${ew.customSqlSegment}")
    @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000)
    @ResultType(AppUserEntity.class)
    void getUserListBigData(@Param(Constants.WRAPPER) QueryWrapper<AppUserEntity> wrapper, ResultHandler<AppUserEntity> handler);

service.class方法调用

    /**
     * @author yangjunxiong
     * @date 2020/12/23 10:51
     * @param sqlSelect 需要返回的字段 逗号分隔 字段越少查询越快
     * @param subDeptIdList
     * @return java.util.List<com.allianity.common.learning.entity.AppUserEntity>
     **/
    @Override
    public List<AppUserEntity> getUserListBigData(String sqlSelect,List<Long> subDeptIdList) {
        List<AppUserEntity> appUserEntities = new ArrayList<>();
        QueryWrapper<AppUserEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(sqlSelect);
        queryWrapper.eq("status", 0);
        queryWrapper.eq("del_flag", 0);
        queryWrapper.in("dept_id", subDeptIdList);
        //流式条件查询
        this.baseMapper.getUserListBigData(queryWrapper, resultContext -> Optional.ofNullable(resultContext.getResultObject())
                .ifPresent(appUserEntities::add));
        return appUserEntities;
    }

demo2
批量处理

一、在需要使用流式查询的mapper文件中,定义流式查询方法

package com.unionpay.dao.db2;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.unionpay.entity.TblMallOrder;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.session.ResultHandler;

@Mapper
public interface TblMallOrderDao extends BaseMapper<TblMallOrder> {
    @Select("${sql}")
    @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000)
    @ResultType(TblMallOrder.class)
    void dynamicSelectLargeData1(@Param("sql") String sql, ResultHandler<TblMallOrder> handler);

    @Select("${sql}")
    @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000)
    @ResultType(Map.class)
    void dynamicSelectLargeData2(@Param("sql") String sql, ResultHandler<Map> handler);
}

二、使用示例

@RestController
public class TestSearchLargeData {
    // 这是每批处理的大小
    private final static int BATCH_SIZE = 1000;
    private int size;
    // 存储每批数据的临时容器
    private List<TblMallOrder> mallOrders;
    

    @Autowired
    private TblMallOrderDao tblMallOrderDao;

    @GetMapping("/getLargeData1")
    public void getLargeData1() {
        String sql = "select * from t_mall_order";
        tblMallOrderDao.dynamicSelectLargeData1(sql, new ResultHandler<TblMallOrder>() {
            @Override
            public void handleResult(ResultContext<? extends TblMallOrder> resultContext) {
                TblMallOrder tblMallOrder = resultContext.getResultObject();
                System.out.println(tblMallOrder);
            }
        });
    }

    @GetMapping("/getLargeData2")
    public void getLargeData2() {
        String sql = "select * from t_mall_order";
        tblMallOrderDao.dynamicSelectLargeData1(sql, new ResultHandler<TblMallOrder>() {
            @Override
            public void handleResult(ResultContext<? extends TblMallOrder> resultContext) {
                TblMallOrder tblMallOrder = resultContext.getResultObject();
                System.out.println(tblMallOrder);
                // 你可以看自己的项目需要分批进行处理或者单个处理,这里以分批处理为例
                mallOrders.add(tblMallOrder);
                size++;
                if (size == BATCH_SIZE) {
                    handle();
                }
            }
        });
        //用来完成最后一批数据处理
        handle();
    }
    /**
     * 数据处理
     */
    private void handle(){
        try{
            // 在这里可以对你获取到的批量结果数据进行需要的业务处理
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            // 处理完每批数据后后将临时清空
            size = 0;
            mallOrders.clear();
        }
    }
}

三、总结
Oracle和DB2,当我们执行一个SQL查询语句的时候,需要在客户端和服务器端都打开一个游标,并且分别申请一块内存空间,作为存放查询的数据的一个缓冲区。这块内存区,存放多少条数据就由fetchsize来决定,同时每次网络包会传送fetchsize条记录到客户端。应该很容易理解,如果fetchsize设置为20,当我们从服务器端查询数据往客户端传送时,每次可以传送20条数据,但是两端分别需要20条数据的内存空闲来保存这些数据。fetchsize决定了每批次可以传输的记录条数,但同时,也决定了内存的大小。这块内存,在oracle服务器端是动态分配的。而在客户端,PS对象会存在一个缓冲中(LRU链表),也就是说,这块内存是事先配好的,应用端内存的分配在conn.prepareStatement(sql)或都conn.CreateStatement(sql)的时候完成。
MySQL本身并没有FetchSize方法, 它是使用CS阻塞的方式,通过网络流控制服务端,MySQL在执行ResultSet.next()方法时,会通过数据库连接一条一条的返回。MySQL按照自己的节奏不断的把buffer写回网络中。flush buffer的过程是阻塞式的,也就是说如果网络中发生了拥塞,send buffer被填满,会导致buffer一直flush不出去,那MySQL的处理线程会阻塞,从而避免数据把客户端内存撑爆。这样带来的问题:如果使用了流式查询,一个MySQL数据库连接同一时间只能为一个ResultSet对象服务,并且如果该ResultSet对象没有关闭,势必会影响其他查询对数据库连接的使用!此为大坑,所以sharding-sphere费劲心思要提供两种数据库连接模式,如果应用对数据库连接的消耗要求严苛,那么流式查询就不再适合。

package com.aegonthtf.fate.dao.secondary;

import com.aegonthtf.fate.constant.CommonConstant;
import com.aegonthtf.fate.entity.user.TuDAgent;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.ResultSetType;
import org.apache.ibatis.session.ResultHandler;

import java.util.Date;

/**
 * 营销员维度表(TuDAgent)表数据库访问层
 *
 * @author yangjunxiong
 * @since 2021-05-11 16:30:00
 */
@Mapper
public interface TuDAgentDao extends BaseMapper<TuDAgent> {

    //流式批量查询处理数据
//    @Select("select * from tu_d_agent")//测试用
    @Select("select * from tu_d_agent t where AGENT_STATUS = 'Active' and lcd >= #{startTime} and lcd <= #{endTime}")
    @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = CommonConstant.BATCH_SIZE)
    @ResultType(TuDAgent.class)
    void getUserListByLcdBigData(@Param("startTime") Date startTime, @Param("endTime") Date endTime, @Param("handler") ResultHandler<TuDAgent> handler);

    //流式批量查询处理数据
//    @Select("select * from tu_d_agent t where lcd <= #{date} LIMIT 10")//测试用
    @Select("select * from tu_d_agent where AGENT_STATUS = 'Active'")
    @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = CommonConstant.BATCH_SIZE)
    @ResultType(TuDAgent.class)
    void getUserListBigData(@Param("handler") ResultHandler<TuDAgent> handler);
}

package com.aegonthtf.fate.service.user.impl;

import cn.com.common.util.util.StringUtils;
import com.aegonthtf.fate.constant.CommonConstant;
import com.aegonthtf.fate.dao.secondary.TuDAgentDao;
import com.aegonthtf.fate.entity.user.FateAgent;
import com.aegonthtf.fate.entity.user.FateGoals;
import com.aegonthtf.fate.entity.user.FateRecord;
import com.aegonthtf.fate.entity.user.TuDAgent;
import com.aegonthtf.fate.service.convert.TuDagentToFateAgentConverter;
import com.aegonthtf.fate.service.convert.TuDagentToFateRecordConverter;
import com.aegonthtf.fate.service.user.*;
import com.aegonthtf.fate.util.JodaTimeUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.joda.time.LocalDateTime;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;

/**
 * 营销员维度表(TuDAgent)表服务实现类
 *
 * @author yangjunxiong
 * @since 2021-05-11 16:30:01
 */
@Service("tuDAgentService")
public class TuDAgentServiceImpl extends ServiceImpl<TuDAgentDao, TuDAgent> implements TuDAgentService, InitializingBean {
    @Autowired
    public               FateAgentService      fateAgentService;
    @Autowired
    public               TuDAgentService       tuDAgentService;
    @Autowired
    private              FateBranchService     fateBranchService;
    @Autowired
    private              FateGoalsService      fateGoalsService;
    @Autowired
    private              FateApolloActService  fateApolloActService;
    @Autowired
    private              FateRecordService     fateRecordService;
    @Autowired
    private              AisuattendanceService aisuattendanceService;
    @Autowired
    private              AisuleaveService      aisuleaveService;
    private              int                   size1;//计数器1
    @Autowired
    @Qualifier("tuDAgentServiceImpl1")
    private              Executor              executor1;
    private volatile     BlockingQueue         tuDAgentServiceImplQueue1; // 存储每批数据的临时容器
    private              int                   size2;//计数器2
    @Autowired
    @Qualifier("tuDAgentServiceImpl2")
    private              Executor              executor2;
    private volatile     BlockingQueue         tuDAgentServiceImplQueue2; // 存储每批数据的临时容器

    @Override
    public void syncFateAgent(LocalDateTime staratDateTime) {
        ImmutablePair<LocalDateTime, LocalDateTime> timeDay = JodaTimeUtils.criticalTimeDay(staratDateTime.toLocalDate());
        this.baseMapper.getUserListByLcdBigData(timeDay.getLeft().toDate(), timeDay.getRight().toDate(),
                resultContext -> {
                    Optional.ofNullable(resultContext.getResultObject())
                            .ifPresent(this.tuDAgentServiceImplQueue1::add);
                    size1++;
                    if (size1 >= CommonConstant.BATCH_SIZE) {
                        this.handle1(staratDateTime);
                    }
                });
        //用来完成最后一批数据处理
        this.handle1(staratDateTime);
    }

    /**
     * 数据处理
     */
    @Transactional
    public void handle1(LocalDateTime staratDateTime) {
        try {
            // 在这里可以对你获取到的批量结果数据进行需要的业务处理
            List<TuDAgent> tuDAgents1 = new ArrayList<>();
            this.tuDAgentServiceImplQueue1.drainTo(tuDAgents1, CommonConstant.BATCH_SIZE);
            if (CollectionUtils.isEmpty(tuDAgents1)) {
                return;
            }
            List<FateAgent> fateAgents = new ArrayList<>();
            String day = staratDateTime.toString("d");
            tuDAgents1.parallelStream().forEach(item -> {
                FateAgent fateAgent = this.fateAgentService.getFateAgentByAgentCode(item.getAgentCode());
                FateAgent to;
                if (Objects.isNull(fateAgent)) {
                    to = TuDagentToFateAgentConverter.to(item, staratDateTime, new FateAgent());
                } else {
//                to = TuDagentToFateAgentConverter.to(item, fateAgent);
                    //有记录的更新AGENT_STATUS
                    to = fateAgent;
                    Optional.ofNullable(item.getAgentStatus()).ifPresent(fateAgent::setAgentStatus);
                }
                if (Objects.isNull(to)) {
                    return;
                }
                //处理签约事件
                this.fateApolloActService.contractEventHandling(staratDateTime, item);
                //获取省一 二 三
                if (StringUtils.isNotBlank(to.getBranchcode())) {
                    FateAgent fateAgentOfBranch123 = this.fateBranchService.getFateBranchByBranchCode(to.getBranchcode());
                    if (!Objects.isNull(fateAgentOfBranch123)) {
                        Optional.ofNullable(fateAgentOfBranch123.getBrhFst()).ifPresent(to::setBrhFst);
                        Optional.ofNullable(fateAgentOfBranch123.getBrhScd()).ifPresent(to::setBrhScd);
                        Optional.ofNullable(fateAgentOfBranch123.getBrhThd()).ifPresent(to::setBrhThd);
                    }
                }
                //获取目标APE 每月一号 二号更新 GAPE和目标新增人力gManpower
                if (StringUtils.equals(day, "1") || StringUtils.equals(day, "2")) {
                    FateGoals one = this.fateGoalsService.getOne(new QueryWrapper<FateGoals>()
                            .lambda()
                            .eq(FateGoals::getAgentCode, to.getAgentCode())
                            .last(CommonConstant.LIMIT1)
                    );
                    Optional.ofNullable(one).ifPresent(itemFateGoals -> {
                        //目标达成-APE
                        to.setGApe(Objects.isNull(itemFateGoals.getGApe()) ? 0D : one.getGApe());
                        //目标-新增人力
                        to.setGManpower(Objects.isNull(itemFateGoals.getGManpower()) ? 0 : one.getGManpower());
                    });
                }
                //达成-新增人力 aManpower
                Integer aManpower = this.tuDAgentService.getFateAgentByAManpowerAndMonth(to.getAgentCode(), staratDateTime);
                to.setAManpower(Objects.isNull(aManpower) ? 0 : aManpower);
                fateAgents.add(to);
            });
            this.fateAgentService.saveOrUpDateBatchEntity(fateAgents);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 处理完每批数据后后将临时清空
            size1 = 0;
        }
    }

    @Override
    public void initFateRecord(LocalDateTime staratDateTime) {
        this.baseMapper.getUserListBigData(resultContext -> {
            Optional.ofNullable(resultContext.getResultObject())
                    .ifPresent(this.tuDAgentServiceImplQueue2::add);
            size2++;
            if (size2 >= CommonConstant.BATCH_SIZE) {
                this.handle2(staratDateTime);
            }
        });
        //用来完成最后一批数据处理
        this.handle2(staratDateTime);
    }

    /**
     * 数据处理
     */
    @Transactional
    public void handle2(LocalDateTime staratDateTime) {
        try {
            // 在这里可以对你获取到的批量结果数据进行需要的业务处理
            List<TuDAgent> tuDAgents2 = new ArrayList<>();
            this.tuDAgentServiceImplQueue1.drainTo(tuDAgents2, CommonConstant.BATCH_SIZE);
            if (CollectionUtils.isEmpty(tuDAgents2)) {
                return;
            }
            List<FateRecord> fateRecords = new ArrayList<>();
            tuDAgents2.parallelStream().forEach(item -> {
                if (Objects.isNull(item)) {
                    return;
                }
                FateRecord fateRecord = this.fateRecordService.getTodayByAgentCode(item.getAgentCode(), staratDateTime);
                FateRecord to;
                if (Objects.isNull(fateRecord)) {
                    to = TuDagentToFateRecordConverter.to(item, staratDateTime, new FateRecord());
                } else {
                    to = TuDagentToFateRecordConverter.to(item, staratDateTime, fateRecord);
                }
                if (Objects.isNull(to)) {
                    return;
                }
                //获取省一 二 三
                if (StringUtils.isNotBlank(to.getBranchcode())) {
                    FateAgent fateAgentOfBranch123 = this.fateBranchService.getFateBranchByBranchCode(to.getBranchcode());
                    if (!Objects.isNull(fateAgentOfBranch123)) {
                        Optional.ofNullable(fateAgentOfBranch123.getBrhFst()).ifPresent(to::setBrhFst);
                        Optional.ofNullable(fateAgentOfBranch123.getBrhScd()).ifPresent(to::setBrhScd);
                        Optional.ofNullable(fateAgentOfBranch123.getBrhThd()).ifPresent(to::setBrhThd);
                    }
                }
                //判断工作日逻辑
                //1.根据代理人工号查询aisuattendance表有无当天记录。
                //SELECT COUNT(1) FROM AISUATTENDANCE T WHERE T.AI_REC_ENDDT IS NULL AND T.AI_AGID = '工号' AND T.AI_ATTENDANCE_DATE = '日期';
                //2.若无记录,则当天非工作日,若有记录,查当前代理人是否请假。
                //select count(1) from aisuleave where ai_agid='0000223106' and ai_leave_enddt >='20201230' and ai_leave_stdt <='20201230';
                //3.若仍无记录,则当天是工作日。
                String workFlag;
                Integer count1 = this.aisuattendanceService.getWorkFlagByAgentCode(to.getAgentCode(), staratDateTime);
                if (count1 <= 0) {
                    workFlag = "N";
                } else {
                    Integer count2 = this.aisuleaveService.getWorkFlagByAgentCode(to.getAgentCode(), staratDateTime);
                    workFlag = (count2 <= 0) ? "Y" : "N";
                }
                to.setWorkFlag(workFlag); //是否是工作日(Y:N)	每天7点批处理更新
                Integer count = this.fateRecordService.getYesterdayFateRecordByAgentCode(to.getAgentCode());
                to.setRecordDays(count); //连续打卡天数
                fateRecords.add(to);
            });
            this.fateRecordService.saveOrUpDateBatchEntity(fateRecords);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 处理完每批数据后后将临时清空
            size2 = 0;
        }
    }

    //查询该工号当月 //当月达成-新增人力 aManpower
    @Override
    public Integer getFateAgentByAManpowerAndMonth(String agentCode, LocalDateTime staratDateTime) {
        ImmutablePair<LocalDateTime, LocalDateTime> timeMonth = JodaTimeUtils.criticalTimeMonth(staratDateTime);
        return this.count(new QueryWrapper<TuDAgent>().lambda()
                .eq(TuDAgent::getRefferCode, agentCode)
                .ge(TuDAgent::getFcd, timeMonth.left.toDate())
                .le(TuDAgent::getFcd, timeMonth.right.toDate())
        );
    }

    @Override
    public void afterPropertiesSet() {
        ThreadPoolTaskExecutor poolTaskExecutor1 = (ThreadPoolTaskExecutor) this.executor1;
        ThreadPoolTaskExecutor poolTaskExecutor2 = (ThreadPoolTaskExecutor) this.executor2;
        this.tuDAgentServiceImplQueue1 = poolTaskExecutor1.getThreadPoolExecutor().getQueue();
        this.tuDAgentServiceImplQueue2 = poolTaskExecutor2.getThreadPoolExecutor().getQueue();
    }
}

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: MybatisPlus流式查询是一种可以大大提高查询效率的查询方式。它允许我们在数据库中逐步获取查询结果,而不是一步到位的将所有结果都加载到内存中。 在MybatisPlus中,我们可以使用LambdaQueryWrapper的stream()方法来实现流式查询。当我们调用stream()方法时,查询条件会被封装成一个Steam查询器。接下来,我们可以通过Steam查询器中的方法,如map()、filter()、sorted()等进行一系列的操作,最终得到所需的查询结果。 MybatisPlus流式查询不仅可以提高查询效率,还可以节约内存占用。在处理大量数据时,使用流式查询可以有效避免OutOfMemoryError等内存溢出问题的发生。 需要注意的是,MybatisPlus流式查询只支持MySQL和PostgreSQL数据库。在使用时,我们还应该根据实际情况选择合适的流式查询方式。如果需要返回实体对象,我们应该使用stream(),如果只需要返回某些字段,则应该使用streamMaps()。 总而言之,MybatisPlus流式查询是一种非常实用的查询方式,可以大幅提高查询效率和减少内存占用。在处理大量数据时,我们应该优先考虑使用流式查询,以提高程序的稳定性和可扩展性。 ### 回答2: Mybatis-Plus是Mybatis的增强工具包,流式查询是其中的一种特性。它能够为Mybatis-Plus查询结果的及时处理带来更好的效能。 流式查询是指使用流式查询API来进行查询,并通过回调函数对结果进行处理,这种处理方式能够在处理大量数据时,节省内存和提高查询效率。Mybatis-Plus提供了LambdaQueryWrapper和LambdaUpdateWrapper两种流式查询API。 LambdaQueryWrapper是Mybatis-Plus一个受欢迎的查询条件之一,它是基于Java8的Lambda表达式和函数式接口实现的,并包含了所有常规查询条件,如eq等,同时它也支持动态SQL生成。 LambdaUpdateWrapper是基于LambdaQueryWrapper实现的,它用于更新操作时,可以生成update语句的where条件。 使用Lambda函数接口可以优雅地实现流式查询。LambdaQueryWrapper和LambdaUpdateWrapper接口都提供了部分相同的函数方法,如eq()、le()等,在实现查询时,可以直接调用这些方法,而无需手动去书写SQL语句。 同时,因为Lambda表达式语法具有高度的可读性和简洁性,流式查询的代码也更加清晰易懂。 总的来说,Mybatis-Plus的流式查询API提供了一种更加高效的查询方式,能够帮助我们在处理大量数据时,提高查询效率和节省内存。 ### 回答3: MybatisPlus是在Mybatis的基础上进行了二次封装,其流式查询MybatisPlus提供的一种高效的查询方式。MybatisPlus流式查询不仅可以减少内存的占用,还可以提高数据查询的效率,特别适用于大数据量的查询场景。 MybatisPlus流式查询可以通过Wrapper<T>构造器中的lambda表达式实现。首先我们需要用lambda表达式构建出一个Wrapper<T>对象,这个对象中包含指定的查询条件。 接下来使用MP提供的stream()方法可轻松实现流式查询。stream()方法返回的是MybatisPlus提供的BaseMapper的Stream实现。 在进行流式查询时,不需要一次性将所有数据读入内存,而是会按照查询条件分步读取数据,这可以显著减少内存开销;此外,流式查询还支持对数据进行排序、分页等操作,有助于提高查询效率。 总之,MybatisPlus流式查询是一种高效、灵活的查询方式,特别适用于大数据量的查询场景。使用流式查询可以减少内存占用,提高查询效率,同时还支持排序、分页等操作,具有很高的实用性和灵活性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值