<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
批次插入是實際工作中常見的一個功能,mysql支援一條sql語句插入多條資料。但是Mybatis-Plus中預設提供的saveBatch方法並不是真正的批次插入,而是遍歷實體集合每執行一次insert語句插入一條記錄。相比批次插入,效能上顯然會差很多。
今天談一下,在Mybatis-Plus中如何通過SQL隱碼攻擊器實現真正的批次插入。
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');
測試的資料表:
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中設定好資料庫連線,並安裝好MybatisX-Generator外掛,生成對應表的model、mapper、service、xml檔案。
生成的檔案推薦儲存在工程目錄下,generator目錄下。先生成檔案,使用者根據自己的需要,再將檔案移動到指定目錄,這樣避免出現檔案覆蓋。
生成實體的設定選項,這裡我勾選了Lombok和Mybatis-Plus3,生成的類更加優雅。
移動生成的檔案到對應目錄:
由於都是生成的程式碼,這裡就不補充程式碼了。
@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); }
執行紀錄檔:
顯然,這裡每次執行insert操作,都只插入了一條資料。
//批次儲存的方法,做了分批請求處理,預設一次處理1000條資料 default boolean saveBatch(Collection<T> entityList) { return this.saveBatch(entityList, 1000); } //使用者也可以自己指定每批次處理的請求數量 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); //每次達到批次數,sqlSession就重新整理一次,進行資料庫請求,生成Id if (i == idxLimit) { sqlSession.flushStatements(); idxLimit = Math.min(idxLimit + batchSize, size); } } }); }
我們將批次數設定為3,用來測試executeBatch的處理機制。
@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); } //批次數設為3,用來測試 userService.saveBatch(list,3); }
執行結果,首批提交的請求,已經生成了id,還沒有提交的id為null。
(這裡的提交是sql請求,而不是說的事物提交)
小結:
Mybatis-Plus中預設的批次儲存方法saveBatch,底層是通過sqlSession.flushStatements()
將一個個單條插入的insert語句分批次進行提交。
相比遍歷集合去呼叫userMapper.insert(entity),執行一次提交一次,saveBatch批次儲存有一定的效能提升,但從sql層面上來說,並不算是真正的批次插入。
補充:
遍歷集合單次提交的批次插入。
@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 插入耗時:"+(System.currentTimeMillis()-start)); }
SQL隱碼攻擊器官方檔案:https://baomidou.com/pages/42ea4a/
SQL隱碼攻擊器sqlInjector 用於注入 ISqlInjector
介面的子類,實現自定義方法注入。
參考預設注入器 DefaultSqlInjector
。
Mybatis-plus預設可以注入的方法如下,大家也可以參考其實現自己擴充套件:
預設注入器DefaultSqlInjector的內容:
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()); } }
目前在mybatis-plus
的擴充套件外掛中com.baomidou.mybatisplus.extension
,給我們額外提供了4個注入方法。
AlwaysUpdateSomeColumnById
根據Id更新每一個欄位,全量更新不忽略null欄位,解決mybatis-plus中updateById預設會自動忽略實體中null值欄位不去更新的問題。InsertBatchSomeColumn
真實批次插入,通過單SQL的insert語句實現批次插入DeleteByIdWithFill
帶自動填充的邏輯刪除,比如自動填充更新時間、操作人Upsert
更新or插入,根據唯一約束判斷是執行更新還是刪除,相當於提供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在存在主鍵衝突或者唯一鍵衝突的情況下,根據插入策略不同,一般有以下三種避免方法。
這裡不展開介紹,大家可以自行檢視:https://www.jb51.net/article/194579.htm
通過SQL隱碼攻擊器sqlInjector 增加批次插入方法InsertBatchSomeColumn的過程如下:
程式碼如下:
/** * 自定義Sql注入 */ public class MySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass) { List<AbstractMethod> methodList = super.getMethodList(mapperClass); //更新時自動填充的欄位,不用插入值 methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE)); return methodList; } }
程式碼如下:
@Configuration public class MybatisPlusConfig { @Bean public MySqlInjector sqlInjector() { return new MySqlInjector(); } }
public interface CommonMapper<T> extends BaseMapper<T> { /** * 全量插入,等價於insert * @param entityList * @return */ int insertBatchSomeColumn(List<T> entityList); }
public interface UserMapper extends CommonMapper<User> { }
@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); }
執行結果:
可以看到已經實現單條insert語句支援資料的批次插入。
注意⚠️:
預設的insertBatchSomeColumn實現中,並沒有類似saveBatch中的分配提交處理,
這就存在一個問題,如果出現一個非常大的集合,就會導致最後組裝提交的insert語句的長度超過mysql的限制。
@Service @Slf4j public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { @Resource private UserMapper userMapper; /** * 採用insertBatchSomeColumn重寫saveBatch方法,保留分批次處理機制 * @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; //儲存單批提交的資料集合 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); //每次提交後需要清空集合資料 oneBatchList.clear(); idxLimit = Math.min(idxLimit + batchSize, size); } } }catch (Exception e){ log.error("saveBatch fail",e); return false; } return true; }
更好的實現是繼承ServiceImpl實現類,自己擴充套件通用的服務實現類,在其中重寫通用的saveBatch方法,這樣就不用在每一個服務類中都重寫一遍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); } //批次數設為3,用來測試 userService.saveBatch(list,3); }
執行結果:
分4次採用insert批次新增,符合我們的結果預期。
本文主要介紹了Mybatis-Plus中如何通過SQL隱碼攻擊器實現真正的批次插入。主要掌握如下內容:
1、瞭解Mybatis-Plus中SQL隱碼攻擊器有什麼作用,如何去進行擴充套件。
2、預設的4個擴充套件方法各自的作用。
3、預設的saveBatch批次新增和通過insertBatchSomeColumn實現的批次新增的底層實現原理的區別,為什麼insertBatchSomeColumn效能更好以及存在哪些弊端。
4、為insertBatchSomeColumn新增分批次處理機制,避免批次插入的insert語句過長問題。
到此這篇關於Mybatis-Plus通過SQL隱碼攻擊器實現批次插入的實踐的文章就介紹到這了,更多相關Mybatis-Plus SQL隱碼攻擊器批次插入內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45