一、批量处理执行类
@Service
//将这个类标注@Transactional,将批量操作交给spring的事务,否则每一次insert/delete/update就会强制关闭sqlSession
@Transactional
public class SqlBatchExecutor{
@Autowired
private SqlSessionFactory sqlSessionFactory;
private final int BATCH_COUNT = 10000;
public <T> void batchExecute(Class<T> mapperClass, List<?> list, MapperAction<T> action) throws Exception {
batchExecute(mapperClass,list,action,BATCH_COUNT);
}
public <T> void batchExecute(Class<T> mapperClass, List<?> list, MapperAction<T> action, Integer BatchCount) throws Exception {
SqlSession batchSqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
try {
T mapper = batchSqlSession.getMapper(mapperClass);
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
action.executeWithSessionNotCommit(mapper,obj);
if (i != 0 && (i % BatchCount == 0)) {
batchSqlSession.commit();
batchSqlSession.clearCache();
}
}
batchSqlSession.commit();
batchSqlSession.clearCache();
}catch (Exception e){
batchSqlSession.rollback();
e.getLocalizedMessage();
}finally {
if (null != batchSqlSession) {
batchSqlSession.close();
}
}
}
public interface MapperAction<T> {
void executeWithSessionNotCommit(T mapper,Object obj);
}
}
以上的@Service和@Transactional是保证此批量执行类不在service层调用时也可产生作用添加的注解,当此执行类的batchExecute方法仅在业务service层调用时,可以不需要加上@Service和@Transactional,仅加上@Component,将此类交给spring管理即可。
二、JDBC的url加上参数rewriteBatchedStatements=true
url: jdbc:mysql://127.0.0.1:3306/agp?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&rewriteBatchedStatements=true
三、调用
try {
sqlBatchExecutor.batchExecute(DeptMapper.class,depts2,(mapper, obj) -> {
mapper.deleteById((Dept) obj);
});
} catch (Exception e) {
e.printStackTrace();
}
四、总结
适用于大数据量且又必须单条修改之类的操作,相对于mybatis的foreach拼接方式,批处理可以防止sql过长导致异常的问题;
批量删除,可以选择此种方式,也可以选择将list切割,然后使用foreach的方式,后者相对来说较快一些;
任何代码在符合业务逻辑要求和代码规范之后,都要注意它的效率问题,没必要只使用一种方式,在不同的情境下选择不一样的实现方式,达到最好的效果才是正道。