Mybatis-plus解決兼容oracle批量插入的示例詳解
1. 自定義SQL注入器
新建一個(gè)名為EasySqlInjector的類,繼承DefaultSqlInjector
。
public class EasySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass) { // 注意:此SQL注入器繼承了DefaultSqlInjector(默認(rèn)注入器),調(diào)用了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,注冊(cè)為一個(gè)bean。
@EnableTransactionManagement(proxyTargetClass = true) @Configuration public class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // 設(shè)置請(qǐng)求的頁(yè)面大于最大頁(yè)后操作, true調(diào)回到首頁(yè),false 繼續(xù)請(qǐng)求 默認(rèn)false // paginationInterceptor.setOverflow(false); // 設(shè)置最大單頁(yè)限制數(shù)量,默認(rèn) 500 條,-1 不受限制 // paginationInterceptor.setLimit(500); // 開(kāi)啟 count 的 join 優(yōu)化,只針對(duì)部分 left join paginationInterceptor.setLimit(-1); // paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); return paginationInterceptor; } /** * 分頁(yè)插件,自動(dòng)識(shí)別數(shù)據(jù)庫(kù)類型 https://baomidou.com/guide/interceptor-pagination.html */ public PaginationInnerInterceptor paginationInnerInterceptor() { PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(); // 設(shè)置數(shù)據(jù)庫(kù)類型為mysql paginationInnerInterceptor.setDbType(DbType.ORACLE); // 設(shè)置最大單頁(yè)限制數(shù)量,默認(rèn) 500 條,-1 不受限制 paginationInnerInterceptor.setMaxLimit(-1L); return paginationInnerInterceptor; } /** * 樂(lè)觀鎖插件 https://baomidou.com/guide/interceptor-optimistic-locker.html */ public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() { return new OptimisticLockerInnerInterceptor(); } /** * 如果是對(duì)全表的刪除或更新操作,就會(huì)終止該操作 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 實(shí)體列表 */ void insertBatchSomeColumn(Collection<T> entityList); }
4.自定義Mybatis攔截器OracleSqlInterceptor
這個(gè)地方要注意,表的主鍵我用觸發(fā)器已經(jīng)自動(dòng)填入,所以keyGenerator設(shè)置為NoKeyGenerator.INSTANCE,這個(gè)地方有個(gè)坑,不那么設(shè)置,SQL一直報(bào)錯(cuò),折騰了兩個(gè)小時(shí),實(shí)際上拼接的SQL沒(méi)問(wèn)題
@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 { //當(dāng)前業(yè)務(wù),兼容pg 和 oracle,需要兼容oracle的批量插入語(yǔ)句 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)){ //開(kāi)始兼容批量插入語(yǔ)句,并設(shè)置boundSql Field declaredField = boundSql.getClass().getDeclaredField("sql"); declaredField.setAccessible(true); declaredField.set(boundSql, convertOracleInsertSql(sql)); log.info("---轉(zhuǎn)換后的sql為:{}", boundSql.getSql()); } return invocation.proceed(); } /** * Oracle Insert語(yǔ)句轉(zhuǎn)化 * * @param sql 傳入的pg的sql * @return 轉(zhuǎn)化后的sql */ public String convertOracleInsertSql(String sql) { //用oracle中的批量語(yǔ)句代替 //查找values的位置,將后面全部括號(hào)里的東西取出,然后再用對(duì)應(yīng)的數(shù)據(jù)進(jìn)行封裝 //獲取前面的sql,這段sql與Oracle的相同 String prefix = sql.substring(0, getKeywordValueIndex(sql)); //排除table中的括號(hào),取后面的括號(hào) 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(); } /** * 使用棧實(shí)現(xiàn)獲取value中括號(hào)的值 * */ 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) { //左括號(hào)進(jìn)棧 brackets.push(c); } else if (')' == c) { //右括號(hào)則將左括號(hào)出棧,清空builder brackets.pop(); values.add(splitValue.toString()); splitValue.delete(0, splitValue.length()); } else if (!brackets.empty()) { //只有進(jìn)入括號(hào)中才將值放入,排除括號(hào)外的逗號(hào) splitValue.append(c); } } return values; } /** * 查找關(guān)鍵字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è)務(wù)Mapper繼承EasyBaseMapper就可以調(diào)用insertBatchSomeColumn()
方法了。
到此這篇關(guān)于Mybatis-plus解決兼容oracle批量插入的示例詳解的文章就介紹到這了,更多相關(guān)Mybatis-plus oracle批量插入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javaweb設(shè)計(jì)中filter粗粒度權(quán)限控制代碼示例
這篇文章主要介紹了javaweb設(shè)計(jì)中filter粗粒度權(quán)限控制代碼示例,小編覺(jué)得還是挺不錯(cuò)的,需要的朋友可以參考。2017-10-10java 實(shí)現(xiàn)將Object類型轉(zhuǎn)換為int類型
這篇文章主要介紹了java 實(shí)現(xiàn)將Object類型轉(zhuǎn)換為int類型的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07IntelliJ IDEA 小技巧之Bookmark(書(shū)簽)的使用
這篇文章主要介紹了IntelliJ IDEA 小技巧之Bookmark(書(shū)簽)的使用,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07servlet實(shí)現(xiàn)簡(jiǎn)單的權(quán)限管理和敏感詞過(guò)濾功能
JavaEE課要求用servlet和過(guò)濾器實(shí)現(xiàn)權(quán)限管理和敏感詞過(guò)濾功能,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05關(guān)于解決iReport4.1.1無(wú)法正常啟動(dòng)或者閃退或者JDK8不兼容的問(wèn)題
在安裝使用iReport的過(guò)程中遇到一個(gè)問(wèn)題,我的iReport始終不能打開(kāi),困擾了我好久。接下來(lái)通過(guò)本文給大家介紹iReport4.1.1無(wú)法正常啟動(dòng)或者閃退或者JDK8不兼容的問(wèn)題,需要的朋友可以參考下2018-09-09