【代码封装】事务内批量插入的封装-方案一: 利用函数式编程

事务内批量插入的封装-方案一: 利用函数式编程

公司的Druid不允许批量提交,无奈自己动手写,觉得可以封装一下。

写了三个方案,这里整理出第一个方案,没有使用多线程,单纯异步的。

适用情形

1、连接池禁止直接向数据库批量插入;
2、跑批的业务场景较多;
3、插入前需要遍历处理数据;
4、需要异步执行的但不需要统一提交/回滚的;

封装示例

关键点:泛型、手动管理事务、函数式编程。

  • 泛型解决与数据类型耦合问题
  • 函数编程解决业务方法耦合问题
  • 手动事务提交解决插入效率问题
/**
 * @author SurfingMaster
 * @date 2023/3/8
 */
@Component("batchGenerateExe")
public class BatchGenerateExe<T> {

    private static final Logger log = LoggerFactory.getLogger(BatchGenerateExe.class);

	/* 可以作为execute的参数 由调用方传入 */
    private static final long PAGE_SIZE = 1000L;

    @Autowired
    private PlatformTransactionManager transactionManager;


    /**
     * BatchGenerateExe.execute <br>
     *
     * @param qurFunc          qurFunc
     * @param insertSingleFunc insertSingleFunc
     * @author SurfingMaster
     * @description BatchGenerateExe.execute
     */
    public void execute(BiFunction<Long, Long, List<T>> qurFunc, Consumer<T> insertSingleFunc) {
        log.info("===== BatchGenerateExe.execute-STARTED. =====");
        List<T> partitionData;
        for (long page = 1, rows = PAGE_SIZE, offset = 0L; ; offset = (++page - 1) * PAGE_SIZE, rows = (page * PAGE_SIZE)) {
            try {
                partitionData = qurFunc.apply(offset, rows);
            } catch (Exception e) {
            	// TODO 异常处理的逻辑(若有) 
                partitionData = null;
            }
            if (CollectionUtils.isEmpty(partitionData)) {
                break;
            }
            insertBatch(partitionData, insertSingleFunc);
        }
        log.info("===== BatchGenerateExe.execute-COMPLETED. =====");
    }

    /**
     * BatchGenerateExe.insertBatch <br>
     *
     * @param records          records
     * @param insertSingleFunc insertSingleFunc
     * @author SurfingMaster
     * @description BatchGenerateExe.insertBatch
     */
    // @
    private void insertBatch(List<T> records, Consumer<T> insertSingleFunc) {
        log.info("===== BatchGenerateExe.insertBatch-STARTED. =====");
        if (CollectionUtils.isEmpty(records)) {
            return;
        }
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
            records.forEach(insertSingleFunc);
        } catch (Exception e) {
            log.error("===== ERROR-BatchInsertTask.insertBatch-e:{} =====", e.getMessage());
            // TODO 异常处理的逻辑(若有) 
        } finally {
            transactionManager.commit(status);
        }
        log.info("===== BatchGenerateExe.insertBatch-COMPLETED. =====");
    }
}

编译结果

不出意料,执行器里的这个for被编译成while,批量插入insertBatch的判空if也被展开了。

BatchGenerateExe.execute编译结果

BatchGenerateExe.insertBatch

使用示例

  • 需求:当天00:10查询前一天的数据。传参只有两个,大于等于前一天零点,小于当天零点。
  • 实现:完成业务查询方法,完成插入前业务操作方法,调用封装好的批量执行器。
/**
 * @author SurfingMaster
 * @date 2023/3/8
 */
@DubboService
public class XxxDetailScheduleImpl implements XxxDetailScheduleI {
    
    @Autowired
    private BatchGenerateExe<XxxDetail> batchExe;

    @Async
    @Override
    public void xxxDetailSchedule() {
        log.info("===== xxx =====");
        final Date zeroOfCurrentDay = getZeroOfCurrentDay();
        final Date lastDayZero = getLastDayZero();

        batchExe.execute(
                (offset, rows) -> xxxDetailDao.selectXxxPartition(zeroOfCurrentDay, offset, rows),
                (element) -> insertDetail(element, zeroOfCurrentDay, lastDayZero));

        batchExe.execute(
                (offset, rows) -> xxxDetailDao.selectXxxPartitionCase1(zeroOfCurrentDay, offset, rows),
                (element) -> insertDetail(element, zeroOfCurrentDay, lastDayZero));
        
        batchExe.execute(
                (offset, rows) -> xxxDetailDao.selectXxxPartitionCase2(zeroOfCurrentDay, offset, rows),
                (element) -> insertDetail(element, zeroOfCurrentDay, lastDayZero));
        
        log.info("===== xxx =====");
    }
    
 
     public void insertDetail(xxxDetail xxx, Date zeroOfCurrentDay, Date lastDayZero) {
        if (xxx == null) return;
        // 业务逻辑...
        
        // 数据 插入/更新...
        xxxGwateway.save(xxx);
        
    }
    
}

使用注意

  • 代码性能:考虑到函数式编程的数据特性,请注意内存资源使用情况;
  • 数据安全:处理异常、回滚等。
  • 业务安全:处理可能被重复调用的情况。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值