Mybatis-plus解決兼容oracle批量插入的示例詳解
1. 自定義SQL注入器
新建一個名為EasySqlInjector的類,繼承DefaultSqlInjector。
public class EasySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
// 注意:此SQL注入器繼承了DefaultSqlInjector(默認注入器),調用了DefaultSqlInjector的getMethodList方法,保留了mybatis-plus的自帶方法
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
return methodList;
}
}2. 將SQL注入器交給Spring容器
在MybatisPlusConfig類中,將剛才創(chuàng)建的SQL注入器EasySqlInjector,注冊為一個bean。
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig
{
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 設置請求的頁面大于最大頁后操作, true調回到首頁,false 繼續(xù)請求 默認false
// paginationInterceptor.setOverflow(false);
// 設置最大單頁限制數量,默認 500 條,-1 不受限制
// paginationInterceptor.setLimit(500);
// 開啟 count 的 join 優(yōu)化,只針對部分 left join
paginationInterceptor.setLimit(-1);
// paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
/**
* 分頁插件,自動識別數據庫類型 https://baomidou.com/guide/interceptor-pagination.html
*/
public PaginationInnerInterceptor paginationInnerInterceptor()
{
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 設置數據庫類型為mysql
paginationInnerInterceptor.setDbType(DbType.ORACLE);
// 設置最大單頁限制數量,默認 500 條,-1 不受限制
paginationInnerInterceptor.setMaxLimit(-1L);
return paginationInnerInterceptor;
}
/**
* 樂觀鎖插件 https://baomidou.com/guide/interceptor-optimistic-locker.html
*/
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor()
{
return new OptimisticLockerInnerInterceptor();
}
/**
* 如果是對全表的刪除或更新操作,就會終止該操作 https://baomidou.com/guide/interceptor-block-attack.html
*/
public BlockAttackInnerInterceptor blockAttackInnerInterceptor()
{
return new BlockAttackInnerInterceptor();
}
@Bean
public EasySqlInjector sqlInjector() {
return new EasySqlInjector();
}
}3. 配置EasyBaseMapper繼承BaseMapper
新建EasyBaseMapper類,繼承BaseMapper,并在此類中配置insertBatchSomeColumn()方法。
public interface EasyBaseMapper<T> extends BaseMapper<T> {
/**
* @param entityList 實體列表
*/
void insertBatchSomeColumn(Collection<T> entityList);
}4.自定義Mybatis攔截器OracleSqlInterceptor
這個地方要注意,表的主鍵我用觸發(fā)器已經自動填入,所以keyGenerator設置為NoKeyGenerator.INSTANCE,這個地方有個坑,不那么設置,SQL一直報錯,折騰了兩個小時,實際上拼接的SQL沒問題
@Component
@Slf4j
@Order(1)
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class OracleSqlInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//當前業(yè)務,兼容pg 和 oracle,需要兼容oracle的批量插入語句
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
StatementHandler delegate = (StatementHandler) ReflectUtil.getFieldValue(statementHandler, "delegate");
MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate, "mappedStatement");
ReflectUtil.setFieldValue(mappedStatement,"keyGenerator", NoKeyGenerator.INSTANCE);
String mName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1);
if("insertBatchSomeColumn".equals(mName)){
//開始兼容批量插入語句,并設置boundSql
Field declaredField = boundSql.getClass().getDeclaredField("sql");
declaredField.setAccessible(true);
declaredField.set(boundSql, convertOracleInsertSql(sql));
log.info("---轉換后的sql為:{}", boundSql.getSql());
}
return invocation.proceed();
}
/**
* Oracle Insert語句轉化
*
* @param sql 傳入的pg的sql
* @return 轉化后的sql
*/
public String convertOracleInsertSql(String sql) {
//用oracle中的批量語句代替
//查找values的位置,將后面全部括號里的東西取出,然后再用對應的數據進行封裝
//獲取前面的sql,這段sql與Oracle的相同
String prefix = sql.substring(0, getKeywordValueIndex(sql));
//排除table中的括號,取后面的括號
String subSql = sql.substring(getKeywordValueIndex(sql));
String valueSql = subSql.substring(subSql.indexOf("("));
List<String> valueList = getValues(valueSql);
//拼接sql
StringBuilder sqlBuilder = new StringBuilder().append(prefix);
//sqlBuilder.append("SELECT A.* FROM (");
String selectValue = "SELECT ";
String endValue = " FROM DUAL ";
String unionValue = "UNION ALL ";
boolean start = true;
for (String value : valueList) {
if (!start) {
sqlBuilder.append(unionValue);
}
else {
start = false;
}
sqlBuilder.append(selectValue).append(value).append(endValue);
}
//sqlBuilder.append(") A");
return sqlBuilder.toString();
}
/**
* 使用棧實現獲取value中括號的值
*
*/
public List<String> getValues(String sql) {
List<String> values = new ArrayList<>();
Stack<Character> brackets = new Stack<>();
StringBuilder splitValue = new StringBuilder();
for (Character c : sql.toCharArray()) {
if ('(' == c) {
//左括號進棧
brackets.push(c);
}
else if (')' == c) {
//右括號則將左括號出棧,清空builder
brackets.pop();
values.add(splitValue.toString());
splitValue.delete(0, splitValue.length());
}
else if (!brackets.empty()) {
//只有進入括號中才將值放入,排除括號外的逗號
splitValue.append(c);
}
}
return values;
}
/**
* 查找關鍵字value的位置
*/
public int getKeywordValueIndex(String sql) {
//先找values,再找value
if (sql.contains("values")) {
return sql.indexOf("values");
}
else if (sql.contains("VALUES")) {
return sql.indexOf("VALUES");
}
else if (sql.contains("value")) {
return sql.indexOf("value");
}
else {
return sql.indexOf("VALUE");
}
}
}然后,用業(yè)務Mapper繼承EasyBaseMapper就可以調用insertBatchSomeColumn()方法了。
到此這篇關于Mybatis-plus解決兼容oracle批量插入的示例詳解的文章就介紹到這了,更多相關Mybatis-plus oracle批量插入內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
IntelliJ IDEA 小技巧之Bookmark(書簽)的使用
這篇文章主要介紹了IntelliJ IDEA 小技巧之Bookmark(書簽)的使用,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07
關于解決iReport4.1.1無法正常啟動或者閃退或者JDK8不兼容的問題
在安裝使用iReport的過程中遇到一個問題,我的iReport始終不能打開,困擾了我好久。接下來通過本文給大家介紹iReport4.1.1無法正常啟動或者閃退或者JDK8不兼容的問題,需要的朋友可以參考下2018-09-09

