最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Mybatis-Plus通過SQL注入器實(shí)現(xiàn)批量插入的實(shí)踐

 更新時(shí)間:2022年08月11日 10:24:35   作者:斗者_(dá)2013  
本文主要介紹了Mybatis-Plus通過SQL注入器實(shí)現(xiàn)批量插入的實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

批量插入是實(shí)際工作中常見的一個(gè)功能,mysql支持一條sql語句插入多條數(shù)據(jù)。但是Mybatis-Plus中默認(rèn)提供的saveBatch方法并不是真正的批量插入,而是遍歷實(shí)體集合每執(zhí)行一次insert語句插入一條記錄。相比批量插入,性能上顯然會差很多。
今天談一下,在Mybatis-Plus中如何通過SQL注入器實(shí)現(xiàn)真正的批量插入。

一、mysql批量插入的支持

insert批量插入的語法支持:

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

二、Mybatis-Plus默認(rèn)saveBatch方法解析

1、測試工程建立

測試的數(shù)據(jù)表:

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL COMMENT '主鍵ID',
  `name` varchar(30) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年齡',
  `email` varchar(50) DEFAULT NULL COMMENT '郵箱',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

在IDEA中配置好數(shù)據(jù)庫連接,并安裝好MybatisX-Generator插件,生成對應(yīng)表的model、mapper、service、xml文件。

生成的文件推薦保存在工程目錄下,generator目錄下。先生成文件,用戶根據(jù)自己的需要,再將文件移動到指定目錄,這樣避免出現(xiàn)文件覆蓋。

生成實(shí)體的配置選項(xiàng),這里我勾選了Lombok和Mybatis-Plus3,生成的類更加優(yōu)雅。

移動生成的文件到對應(yīng)目錄:

由于都是生成的代碼,這里就不補(bǔ)充代碼了。

2、默認(rèn)批量插入saveBatch方法測試

    @Test
    public void testBatchInsert() {
        System.out.println("----- batch insert method test ------");
        List<User> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setName("test");
            user.setAge(13);
            user.setEmail("101@qq.com");
            list.add(user);
        }
        userService.saveBatch(list);
    }

執(zhí)行日志:

顯然,這里每次執(zhí)行insert操作,都只插入了一條數(shù)據(jù)。

3、saveBatch方法實(shí)現(xiàn)分析

//批量保存的方法,做了分批請求處理,默認(rèn)一次處理1000條數(shù)據(jù)
default boolean saveBatch(Collection<T> entityList) {
    return this.saveBatch(entityList, 1000);
}

//用戶也可以自己指定每批處理的請求數(shù)量
boolean saveBatch(Collection<T> entityList, int batchSize);
public static <E> boolean executeBatch(Class<?> entityClass, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
    Assert.isFalse(batchSize < 1, "batchSize must not be less than one", new Object[0]);
    return !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, (sqlSession) -> {
        int size = list.size();
        int idxLimit = Math.min(batchSize, size);
        int i = 1;

        for(Iterator var7 = list.iterator(); var7.hasNext(); ++i) {
            E element = var7.next();
            consumer.accept(sqlSession, element);
            //每次達(dá)到批次數(shù),sqlSession就刷新一次,進(jìn)行數(shù)據(jù)庫請求,生成Id
            if (i == idxLimit) {
                sqlSession.flushStatements();
                idxLimit = Math.min(idxLimit + batchSize, size);
            }
        }

    });
}

我們將批次數(shù)設(shè)置為3,用來測試executeBatch的處理機(jī)制。

    @Test
    public void testBatchInsert() {
        System.out.println("----- batch insert method test ------");
        List<User> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setName("test");
            user.setAge(13);
            user.setEmail("101@qq.com");
            list.add(user);
        }
        //批次數(shù)設(shè)為3,用來測試
        userService.saveBatch(list,3);
    }

執(zhí)行結(jié)果,首批提交的請求,已經(jīng)生成了id,還沒有提交的id為null。
(這里的提交是sql請求,而不是說的事物提交)

小結(jié):
Mybatis-Plus中默認(rèn)的批量保存方法saveBatch,底層是通過sqlSession.flushStatements()將一個(gè)個(gè)單條插入的insert語句分批次進(jìn)行提交。
相比遍歷集合去調(diào)用userMapper.insert(entity),執(zhí)行一次提交一次,saveBatch批量保存有一定的性能提升,但從sql層面上來說,并不算是真正的批量插入。

