yewanji 2022-04-24 18:32 采纳率: 37.9%
浏览 242
已结题

java多线程批量插入数据报错

问题是这样的,我们有一个业务场景支持excel 批量导入上千条数据,且需要支持整批同时成功或失败,于是我打算将连接改成不自动提交,以手工开启事务形式处理,代码如下

 ExecutorService service = Executors.newFixedThreadPool(5);
    
    @Resource
    SqlSession sqlSession;
    
    @Test
    public void batchInsert() throws SQLException {
        Connection connection = sqlSession.getConnection();
        try {
            // 设置手动提交
            connection.setAutoCommit(false);
            StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
            //执行子线程
            Student stu1 = new Student("张三");
            Student stu2 = new Student("李四");
            Student stu3 = new Student("王五");
            List<Student> students= Arrays.asList(stu1,stu2,stu3);
            List<Callable<Integer>> callableList  = new ArrayList<>();
            for (Student student : students) {
                Callable<Integer> callable=()-> studentMapper.addStudent(student);
                callableList.add(callable);
            }
            List<Future<Integer>> futures = service.invokeAll(callableList);
            for (Future<Integer> future:futures) {
                if (future.get()<=0){
                    connection.rollback();
                    return;
                }
            }
            connection.commit();
            System.out.println("添加完毕");
        }catch (Exception e){
            connection.rollback();
            log.info("error",e);
            throw new RuntimeException("002出现异常");
        }
        Scanner scanner=new Scanner(System.in);
        scanner.next();
    }

mysq和mybatis 配置如下

 @Bean("masterDataSource")
    @Primary
    public DataSource masterDatasource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(userName);
        dataSource.setPassword(password);
        dataSource.setRemoveAbandonedTimeoutMillis(10*1000);
        dataSource.setRemoveAbandoned(true);
        dataSource.setTimeBetweenConnectErrorMillis(10*1000);
        return dataSource;
    }
    
    /***
     * 管理事务
     *
     * */
    @Bean(name = "masterTransactionManager")
    @Primary
    public DataSourceTransactionManager masterTransactionManager() {
        return new DataSourceTransactionManager(masterDatasource());
    }
    
    /***
     * 创建sqlsessionFactory
     *
     * */
    @Bean(name = "masterSqlSessionFactory")
    @Primary
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource)
            throws Exception {
        final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(masterDataSource);
        org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
        config.setMapUnderscoreToCamelCase(true);
        sessionFactory.setConfiguration(config);
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(MAPPER_LOCAL));
        sessionFactory.setPlugins(new SqlCostInterceptor(),new GlobalInterceptor());
        return sessionFactory.getObject();
    }

但现实很残酷,报错了
java.sql.SQLException: connection holder is null
at com.alibaba.druid.pool.DruidPooledConnection.checkStateInternal(DruidPooledConnection.java:1157)
at com.alibaba.druid.pool.DruidPooledConnection.checkState(DruidPooledConnection.java:1148)
at com.alibaba.druid.pool.DruidPooledConnection.commit(DruidPooledConnection.java:743)

  • 写回答

11条回答 默认 最新

  • 瑾莫 2022-04-27 16:38
    关注

    如果你是springboot项目,像你说的那个批量处理数据什么的,你就用他自带的多线程方法ThreadPoolTaskExecutor,去处理,最后.join的方式加入到主线程,你只需要处理数据网数据库查的操作就行,ThreadPoolTaskExecutor的使用方式b站上有《SpringBoot项目如何使用线程池提升业务速度?》里面有这个的使用方式,是一个大佬老杨做的视频,视频时长2分零7秒,可以学习使用一下,像多线程挂钩的,很多现在都封装好的方式,没必要自己写代码,只需要完成业务逻辑就行

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
  • Haven55555 2022-04-24 18:37
    关注

    大批量数据库插入 使用sqlbulkcopy 试试

    评论
  • 中华小阿壮 2022-04-24 20:31
    关注

    手动删除数据也可以

    img

    评论
  • 关注

    应该挺多答案的,connection holder is Null ,大概率是事务时间太长被回收了,可以尝试调大removeAbandonedTimeout

    评论 编辑记录
  • 太空眼睛 Java领域新星创作者 2022-04-24 21:29
    关注

    报这个错误是因为超时时间比sql实际执行时间要短。
    需要调整参数设置,比如AbandonedTimeout等,把所有相关的timeout时间设置足够长即可。

    如有帮助,请采纳,十分感谢!

    评论
  • 刘了了 2022-04-24 23:30
    关注
    评论
  • zcl_1991 2022-04-25 09:30
    关注

    执行时间是否超过了10秒,调大

    评论
  • 小苗爸爸 2022-04-25 11:49
    关注

    超时时间设置长一点

    评论
  • 张三博客 张三解忧百货店官方账号 2022-04-25 14:02
    关注

    或者你思路错了呢 为什么不可以使用批量插入呢? 问中也没有说是高并发场景 且楼主觉得这种方式真的合理吗?

    评论
  • 一起随缘 2022-04-25 22:45
    关注

    首先你可以尝试在catch后加上finally模块,用于判断connect是否为null,然后进行关闭的判断,如果还是不行,建议使用批量插入的逻辑,我们项目中就是这样实现的,具体是将导入的数据分批次,一次批量执行一百条数据,不会超过sql的最大长度,对于你的上千条数据十几次就执行完了,效率不会低的,我们导入时涉及到上万条数据,几十秒就结束了

    评论
  • oNuoyi 2022-04-26 10:52
    关注

    我做过一个这样的相关场景你可以看看,我的是非结构化转结构化数据,开启线程池批量处理要么都成功要么都失败,你可以看看我的博客https://blog.csdn.net/qq_41973632/article/details/122490990?spm=1001.2014.3001.5502

    评论
查看更多回答(10条)

报告相同问题?

问题事件

  • 系统已结题 5月7日
  • 已采纳回答 4月29日
  • 创建了问题 4月24日

悬赏问题

  • ¥15 Tpad api账户 api口令
  • ¥30 ppt进度条制作,vba语言
  • ¥15 stc12c5a60s2单片机测光敏ADC
  • ¥15 生信simpleaffy包下载
  • ¥15 请教一下simulink中S函数相关问题
  • ¥15 在二层网络中,掩码存在包含关系即可通信
  • ¥15 端口转发器解析失败不知道电脑设置了啥
  • ¥15 Latex算法流程图行号自定义
  • ¥15 关于#python#的问题:我在自己的电脑上运行起来总是报错,希望能给我一个详细的教程,(开发工具-github)
  • ¥40 基于51单片机实现球赛计分器功能