package cn.mybatisboost.lang.provider; import cn.mybatisboost.core.Configuration; import cn.mybatisboost.core.ConfigurationAware; import cn.mybatisboost.core.SqlProvider; import cn.mybatisboost.util.EntityUtils; import cn.mybatisboost.util.MapperUtils; import cn.mybatisboost.util.MyBatisUtils; import cn.mybatisboost.util.SqlUtils; import cn.mybatisboost.util.tuple.BinaryTuple; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.reflection.MetaObject; import java.sql.Connection; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; public class InsertEnhancement implements SqlProvider, ConfigurationAware { private static final Pattern PATTERN_LITERAL_COLUMNS = Pattern.compile("((NOT|not) )?(\\w+, ?)*\\w+|\\*"); private Configuration configuration; @Override public void replace(Connection connection, MetaObject metaObject, MappedStatement mappedStatement, BoundSql boundSql) { String sql = boundSql.getSql(); String sqlUpperCase = sql.toUpperCase(); if (mappedStatement.getSqlCommandType() == SqlCommandType.INSERT && !sqlUpperCase.startsWith("INSERT INTO ") && sqlUpperCase.startsWith("INSERT ")) { Matcher matcher = PATTERN_LITERAL_COLUMNS.matcher(sql = sql.substring(7)); if (!matcher.find()) { throw new IllegalStateException("Found INSERT statement but no column is specified"); } String literalColumns = matcher.group(); Class<?> entityType = MapperUtils.getEntityTypeFromMapper (mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf('.'))); boolean mapUnderscoreToCamelCase = (boolean) metaObject.getValue("delegate.configuration.mapUnderscoreToCamelCase"); BinaryTuple<List<String>, List<String>> propertiesAndColumns = SqlUtils.getPropertiesAndColumnsFromLiteralColumns(literalColumns, entityType, mapUnderscoreToCamelCase); List<?> entities = boundSql.getParameterObject() instanceof Map ? (List<?>) ((Map) boundSql.getParameterObject()).get("param1") : Collections.singletonList(Objects.requireNonNull(boundSql.getParameterObject(), "ParameterObject mustn't be null")); if (entities.isEmpty()) { throw new IllegalArgumentException("Can't insert empty list"); } else { String additionalStatement = sql.substring(literalColumns.length()); org.apache.ibatis.session.Configuration configuration = (org.apache.ibatis.session.Configuration) metaObject.getValue("delegate.configuration"); Object parameterObject = buildParameterObject(entities); metaObject.setValue("delegate.boundSql.sql", buildSql(entityType, propertiesAndColumns.second(), entities.size(), additionalStatement)); metaObject.setValue("delegate.boundSql.parameterMappings", MyBatisUtils.getListParameterMappings(configuration, propertiesAndColumns.first(), entities.size())); metaObject.setValue("delegate.boundSql.parameterObject", parameterObject); MyBatisUtils.getMetaObject(metaObject.getValue("delegate.parameterHandler")) .setValue("parameterObject", parameterObject); } } } @Override public void setConfiguration(Configuration configuration) { this.configuration = configuration; } private Object buildParameterObject(List<?> entities) { Map<String, Object> map = new HashMap<>(); map.put("list", entities); map.put("collection", entities); return map; } private String buildSql(Class<?> entityType, List<String> columns, int batchSize, String additionalStatement) { StringBuilder sqlBuilder = new StringBuilder(); sqlBuilder.append("INSERT INTO ") .append(EntityUtils.getTableName(entityType, configuration.getNameAdaptor())); sqlBuilder.append(" ("); columns.forEach(c -> sqlBuilder.append(c).append(", ")); sqlBuilder.setLength(sqlBuilder.length() - 2); sqlBuilder.append(") VALUES "); for (int i = 0; i < batchSize; i++) { sqlBuilder.append("("); columns.forEach(c -> sqlBuilder.append("?, ")); sqlBuilder.setLength(sqlBuilder.length() - 2); sqlBuilder.append("), "); } sqlBuilder.setLength(sqlBuilder.length() - 2); sqlBuilder.append(additionalStatement); return sqlBuilder.toString(); } }