如何使用MybatisPlus的SQL注入器提升批量插入性能
上一次我們給大家詳細(xì)講解 MybatisPlus 的條件構(gòu)造器wrapper。這次我們來講另一個(gè)開發(fā)者需要了解的功能——SQL注入器,并以實(shí)戰(zhàn)視角講述如何利用該特性提升MybatisPlus 的批量插入性能
一、SQL注入器是什么?
在上次的文章《MybatisPlus 構(gòu)造器wrapper的使用與原理》 的第三部分,我們講解了 MybatisPlus 運(yùn)行的一些原理
獲取語法模板SQL
代入表及字段信息
代入條件構(gòu)造器邏輯
如果不太熟悉的,可以再去復(fù)習(xí)一遍。不難發(fā)現(xiàn)這第一步,就是獲取語法模板SQL,MybatisPlus源碼中就有不少簡單的模板,如下圖。但是坦白來說,對于一些大型的項(xiàng)目來說,內(nèi)置的SQL可能還是少了點(diǎn)
如果只是個(gè)別SQL有特殊的語法,那倒罷了,針對個(gè)別SQL寫個(gè)xml文件就可以解決。然而如果有很多SQL都使用同一個(gè)模板,那我們一個(gè)個(gè)去改就比較費(fèi)事了,而SQL注入器
就能為我們解決這個(gè)困擾
二、批量插入的性能問題
1. saveBatch 的運(yùn)行原理
我們在日常使用中,可能會(huì)使用到Iservice
的 saveBatch
方法來實(shí)現(xiàn)批量插入
但是如果我們繼續(xù)在源碼中深入,就可以發(fā)現(xiàn)這個(gè)批量插入本質(zhì)上是在for循環(huán)中插入,默認(rèn)以1000為一批,進(jìn)行一次刷新flush
雖然這里使用的同一個(gè)sqlSession能夠提高一些效率,但畢竟是for循環(huán)內(nèi)的插入,真在大批量插入時(shí)性能還是有待提升。而一個(gè)大項(xiàng)目中,批量插入的功能還是很常見的,本期我們就看看,如何使用 MybatisPlus的SQL注入器提升批量插入性能
2. 理想的批量插入形式
我們都知道,數(shù)據(jù)庫其實(shí)提供了批量插入的SQL樣式,比如Mysql的
INSERT INTO your_table_name (column1, column2, column3) VALUES (value1a, value2a, value3a), (value1b, value2b, value3b), (value1c, value2c, value3c);
而對于Oracle,其也有對應(yīng)的樣式
INSERT ALL INTO table_name (column1, column2, ...) VALUES (value1a, value2a, ...) INTO table_name (column1, column2, ...) VALUES (value1b, value2b, ...) INTO table_name (column1, column2, ...) VALUES (value1c, value2c, ...) SELECT * FROM dual;
或者
INSERT INTO table_name (column1, column2, ...) SELECT value1a, value2a, ... FROM dual UNION ALL SELECT value1b, value2b, ... FROM dual UNION ALL SELECT value1c, value2c, ... FROM dual;
使用對應(yīng)數(shù)據(jù)庫的批量插入樣式,毫無疑問是最合適的,所以我們需要在MybatisPlus中加入這樣的模板
三、構(gòu)造批量插入的SQL注入器
1. 自定義方法
首先我們要自定義一個(gè)方法,因?yàn)镸ysql 和 Oracle 的批量插入樣式不一樣,所以理論上要寫兩個(gè)方法,但實(shí)際上MybatisPlus 針對 Mysql 已經(jīng)有了一個(gè)內(nèi)置方法 InsertBatchSomeColumn,所以我們只需要針對 Oracle 自定義方法即可 繼承自·AbstractMethod
public class InsertBatchSomeColumnOracle extends AbstractMethod { // oracle 批量插入的格式 private static String ORACLE_INSERT_SQL_MODE = "<script>\nINSERT ALL\n %s \n SELECT 1 FROM DUAL</script>"; // 字段篩選條件 @Setter @Accessors(chain = true) private Predicate<TableFieldInfo> predicate; public InsertBatchSomeColumnOracle() { super("insertBatchSomeColumnOracle"); } @Override public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { KeyGenerator keyGenerator = new NoKeyGenerator(); List<TableFieldInfo> fieldList = tableInfo.getFieldList(); String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(true, false) + this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY); String columnScript = LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET; String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(true, ENTITY_DOT, false) + this.filterTableFieldInfo(fieldList, predicate, i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY); insertSqlProperty = LEFT_BRACKET + insertSqlProperty.substring(0, insertSqlProperty.length() - 1) + RIGHT_BRACKET; String oracleModelInsertSql = "INTO " + tableInfo.getTableName() + columnScript + " VALUES " + insertSqlProperty; String valuesScript = SqlScriptUtils.convertForeach(oracleModelInsertSql, "list", null, ENTITY, " "); String keyProperty = null; String keyColumn = null; //表包含主鍵處理邏輯,如果不包含主鍵當(dāng)普通字段處理 if (tableInfo.havePK()) { if (tableInfo.getIdType() == IdType.AUTO) { /* 自增主鍵 */ keyGenerator = new Jdbc3KeyGenerator(); keyProperty = tableInfo.getKeyProperty(); keyColumn = tableInfo.getKeyColumn(); } else { if (null != tableInfo.getKeySequence()) { Keybenerator: TableInfoHelper.genKeyGenerator("insertBatchSomeColumnOracle", tableInfo, builderAssistant); keyProperty = tableInfo.getKeyProperty(); keyColumn = tableInfo.getKeyColumn(); } } } String sql = String.format(ORACLE_INSERT_SQL_MODE, valuesScript); SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); return this.addInsertMappedStatement(mapperClass, modelClass, "insertBatchSomeColumnOracle", sqlSource, keyGenerator, keyProperty, keyColumn); } }
2. 將方法注入 MybatisPlus
僅僅寫一個(gè)類,并不能直接就使用,我們還需要把這個(gè)方法,融入 MybatisPlus 的運(yùn)行時(shí)。這時(shí)就要用到另一個(gè)類,這個(gè)類要繼承DefaultSqlInjector
,如下,我們把mysql 和 oracle 的兩個(gè)批量插入都加入 methodList 中
public class CustomerSqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperclass, TableInfo tableInfo) { List<AbstractMethod> methodList = super.getMethodList(mapperclass, tableInfo); // 真正的批量插入,添加 InsertBatchSomeColumn 方法,以下僅適用于 MySQL methodList.add(new InsertBatchSomeColumn()); // 真正的批量插入,添加 InsertBatchSomeColumn 方法,以下僅適用于 oracle methodList.add(new InsertBatchSomeColumnOracle()); return methodList; } }
3. 創(chuàng)建使用方法的接口
上面的內(nèi)容是把新增的方法放入MybatisPlus,下面我們還需要一個(gè)給開發(fā)者使用的入口
public interface CustomerMapper<T> extends BaseMapper<T> { void insertBatchSomeColumn(@Param("list") List<T> list); void insertBatchSomeColumnOracle(@Param("list") List<T> list); }
當(dāng)然,構(gòu)建完Mapper級別,你也可以繼續(xù)往上構(gòu)建,比如構(gòu)建自己的Service級(接口及實(shí)現(xiàn)類),如下
public interface CustomerService<T> extends Iservice<T> { void insertBatchSomeColumn(List<T> list); void insertBatchSomeColumnOracle(List<T> list); }
4. 啟用該注入器
現(xiàn)在代碼層面的配置解決了,想要使用批量插入功能的,只要讓我們的Mapper接口繼承CostomerMapper就可以,如下:
@Mapper public interface CsdnUserInfoMapper extends CustomerMapper<CsdnUserInfo> { }
然后我們還需要在配置中開啟這個(gè)注入器。我們可以在yml文件中這么配置
mybatis-plus: global-config: sql-injector: com.example.MyLogicSqlInjector
四、總結(jié)
在上面的學(xué)習(xí)里,我們詳細(xì)闡述了如何使用 MybatisPlus的SQL注入器提升批量插入性能,希望幫大家更深入的了解 MybatisPlus 的特性及其使用,也希望能幫助大家開闊性能優(yōu)化的思路,后續(xù)我們將繼續(xù)為大家講解 MybatisPlus 的更多內(nèi)容
到此這篇關(guān)于MybatisPlus的SQL注入器提升批量插入性能的文章就介紹到這了,更多相關(guān)MybatisPlus SQL注入器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringMVC接收java.util.Date類型數(shù)據(jù)的2種方式小結(jié)
這篇文章主要介紹了使用SpringMVC接收java.util.Date類型數(shù)據(jù)的2種方法,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08springboot jasypt2.x與jasypt3.x的使用方式
在軟件開發(fā)中,將配置文件中的敏感信息(如數(shù)據(jù)庫密碼)進(jìn)行加密是保障安全的有效手段,jasypt框架提供了這一功能,支持通過加密工具類或命令行工具生成密文,并通過修改配置文件和啟動(dòng)參數(shù)的方式使用密文和密鑰,這樣即便配置文件被泄露2024-09-09SWT(JFace)體驗(yàn)之FormLayout布局
SWT(JFace)體驗(yàn)之FormLayout布局示例代碼。2009-06-06Java String不可變性實(shí)現(xiàn)原理解析
這篇文章主要介紹了Java String不可變性實(shí)現(xiàn)原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04Spring AOP的概念與實(shí)現(xiàn)過程詳解
AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,可通過運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。AOP是 Spring框架中的一個(gè)重要內(nèi)容2023-02-02