天天看點

記一次優化JPA批量存儲資料

記一次優化JPA批量存儲資料

  • ​​寫在前面​​
  • ​​一、優化細節​​
  • ​​二、批量 insert 資料時,應注意的​​

寫在前面

JPA中的批量存儲方案,Save( ),或者saveAll( ),

一、優化細節

其實資料不大,excel 導入 500條資料,用了 40s,可能的原因

  • excel解析速度
  • 集合操作(幾種for循環的不同)
  • JPA的儲存操作,save()和saveAll()
// 這裡密碼部分若是放到循環裡面,會大大消耗執行時間
        String encode = passwordEncoder.encode(Constants.USER_DEFAULT_PD);

        usersFromExcel.forEach(user -> {
            user.setPassword(encode);
            user.setDepartmentId(departmentNames.get(user.getDepartmentName().trim()));

            user.setCreateBy(currentUserLogin);
            user.setUpdateBy(currentUserLogin);
            user.setCreateTime(now);
            user.setUpdateTime(now);
            UserInfo saveUser = userRepository.save(user);

            UserRole userRole = new UserRole(saveUser.getUserId(), defaultRoleId);
            userRoleRepository.save(userRole);
        });      

二、批量 insert 資料時,應注意的

  • 盡量使用saveAll(),有時候并不是主要原因,甚至兩者差不多,兩者在儲存時,其實都對資料驗證了,其實也浪費了沒必要的步驟,
  • Mysql服務端的緩存配置
  • 用戶端的連接配接配置
  • 構造初始化對象,這裡可能是你并沒有很好的初始化對象,占用太多時間構造資料(save之前)
  • save(),或者SaveAll(),或者如下代碼,分批次批處理
  • 多線程異步操作,分隔 task,以及單個失敗時,處理機制
//     公共方法抽取
    private void saveUsers(LocalDateTime now, String currentUserLogin, List<UserInfo> usersFromExcel,
            Map<String, Long> departmentNames,Long defaultRoleId) {

        usersFromExcel.forEach(user -> {
            user.setPassword(passwordEncoder.encode(Constants.USER_DEFAULT_PD));
            user.setDepartmentId(departmentNames.get(user.getDepartmentName().trim()));

            user.setCreateBy(currentUserLogin);
            user.setUpdateBy(currentUserLogin);
            user.setCreateTime(now);
            user.setUpdateTime(now);
        });


        // 批量儲存
        List<UserInfo> userInfos = userRepository.saveAll(usersFromExcel);
//        this.addListModelParams(usersFromExcel);

//        List<UserRole> collect = usersFromExcel.stream().map(e -> new UserRole(e.getUserId(), defaultRoleId)).collect(Collectors.toList());
//
//        userRoleRepository.saveAll(collect);
    }

    public void addListModelParams(List<UserInfo> list) {
        int listsize = list.size();
        if (listsize == 0) {
            throw new RuntimeException("集合為空!") ;
        } else {

            //批量存儲的集合
            List<UserInfo> data = new ArrayList<UserInfo>();

            //批量存儲
            for (UserInfo s : list) {
                // 分 6 次批量存儲
                if(data.size() == listsize/5) {
                    userRepository.saveAll(data);
                    data.clear();
                }
                data.add(s);
            }
            //将剩下的資料也導入
            if(!data.isEmpty()) {
                userRepository.saveAll(data);
            }
        }
    }