记一次优化JPA批量存储数据
写在前面
JPA中的批量存储方案,Save( ),或者saveAll( ),
一、优化细节
其实数据不大,excel 导入 500条数据,用了 40s,可能的原因
- excel解析速度
- 集合操作(几种for循环的不同)
- JPA的保存操作,save()和saveAll()
其实最后,试了很多次,这么都不是主要原因,而是SpringSecurity中一个对象
// 这里密码部分若是放到循环里面,会大大消耗执行时间
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);
}
}
}