一、背景
为了提升性能,时常需要批量执行SQL语句。但是MyBATIS官方并没有给出很好的解决方案。俗话说,自己动手,丰衣足食。自己写一个呗
二、实现思路
Spring中的jdbcTemplate.batchUpdate()能够批量执行SQL语句。所以,只要想办法拿到Mybatis中的SQL语句(以?作为占位符),然后结合jdbcTemplate.batchUpdate()就能够批量执行了
但是,要注意的是此处没有事务控制,事务控制在业务外层需要自己控制
三、代码实现
/** * 批处理的最大执行次数 */ protected static final int MAX_BATCH_COUNT = 1000; @Resource private JdbcTemplate jdbcTemplate; /** * <p> * 批量处理update或insert, 使用jdbcTemplate做批处理 * </p> * <p> * List中的对象需要和sqlMapper中的parameterType属性一致 * </p> * * @param sqlId sqlMap中的ID * @param objList 需要增加或更新的obj对象 * @return */ public <T> int executeBatch(@Nullable final String sqlId, @Nullable final List<T> objList) { if (StringUtils.isEmpty(sqlId) || CollectionUtils.isEmpty(objList)) { throw new IllegalArgumentException("请求参数不能为空!"); } SqlSessionDaoSupport daoSupport = (SqlSessionDaoSupport) updateDAO; // 从mybatis配置文件中获取动态sql BoundSql bound = daoSupport.getSqlSession().getConfiguration().getMappedStatement(sqlId).getSqlSource() .getBoundSql(objList.get(0)); String preparedSql = bound.getSql(); final List<ParameterMapping> parameterMappingList = bound.getParameterMappings(); int[][] result = jdbcTemplate.batchUpdate(preparedSql, objList, MAX_BATCH_COUNT, new ParameterizedPreparedStatementSetter<T>() { @Override public void setValues(PreparedStatement ps, T obj) throws SQLException { Map map = convertParamToMap(obj); if (map.size() == 0) { throw new IllegalArgumentException("参数类型错误!"); } for (int index = 0; index < parameterMappingList.size(); index++) { ParameterMapping parameter = parameterMappingList.get(index); Object o = map.get(parameter.getProperty()); if (o == null) { if (parameter.getJavaType() == Boolean.class) { if (parameter.getProperty().startsWith("is")) { String booleanPropertyName = StringUtils.uncapitalize(StringUtils.substring( parameter.getProperty(), 2)); o = map.get(booleanPropertyName); } if (o == null) { logger.error("请求参数属性名称不合法, 找不到批处理请求数据的属性值[" + parameter.getProperty() + "]"); } } } else { // 如果是枚举类型,则转换成name if (o.getClass().isEnum()) { o = o.toString(); } } ps.setObject(index + 1, o); } } }); int succ = 0; for (int i = 0; i < result.length; i++) { for (int j = 0; j < result[i].length; j++) { succ += result[i][j] > 0 ? 1 : 0; } } return succ; } private Map convertParamToMap(Object param) { if (param == null) { return Maps.newHashMap(); } if (param instanceof Map) { return (Map) param; } else { return BeanMappingUtil.describe(param); } }
其中:BeanMappingUtil是guava的包