Mybatis-plus解决兼容oracle批量插入的示例详解

 更新时间:2024年11月03日 08:33:09   作者:骑龟的狗  
Mybatis-Plus 是一个 MyBatis 的增强工具,提供无侵入、损耗小的 CRUD 操作,本文给大家介绍了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类中,将刚才创建的SQL注入器EasySqlInjector,注册为一个bean。

@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig
{
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,默认 500 条,-1 不受限制
        // paginationInterceptor.setLimit(500);
        // 开启 count 的 join 优化,只针对部分 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

这个地方要注意,表的主键我用触发器已经自动填入,所以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 {
        //当前业务,兼容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");
        }
    }
 
}

然后,用业务Mapper继承EasyBaseMapper就可以调用insertBatchSomeColumn()方法了。

到此这篇关于Mybatis-plus解决兼容oracle批量插入的示例详解的文章就介绍到这了,更多相关Mybatis-plus oracle批量插入内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot如何将http转https

    springboot如何将http转https

    这篇文章主要介绍了springboot如何将http转https,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-04-04
  • 浅谈Java响应式系统

    浅谈Java响应式系统

    第一次听到reactive这个词还是在几年前,偶然了解到了Rxjava这个项目,仿佛为我打开了一扇新的大门,Rxjava是ReactiveX的java实现,ReactiveX家族除了Rxjava还有RxJS, Rx.NET,RxScala等等。
    2021-06-06
  • IDEA中如何移除未使用的包

    IDEA中如何移除未使用的包

    这篇文章主要介绍了IDEA中如何移除未使用的包问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-02-02
  • mybatis逆向工程与分页在springboot中的应用及遇到坑

    mybatis逆向工程与分页在springboot中的应用及遇到坑

    最近在项目中应用到springboot与mybatis,在进行整合过程中遇到一些坑,在此将其整理出来,分享到脚本之家平台供大家参考下
    2018-09-09
  • 关于RedisTemplate之opsForValue的使用说明

    关于RedisTemplate之opsForValue的使用说明

    这篇文章主要介绍了关于RedisTemplate之opsForValue的使用说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • Java发送https请求代码实例

    Java发送https请求代码实例

    这篇文章主要介绍了Java发送https请求代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-08-08
  • 使用@ConfigurationProperties注解获取为null的解决方法

    使用@ConfigurationProperties注解获取为null的解决方法

    在SpringBoot中,当想需要获取到配置文件数据时,除了可以用 Spring 自带的@Value注解外,SpringBoot还提供了一种更加方便的方式:@ConfigurationProperties,但我们在通过通过get方法去取值一直为null,本文介绍了使用@ConfigurationProperties注解获取为null的解决方法
    2024-09-09
  • java IO数据操作流、对象序列化、压缩流代码解析

    java IO数据操作流、对象序列化、压缩流代码解析

    这篇文章主要介绍了java IO数据操作流、对象序列化、压缩流代码解析,具有一定借鉴价值,需要的朋友可以参考下
    2018-01-01
  • Spring整合Mybatis使用<context:property-placeholder>时的坑

    Spring整合Mybatis使用<context:property-placeholder>时的坑

    这篇文章主要介绍了Spring整合Mybatis使用<context:property-placeholder>时的坑 的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-06-06
  • 深入浅出Java中的Happens-Before核心规则

    深入浅出Java中的Happens-Before核心规则

    本文解析Java内存模型中的Happens-Before原则,解释其定义、核心规则及实际应用,帮助理解多线程可见性与有序性问题,掌握并发编程关键,感兴趣的朋友跟随小编一起看看吧
    2025-09-09

最新评论