抱歉,你查看的文章不存在

批量查询DB的实现

背景

由于业务量的上涨,门店转入业务数据的需求,催生了一批excel导入系统的需求。但是由于原先excel导入的实现基于行的模式(一行一行导入)

导致系统导入的效率很低,经常出现超时等等问题。

解决方案

  1. 将多数的导入由一行转为批量导入,那么比如一次插入1000条数据。当然存在一些业务上的check(重复等等,不合理值等等),暂且不表。

比如

<insert id="addMemberCardInBatchModel" parameterType="com.air.tqb.model.TaMemberCard">
    insert into
    ta_member_card(pk_id,name,memo,card_type,prestore_amount,validate,permanent,use_type,amount,vip_money,favourable_money,total_number,creationtime,creator,id_own_org)
    VALUES
    <foreach collection="list" item="card" separator=",">
    (CAST(#{card.pkId} as unsigned),#{card.name},#{card.memo},#{card.cardType},#{card.prestoreAmount},#{card.validate},#{card.permanent},#{card.useType},#{card.totalMoney},#{card.vipMoney},#{card.favourableMoney},#{card.totalNumber},now(),#{card.creator},CAST(#{card.idOwnOrg} as unsigned ))
    </foreach>
</insert>

问题

  1. 部分自动增加表在插入db需要用到业务主键,如何批量生成pk?(batchExecutor存在问题,可以参考https://my.oschina.net/zudajun/blog/674946 本文使用的是ReuseExecutor)
  2. 部分表的主键是uuid,那么如何批量生成uuid?
  3. 由于sql过长或者过于复杂导致出现一些异常,比如执行时间超时,栈溢出等等 如下 Packet for query is too large (2302364 > 1048576). You can change this value on the server by setting the max_allowed_packet’ variable Thread stack overrun: 180624 bytes used of a 196608 byte stack, and 16000 bytes needed.

实现方案

  1. 系统中使用mybatis作为orm框架。那么mybatis通常我们使用selectKey或者jdbc3Key作为主键设置。通常可能如下
<selectKey keyProperty="pkId" resultType="String" order="BEFORE">
    select Uuid_short() from dual
</selectKey>
<insert  useGeneratedKeys="true" keyProperty="id"  keyColumn="id">

其中selectKey不可以生成多主键(排除) 剩下的在mybatis3.3.0不支持多主键(bug)后已经修复 (3.3.1) When insert a list or array,resolve the useGeneratedKeys error.

  1. 我们可能需要批量生成uuid了,那么方案可能如下
<select id="getUUidInBatch" resultType="java.lang.String" flushCache="true">
<foreach collection="list" separator=" union " >
        select  Uuid_short()
</foreach>
</select>

当然更好的方案使用存储过程等根据次数要求生成 后面补上方案(只是示例)

3.对于批量的问题导致出现问题,我们考虑希望开发者在无感知的情况下完成数据的插入或者查询。那么考虑如下切面

package com.air.tqb.aop;
 
import com.air.tqb.annoate.Batch;
import com.google.common.collect.Lists;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
 
import java.util.Collection;
import java.util.List;
 
/**
 * Created by qixiaobo on 2017/07/31.
 */
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 3)
public class BatchAspect {
 
    private static final Log logger = LogFactory.getLog(BatchAspect.class);
 
    @Pointcut("@annotation(com.air.tqb.annoate.Batch)&& @annotation(batch) ")
    public void batchMethod(Batch batch) {
 
    }
 
    @Pointcut("execution(public int com.air.tqb.service..*.*(..)) && batchMethod(batch)")
    public void intReturnMethod(Batch batch) {
 
    }
 
    @Pointcut("execution(public java.util.List com.air.tqb.service..*.*(..)) && batchMethod(batch)")
    public void listReturnMethod(Batch batch) {
 
    }
 
    @Pointcut("execution(public void com.air.tqb.service..*.*(..)) && batchMethod(batch)")
    public void voidReturnMethod(Batch batch) {
 
    }
 
    @Pointcut("args(java.util.List)&& args(list)")
    public void listArgumentMethod(List list) {
 
    }
 
    @Around("listReturnMethod(batch)&&listArgumentMethod(list)")
    public Object batchExecuteReturnList(ProceedingJoinPoint joinPoint, Batch batch, List list) throws Throwable {
        int batchSize = batch.value();
        List rltList = Lists.newArrayListWithExpectedSize(list.size());
        List<List> partitions = Lists.partition(list, batchSize);
        for (List partition : partitions) {
            Object rlt = joinPoint.proceed(new Object[]{partition});
            rltList.addAll((Collection) rlt);
        }
        return rltList;
 
    }
 
    @Around("intReturnMethod(batch)&&listArgumentMethod(list)")
    public Object batchExecuteReturnInt(ProceedingJoinPoint joinPoint, Batch batch, List list) throws Throwable {
        int batchSize = batch.value();
        int rltTotal = 0;
        List<List> partitions = Lists.partition(list, batchSize);
        for (List partition : partitions) {
            int rlt = (int) joinPoint.proceed(new Object[]{partition});
            rltTotal += rlt;
        }
        return rltTotal;
 
    }
 
 
    @Around("voidReturnMethod(batch)&&listArgumentMethod(list)")
    public Object batchExecuteReturnVoid(ProceedingJoinPoint joinPoint, Batch batch, List list) throws Throwable {
        int batchSize = batch.value();
        List<List> partitions = Lists.partition(list, batchSize);
        for (List partition : partitions) {
            joinPoint.proceed(new Object[]{partition});
        }
        return null;
 
    }
 
 
}

其中Batch注解如下

package com.air.tqb.annoate;
 
import com.air.tqb.common.AppConstant;
 
import java.lang.annotation.*;
 
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Batch {
    int value() default AppConstant.DEFAULT_DATA_BASE_BATCH_SIZE;
}

只要在方法的签名上声明如下

@Batch
int addPartInfoDetailBatch(List<TmPartInfoDetail> tmPartInfoDetails);

如上可以完成批量插入。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

编辑于

后端之路

0 篇文章0 人订阅

相关文章

来自专栏程序员的SOD蜜

调用PostgreSQL存储过程,找不到函数名的问题

PostgreSQL的表,函数名称都是严格区分大小写的,所以在使用的时候没有注意大小写问题容易导致找不到函数名的错误,但最近两天我们发现,如果函数参数使用了自定...

2815
来自专栏nice_每一天

Elasticsearch JavaApi

 官网JavaApi地址:https://www.elastic.co/guide/en/elasticsearch/client/java-api/curre...

6894
来自专栏技术与生活

设计模式-备忘录模式

备忘录角色对如何其他对象提供一个接口,也就是宽接口的话,那么备忘录角色存储的内部状态都暴露给其他对象。这种情况导致发起人的状态都没看到,是破坏封装性的,只能通过...

1052
来自专栏Coding迪斯尼

VUE+WebPack游戏设计:实现子弹发射击打外星人效果

1043
来自专栏生信宝典

NGS基础 - GTF/GFF文件格式解读和转换

GFF 文件 GFF全称为general feature format,这种格式主要是用来注释基因组。 从 Ensembl 导出的GFF文件示例: X E...

2.1K10
来自专栏美团技术团队

【美团技术团队博客】Dive into Category

本文系学习Objective-C的runtime源码时候整理所成,主要剖析了category在runtime层的实现原理以及和category相关的方方面面,包...

3696
来自专栏机器人网

中英文对照,瞬间理解西门子PLC指令

指令( 英文全称意思 ) :指令含义 1、LD ( Load 装载 ) :动合触点 2、LDN ( Load Not 不装载 ) : 动断触点 3、A ( A...

3577
来自专栏IT开发技术与工作效率

一些容易忽略的Java基础题

Oracle 在 DDL 前后各执行一次 COMMIT,所以慎用 truncate

953
来自专栏我杨某人的青春满是悔恨

封装一个 Swift-Style 的网络模块

Swift 跟 OC 有着完全不同的设计哲学,它鼓励你使用 protocol 而不是 super class,使用 enum 和 struct 而不是 clas...

983
来自专栏岑玉海

hbase源码系列(九)StoreFile存储格式

从这一章开始要讲Region Server这块的了,但是在讲Region Server这块之前得讲一下StoreFile,否则后面的不好讲下去,这块是基础,Re...

4005

扫码关注云+社区

领取腾讯云代金券