補(bǔ)充:

遍歷集合單次提交的批量插入。

 @Test
    public void forEachInsert() {
        System.out.println("forEachInsert 插入開始========");
        long start = System.currentTimeMillis();
        for (int i = 0; i < list.size(); i++) {
            userMapper.insert(list.get(i));
        }
        System.out.println("foreach 插入耗時(shí):"+(System.currentTimeMillis()-start));
    }

三、Mybatis-plus中SQL注入器介紹

SQL注入器官方文檔:https://baomidou.com/pages/42ea4a/

1.sqlInjector介紹

SQL注入器sqlInjector 用于注入 ISqlInjector 接口的子類,實(shí)現(xiàn)自定義方法注入。
參考默認(rèn)注入器 DefaultSqlInjector。

Mybatis-plus默認(rèn)可以注入的方法如下,大家也可以參考其實(shí)現(xiàn)自己擴(kuò)展:

默認(rèn)注入器DefaultSqlInjector的內(nèi)容:

public class DefaultSqlInjector extends AbstractSqlInjector {
    public DefaultSqlInjector() {
    }

    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        //注入通用的dao層接口的操作方法
        return (List)Stream.of(new Insert(), new Delete(), new DeleteByMap(), new DeleteById(), new DeleteBatchByIds(), new Update(), new UpdateById(), new SelectById(), new SelectBatchByIds(), new SelectByMap(), new SelectOne(), new SelectCount(), new SelectMaps(), new SelectMapsPage(), new SelectObjs(), new SelectList(), new SelectPage()).collect(Collectors.toList());
    }
}

2.擴(kuò)展中提供的4個(gè)可注入方法實(shí)現(xiàn)

目前在mybatis-plus的擴(kuò)展插件中com.baomidou.mybatisplus.extension,給我們額外提供了4個(gè)注入方法。

  • AlwaysUpdateSomeColumnById 根據(jù)Id更新每一個(gè)字段,全量更新不忽略null字段,解決mybatis-plus中updateById默認(rèn)會自動忽略實(shí)體中null值字段不去更新的問題。
  • InsertBatchSomeColumn 真實(shí)批量插入,通過單SQL的insert語句實(shí)現(xiàn)批量插入
  • DeleteByIdWithFill 帶自動填充的邏輯刪除,比如自動填充更新時(shí)間、操作人
  • Upsert 更新or插入,根據(jù)唯一約束判斷是執(zhí)行更新還是刪除,相當(dāng)于提供insert on duplicate key update支持
insert into t_name (uid, app_id,createTime,modifyTime)
values(111, 1000000,'2017-03-07 10:19:12','2017-03-07 10:19:12')
on duplicate key update uid=111, app_id=1000000, 
createTime='2017-03-07 10:19:12',modifyTime='2017-05-07 10:19:12'

mysql在存在主鍵沖突或者唯一鍵沖突的情況下,根據(jù)插入策略不同,一般有以下三種避免方法。

  • insert ignore
  • replace into
  • insert on duplicate key update

這里不展開介紹,大家可以自行查看:http://www.fzitv.net/article/194579.htm

四、通過SQL注入器實(shí)現(xiàn)真正的批量插入

通過SQL注入器sqlInjector 增加批量插入方法InsertBatchSomeColumn的過程如下:

1.繼承DefaultSqlInjector擴(kuò)展自定義的SQL注入器

代碼如下:

/**
 * 自定義Sql注入
 */
public class MySqlInjector extends DefaultSqlInjector {
    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
         //更新時(shí)自動填充的字段,不用插入值
         methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
        return methodList;
    }
}

2.將自定義的SQL注入器注入到Mybatis容器中

代碼如下:

@Configuration
public class MybatisPlusConfig {

    @Bean
    public MySqlInjector sqlInjector() {
        return new MySqlInjector();
    }
}

3.繼承 BaseMapper 添加自定義方法

public interface CommonMapper<T> extends BaseMapper<T> {
    /**
     * 全量插入,等價(jià)于insert
     * @param entityList
     * @return
     */
    int insertBatchSomeColumn(List<T> entityList);
}

4.Mapper層接口繼承新的CommonMapper

