批量更新和批量插入,内含jdbc工具类
一、批量更新
1.JDBC直接批量更新
public void updateBBCPResult(List<Example> dataList, String obj) {Connection connection = null;PreparedStatement preparedStatement = null;try {connection = JDBCUitls.getConnection();connection.setAutoCommit(false);// 批量更新 BBCP 结果String sql = "UPDATE ta_test SET amt = ? WHERE serial_no = ?";preparedStatement = connection.prepareStatement(sql);for (Example example : dataList) {preparedStatement.setDouble(1, Double.valueOf(example.getActualDeductionAmount()));preparedStatement.setString(2, example.getId());preparedStatement.addBatch();}preparedStatement.executeBatch();connection.commit();} catch (SQLException e) {try {if (connection != null) {connection.rollback();}} catch (SQLException e2) {log.error(e2.getMessage());}} finally {closeResource(preparedStatement);closeResource(connection);}}
2.使用工具类将查出来的List数据分批,然后再分批更新
工具类import java.util.ArrayList;
import java.util.List;public class BatchUtils {// 分片大小:根据每条数据的参数数计算(建议保守设为1000,避免超参)private static final int BATCH_SIZE = 1000;/*** 集合分片* @param list 原始大集合* @param <T> 数据类型* @return 分片后的小集合列表*/public static <T> List<List<T>> splitBatch(List<T> list) {List<List<T>> batchList = new ArrayList<>();if (list == null || list.isEmpty()) {return batchList;}int totalSize = list.size();// 计算需要分多少批int batchCount = (totalSize + BATCH_SIZE - 1) / BATCH_SIZE;for (int i = 0; i < batchCount; i++) {// 计算每批的起始和结束索引int start = i * BATCH_SIZE;int end = Math.min((i + 1) * BATCH_SIZE, totalSize);// 截取小集合并加入结果batchList.add(list.subList(start, end));}return batchList;}
}
二、批量插入
1.直接写sql,使用<foreach>
标签
mapper
void insertUserTodoTask(@Param("list") List<UserTodoTask> userTodoTask);
xml
<insert id="insertUserTodoTask">INSERT INTO user_todo_task (app_id,doto_id,creator_ehr,receiver_ehr,subject,module_name,content,url,create_at,send_hx_message) VALUES<foreach collection="list" item="task" separator=",">(#{task.app_id},#{task.doto_id},#{task.creator_ehr},#{task.receiver_ehr},#{task.subject},#{task.module_name},#{task.content},#{task.url},#{task.create_at},#{task.send_hx_message})</foreach></insert>
2.使用JDBCUtil工具类直接批量插入
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.google.common.base.CaseFormat;
import com.mingyoutech.appcase.tbs.util.exception.ExceptionUtils;
import com.mingyoutech.mybi.boot.framework.core.domain.R;
import com.mingyoutech.mybi.boot.framework.core.utils.SpringUtils;
import lombok.extern.slf4j.Slf4j;import javax.sql.DataSource;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.*;/*** 只用于大数据量的插入,例如几十万条这种,常规增删改查,用mybatis即可*/
@Slf4j
public class JDBCUitls {/*** 属性名与字段名映射规则*///小驼峰属性名映射下划线字段名public static final String LOWER_CAMEL_CASE_MODEL = "lowerCamelCase";//不转换(属性名与字段名相同时)public static final String DEFAULT_MODEL = "default";public static DataSource dataSource = SpringUtils.getBean(DataSource.class);/*** 批量插入* 表名,插入字段,都在集合的泛型类中通过@TableName,和@TableField指定* @param dataList 数据集合*/public static R<String> batchSaveDate(List<?> dataList) {LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap<>();String tableName;if (dataList.size()>0){Class<?> aClass = dataList.get(0).getClass();//取表名if(aClass.isAnnotationPresent(TableName.class)){TableName annotation = aClass.getAnnotation(TableName.class);tableName = annotation.value();Field[] declaredFields = aClass.getDeclaredFields();for (Field field : declaredFields){if (field.isAnnotationPresent(TableField.class)){String columnName = field.getAnnotation(TableField.class).value();String fieldName = field.getName();linkedHashMap.put(fieldName,columnName);}}if (linkedHashMap.size() == 0) return R.fail(aClass.getName()+"上没有任何一个属性拥有@TableField注释");}else {return R.fail("类上缺少@TableName注解,无法确定目标表");}}else {return R.fail("查询数据为空");}return batchSaveDate(dataList, tableName, linkedHashMap);}/*** 批量插入* 1.指定插入的字段并在filedColumnMap中声明类属性与表字段的映射关系* 2.只插入filedColumnMap中存在的字段** @param dataList 数据集合* @param tableName 表名* @param filedColumnMap 以及属性与字段的映射关系* @return 插入结果*/public static R<String> batchSaveDate(List<?> dataList, String tableName, LinkedHashMap<String, String> filedColumnMap) {int size = dataList.size();if (size == 0) {return R.fail("数据集合长度为0");}Object o = dataList.get(0);Class<?> aClass = o.getClass();Map<String, Field> fieldCache = new HashMap<>();for (String fieldName : filedColumnMap.keySet()) {try {Field field = aClass.getDeclaredField(fieldName);field.setAccessible(true);fieldCache.put(fieldName, field);} catch (NoSuchFieldException e) {return R.fail("找不到字段: " + fieldName);}}Connection conn = null;PreparedStatement ps = null;try {long startTime = System.currentTimeMillis();log.info(size + "条,开始导入到数据库时间:" + startTime + "ms");conn = dataSource.getConnection();//控制事务:默认不提交conn.setAutoCommit(false);StringBuilder stringBuilder = new StringBuilder("insert into " + tableName + "(");Set<Map.Entry<String, String>> entries = filedColumnMap.entrySet();for (Map.Entry<String, String> entry : entries) {String column = entry.getValue();stringBuilder.append(column).append(",");}stringBuilder.deleteCharAt(stringBuilder.length() - 1);stringBuilder.append(")values(");stringBuilder.append("?,".repeat(entries.size()));stringBuilder.deleteCharAt(stringBuilder.length() - 1);stringBuilder.append(")");String sql = stringBuilder.toString();ps = conn.prepareStatement(sql);//循环结果集for (Object item : dataList) {int j = 1;for (Map.Entry<String, String> entry : entries) {Field field = fieldCache.get(entry.getKey());Object filedObj = field.get(item);String value = filedObj != null ? filedObj.toString():"";ps.setString(j, value);j++;}ps.addBatch();}//执行批处理ps.executeBatch();//手动提交事务conn.commit();long endTime = System.currentTimeMillis();log.info(size + "条,结束导入到数据库时间:" + endTime + "ms");log.info(size + "条,导入用时:" + (endTime - startTime) + "ms");} catch (Exception e) {try {if (conn!=null){conn.rollback();}} catch (SQLException throwables) {throwables.printStackTrace();}e.printStackTrace();return R.fail(ExceptionUtils.getMsg(e, 500));} finally {//关连接try {assert ps != null;ps.close();Objects.requireNonNull(conn).setAutoCommit(true);conn.close();} catch (SQLException e) {e.printStackTrace();}}return R.ok(size + "导入成功");}/*** 批量插入* 1.批量插入所有属性对应的字段** @param dataList 数据集合* @param tableName 表名* @param model 属性名与字段名映射模式* default :字段名与属性名一致不需要转换* lowerCamelCase : 小驼峰属性名映射下划线字段名* @return 插入结果*/public static R<String> batchSaveDate(List<Object> dataList, String tableName, String model) throws SQLException {int size = dataList.size();if (size == 0) {return R.fail("数据集合长度为0");}Object o = dataList.get(0);Class<?> aClass = o.getClass();//JDBC分批插入+事务操作完成对数据的插入Connection conn = null;PreparedStatement ps = null;try {long startTime = System.currentTimeMillis();log.info(size + "条,开始导入到数据库时间:" + startTime + "ms");conn = dataSource.getConnection();//控制事务:默认不提交conn.setAutoCommit(false);Field[] fields = aClass.getDeclaredFields();ArrayList<String> columns = new ArrayList<>();for (Field field : fields) {columns.add(field.getName());}String sql = getSql(columns, tableName, model);ps = conn.prepareStatement(sql);//循环结果集for (Object item : dataList) {int j = 1;for (Field field : fields) {field.setAccessible(true);String value = (String) field.get(item);ps.setString(j, value);j++;}ps.addBatch();}//执行批处理ps.executeBatch();//手动提交事务conn.commit();long endTime = System.currentTimeMillis();log.info(size + "条,结束导入到数据库时间:" + endTime + "ms");log.info(size + "条,导入用时:" + (endTime - startTime) + "ms");} catch (Exception e) {e.printStackTrace();return R.fail(ExceptionUtils.getMsg(e, 500));} finally {//关连接try {assert ps != null;ps.close();Objects.requireNonNull(conn).setAutoCommit(true);conn.close();} catch (SQLException e) {e.printStackTrace();}}return R.ok(size + "条导入成功");}/*** 批量插入,指定插入字段** @param fieldNames 需要插入到数据库的属性名称数组* @param dataList 数据集合* @param tableName 表名* @param model 属性名与字段名映射模式* default :字段名与属性名一致* lowerCamelCase : 小驼峰转换下划线* @return 插入结果*/public static R<String> batchSaveDate(List<Object> dataList, String tableName, String[] fieldNames, String model) {int size = dataList.size();if (size == 0) {return R.fail("数据集合长度为0");}Object o = dataList.get(0);Class<?> aClass = o.getClass();//JDBC分批插入+事务操作完成对数据的插入Connection conn = null;PreparedStatement ps = null;try {long startTime = System.currentTimeMillis();log.info(size + "条,开始导入到数据库时间:" + startTime + "ms");conn = dataSource.getConnection();//控制事务:默认不提交conn.setAutoCommit(false);String sql = getSql(Arrays.asList(fieldNames), tableName, model);ps = conn.prepareStatement(sql);//循环结果集for (Object item : dataList) {int j = 1;for (String fieldName : fieldNames) {Field field = aClass.getDeclaredField(fieldName);field.setAccessible(true);String value = (String) field.get(item);ps.setString(j, value);j++;}ps.addBatch();}//执行批处理ps.executeBatch();//手动提交事务conn.commit();long endTime = System.currentTimeMillis();log.info(size + "条,结束导入到数据库时间:" + endTime + "ms");log.info(size + "条,导入用时:" + (endTime - startTime) + "ms");} catch (Exception e) {e.printStackTrace();return R.fail(ExceptionUtils.getMsg(e, 500));} finally {//关连接try {assert ps != null;ps.close();Objects.requireNonNull(conn).setAutoCommit(true);conn.close();} catch (SQLException e) {e.printStackTrace();}}return R.ok(size + "条导入成功");}/*** 生成插入sql* @param filedNames 要插入的属性名数组* @param tableName 表名* @param model 属性名与字段的自动映射模式* @return 插入sql*/private static String getSql(List<String> filedNames, String tableName, String model) {StringBuilder stringBuilder = new StringBuilder("insert into " + tableName + " (");if (model.equals(LOWER_CAMEL_CASE_MODEL)) {for (String column : filedNames) {column = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, column);stringBuilder.append(column).append(",");}} else if (model.equals(DEFAULT_MODEL)) {for (String column : filedNames) {stringBuilder.append(column).append(",");}}stringBuilder.deleteCharAt(stringBuilder.length() - 1);stringBuilder.append(")values(");stringBuilder.append("?,".repeat(filedNames.size()));stringBuilder.deleteCharAt(stringBuilder.length() - 1);stringBuilder.append(")");return stringBuilder.toString();}public static Connection getConnection() throws SQLException {return dataSource.getConnection();}}