说明
1、主键上面需要有注解@TableId,@IdentifyPK
2、字段类型
package com.geekmice.springbootselfexercise.utils;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.excel.util.DateUtils;
import com.baomidou.mybatisplus.annotation.TableId;
import com.geekmice.springbootselfexercise.annotation.IdentifyPK;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.core.env.Environment;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.*;
import java.util.List;
import java.util.Objects;
/**
* @BelongsProject: self_pratise
* @BelongsPackage: com.geekmice.praise.utils
* @Author: pingmingbo
* @CreateTime: 2023-08-08 08:44
* @Description: jdbc工具类 说明:yml中添加&rewriteBatchedStatements=true
**/
@Slf4j
public class JdbcUtil {
public static final String ERROR_MSG = "error msg:【{}】";
public static final String LOG_ERROR_MSG="params:【{}】,error msg:【{}】";
public static final String ERROR_LOG_MSG="error msg:";
/**
* 访问环境变量
*/
private static Environment environment = SpringUtil.getBean(Environment.class);
/**
* 数据库连接,会话操作,执行SQL语句
*/
private static Connection connection = null;
/**
* @description 批量录入
*/
public static void saveBatch(Object[] arr, String sql) {
// 加载驱动
String driverClassName = environment.getProperty("spring.datasource.driver-class-name");
String username = environment.getProperty("spring.datasource.username");
String password = environment.getProperty("spring.datasource.password");
String url = environment.getProperty("spring.datasource.url");
try {
Class.forName(driverClassName);
connection = DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException | SQLException e) {
log.error(ERROR_MSG, e);
throw new IllegalArgumentException(e);
}
PreparedStatement preparedStatement = null;
try {
// 主键自增
preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
connection.setAutoCommit(false);
constructPreparedStatement(sql, preparedStatement, arr);
preparedStatement.executeBatch();
connection.commit();
} catch (SQLException e) {
// 异常,数据回滚
try {
connection.rollback();
} catch (SQLException ex) {
log.error(ERROR_MSG, e);
throw new IllegalArgumentException(e);
}
log.error(ERROR_MSG, e);
throw new IllegalArgumentException(e);
} finally {
if (Objects.nonNull(preparedStatement)) {
try {
preparedStatement.close();
} catch (SQLException e) {
log.error(ERROR_MSG, e);
throw new IllegalArgumentException(e);
}
}
if (Objects.nonNull(connection)) {
try {
connection.close();
} catch (SQLException e) {
log.error(ERROR_MSG, e);
throw new IllegalArgumentException(e);
}
}
}
}
/**
* @param sql SQL语句
* @param parameterList 入参
* @description 打印SQL方法,说明:批量操作,比较耗时,排查时候可以使用
*/
public static void showFinalSql(String sql, List<Object> parameterList) {
int paramCount = 0;
if (CollectionUtils.isNotEmpty(parameterList)) {
paramCount = parameterList.size();
}
if (paramCount < 1) {
log.info("sql:【{}】", sql);
return;
}
// 如果有参数,动态SQL语句
StringBuilder returnSql = new StringBuilder();
String[] subSql = sql.split("\\?");
for (int i = 0; i < paramCount; i++) {
Object item = parameterList.get(i);
if (item instanceof Integer || item instanceof BigDecimal || item instanceof Long) {
returnSql.append(subSql[i]).append(" ").append(item).append(" ");
} else if (item instanceof Date) {
String formatStr = DateFormatUtils.format((Date) item, DateUtils.DATE_FORMAT_19);
returnSql.append(subSql[i]).append("'").append(formatStr).append("'");
} else {
returnSql.append(subSql[i]).append("'").append(item).append("'");
}
}
if (subSql.length > parameterList.size()) {
returnSql.append(subSql[subSql.length - 1]);
}
log.info("打印SQL:【{}】", returnSql);
}
/**
* @param preparedStatement 预编译SQL
* @param arr 入参
* @description PreparedStatement赋值操作
*/
public static void constructPreparedStatement(String sql, PreparedStatement preparedStatement, Object[] arr) {
// 10个元素
int length = arr.length;
if (arr == null || arr.length == 0) {
return;
}
Object tempItem = arr[0];
Class<?> aClass = tempItem.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
// 获取除了主键之外的字段个数
int len = declaredFields.length;
// 定义map,key:序列号 value:所有字段 例如1对应user_name
// 标识主键,直接获取第一个字段
List<Object> sqlPrintList = Lists.newArrayList();
for (int i = 0; i < length; i++) {
Object o = arr[i];
int handleFieldCount = len - 1;
int j = 0;
for (Field declaredField : declaredFields) {
// 主键字段上有TableId注解 去除
if (Objects.nonNull(declaredField.getAnnotation(TableId.class))) {
continue;
}
// 主键字段有@IdentifyPK注解 去除
if (Objects.nonNull(declaredField.getAnnotation(IdentifyPK.class))) {
continue;
}
// 序列化号去除
if ("serialVersionUID".equals(declaredField.getName())) {
continue;
}
declaredField.setAccessible(true);
Class<?> itemType = declaredField.getType();
String itemTypeStr = itemType.toString();
Object o1ToValue;
try {
o1ToValue = declaredField.get(o);
} catch (IllegalAccessException e) {
log.error(LOG_ERROR_MSG, arr.toString(), e);
throw new RuntimeException(ERROR_LOG_MSG + e);
}
try {
if ("class java.lang.Integer".equals(itemTypeStr)) {
j++;
preparedStatement.setInt(j, (Integer) o1ToValue);
} else if ("class java.lang.String".equals(itemTypeStr)) {
j++;
preparedStatement.setString(j, (String) o1ToValue);
} else if ("class java.util.Date".equals(itemTypeStr)) {
j++;
preparedStatement.setString(j, DateFormatUtils.format((java.util.Date) o1ToValue, DateUtils.DATE_FORMAT_19));
} else if ("class java.math.BigDecimal".equals(itemTypeStr)) {
assert o1ToValue != null;
j++;
preparedStatement.setBigDecimal(j, new BigDecimal(o1ToValue.toString()));
} else if ("class java.lang.Long".equals(itemTypeStr)) {
j++;
preparedStatement.setLong(j, (Long) o1ToValue);
} else if ("class java.lang.Boolean".equals(itemTypeStr)) {
j++;
preparedStatement.setBoolean(j, (Boolean) o1ToValue);
}
sqlPrintList.add(o1ToValue);
if (j == handleFieldCount) {
preparedStatement.addBatch();
}
} catch (SQLException e) {
try {
connection.rollback();
} catch (SQLException ex) {
log.error(LOG_ERROR_MSG, arr.toString(), e);
throw new RuntimeException(ERROR_LOG_MSG + e);
}
log.error(LOG_ERROR_MSG, arr.toString(), e);
throw new RuntimeException(ERROR_LOG_MSG + e);
}
}
showFinalSql(sql, sqlPrintList);
sqlPrintList.clear();
}
}
}
@Test
public void handleBatchSaveTest(){
List<AccountDomain> list = new ArrayList(16);
list.add(AccountDomain.builder().money(new BigDecimal("1000.00")).uid("02").build());
String sql = "insert into account(uid,money) values(?,?)";
Object[] arr = list.stream().toArray();
JdbcUtil.saveBatch(arr,sql);
}
2023-09-27 15:08:13.525 INFO 26064 — [ main] c.g.s.utils.JdbcUtil : 打印SQL:【insert into account(uid,money) values(‘02’, 1000.00 )】