public interface UserMapper extends CommonMapper<User> {

}

5.單元測試

 @Test
    public void testBatchInsert() {
        System.out.println("----- batch insert method test ------");
        List<User> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setName("test");
            user.setAge(13);
            user.setEmail("101@qq.com");
            list.add(user);
        }
        userMapper.insertBatchSomeColumn(list);
    }

執(zhí)行結(jié)果:

可以看到已經(jīng)實(shí)現(xiàn)單條insert語句支持?jǐn)?shù)據(jù)的批量插入。

注意??:
默認(rèn)的insertBatchSomeColumn實(shí)現(xiàn)中,并沒有類似saveBatch中的分配提交處理,
這就存在一個(gè)問題,如果出現(xiàn)一個(gè)非常大的集合,就會導(dǎo)致最后組裝提交的insert語句的長度超過mysql的限制。

6.insertBatchSomeColumn添加分批處理機(jī)制

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private UserMapper userMapper;

    /**
     * 采用insertBatchSomeColumn重寫saveBatch方法,保留分批處理機(jī)制
     * @param entityList
     * @param batchSize
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean saveBatch(Collection<User> entityList, int batchSize) {
        try {
            int size = entityList.size();
            int idxLimit = Math.min(batchSize, size);
            int i = 1;
            //保存單批提交的數(shù)據(jù)集合
            List<User> oneBatchList = new ArrayList<>();
            for(Iterator<User> var7 = entityList.iterator(); var7.hasNext(); ++i) {
                User element = var7.next();
                oneBatchList.add(element);
                if (i == idxLimit) {
                    userMapper.insertBatchSomeColumn(oneBatchList);
                    //每次提交后需要清空集合數(shù)據(jù)
                    oneBatchList.clear();
                    idxLimit = Math.min(idxLimit + batchSize, size);
                }
            }
        }catch (Exception e){
            log.error("saveBatch fail",e);
            return false;
        }
        return  true;
    }

更好的實(shí)現(xiàn)是繼承ServiceImpl實(shí)現(xiàn)類,自己擴(kuò)展通用的服務(wù)實(shí)現(xiàn)類,在其中重寫通用的saveBatch方法,這樣就不用在每一個(gè)服務(wù)類中都重寫一遍saveBatch方法。

單元測試:

    @Test
    public void testBatchInsert() {
        System.out.println("----- batch insert method test ------");
        List<User> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setName("test");
            user.setAge(13);
            user.setEmail("101@qq.com");
            list.add(user);
        }
        //批次數(shù)設(shè)為3,用來測試
        userService.saveBatch(list,3);
    }

執(zhí)行結(jié)果:

分4次采用insert批量新增,符合我們的結(jié)果預(yù)期。

總結(jié)

本文主要介紹了Mybatis-Plus中如何通過SQL注入器實(shí)現(xiàn)真正的批量插入。主要掌握如下內(nèi)容:
1、了解Mybatis-Plus中SQL注入器有什么作用,如何去進(jìn)行擴(kuò)展。
2、默認(rèn)的4個(gè)擴(kuò)展方法各自的作用。
3、默認(rèn)的saveBatch批量新增和通過insertBatchSomeColumn實(shí)現(xiàn)的批量新增的底層實(shí)現(xiàn)原理的區(qū)別,為什么insertBatchSomeColumn性能更好以及存在哪些弊端。
4、為insertBatchSomeColumn添加分批處理機(jī)制,避免批量插入的insert語句過長問題。

到此這篇關(guān)于Mybatis-Plus通過SQL注入器實(shí)現(xiàn)批量插入的實(shí)踐的文章就介紹到這了,更多相關(guān)Mybatis-Plus SQL注入器批量插入內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論

历史| 上思县| 通州区| 黄骅市| 花莲市| 翼城县| 县级市| 大洼县| 清远市| 岢岚县| 广宗县| 文安县| 志丹县| 雅江县| 罗甸县| 简阳市| 阳山县| 米林县| 壤塘县| 卓尼县| 龙里县| 嵊泗县| 凤山县| 宁化县| 恩平市| 盖州市| 清镇市| 泰来县| 海原县| 疏勒县| 龙陵县| 乡宁县| 正蓝旗| 平果县| 平泉县| 化州市| 花莲县| 襄垣县| 济南市| 山阳县| 扎赉特旗|