如何使用MybatisPlus的SQL注入器提升批量插入性能

上一次我們給大家詳細(xì)講解 MybatisPlus 的條件構(gòu)造器wrapper。這次我們來講另一個開發(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)

如果只是個別SQL有特殊的語法,那倒罷了,針對個別SQL寫個xml文件就可以解決。然而如果有很多SQL都使用同一個模板,那我們一個個去改就比較費(fèi)事了,而SQL注入器就能為我們解決這個困擾
二、批量插入的性能問題
1. saveBatch 的運(yùn)行原理
我們在日常使用中,可能會使用到Iservice的 saveBatch 方法來實(shí)現(xiàn)批量插入

但是如果我們繼續(xù)在源碼中深入,就可以發(fā)現(xiàn)這個批量插入本質(zhì)上是在for循環(huán)中插入,默認(rèn)以1000為一批,進(jìn)行一次刷新flush

雖然這里使用的同一個sqlSession能夠提高一些效率,但畢竟是for循環(huán)內(nèi)的插入,真在大批量插入時性能還是有待提升。而一個大項(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. 自定義方法
首先我們要自定義一個方法,因?yàn)镸ysql 和 Oracle 的批量插入樣式不一樣,所以理論上要寫兩個方法,但實(shí)際上MybatisPlus 針對 Mysql 已經(jīng)有了一個內(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
僅僅寫一個類,并不能直接就使用,我們還需要把這個方法,融入 MybatisPlus 的運(yùn)行時。這時就要用到另一個類,這個類要繼承DefaultSqlInjector,如下,我們把mysql 和 oracle 的兩個批量插入都加入 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,下面我們還需要一個給開發(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> {
}然后我們還需要在配置中開啟這個注入器。我們可以在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種方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
springboot jasypt2.x與jasypt3.x的使用方式
在軟件開發(fā)中,將配置文件中的敏感信息(如數(shù)據(jù)庫密碼)進(jìn)行加密是保障安全的有效手段,jasypt框架提供了這一功能,支持通過加密工具類或命令行工具生成密文,并通過修改配置文件和啟動參數(shù)的方式使用密文和密鑰,這樣即便配置文件被泄露2024-09-09
SWT(JFace)體驗(yàn)之FormLayout布局
SWT(JFace)體驗(yàn)之FormLayout布局示例代碼。2009-06-06
Java String不可變性實(shí)現(xiàn)原理解析
這篇文章主要介紹了Java String不可變性實(shí)現(xiàn)原理解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-04-04
Spring AOP的概念與實(shí)現(xiàn)過程詳解
AOP為Aspect Oriented Programming的縮寫,意為:面向切面編程,可通過運(yùn)行期動態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。AOP是 Spring框架中的一個重要內(nèi)容2023-02-02

