SQL百万数据高效插入框架

原生SQL高效插入框架(通用)

databaseUrl后面需要加上rewriteBatchedStatements=true,否则批量插入性能会收到很大的影响

业务代码

/**
     * 批量保存
     * @param list
     * @return {@link Boolean}
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    public static <T> Boolean saveBatch(List<T> list) throws SQLException {
        T info = list.get(0);
        List<Field> fieldList = Arrays.asList(info.getClass().getDeclaredFields());
        String prepareSql = generalPrepareSql(info, fieldList);
        Connection conn = null;
        PreparedStatement preparedStatement = null;
        try {
            Class.forName(driver);
            //连接数据库
            conn = DriverManager.getConnection(databaseUrl, databaseUser, databasePassword);
            conn.setAutoCommit(Boolean.FALSE);
            //预编译sql
            preparedStatement = conn.prepareStatement(prepareSql);
            //批量提交
            prepareExecuteBatch(list, fieldList, preparedStatement);
            conn.commit();
            return Boolean.TRUE;
        } catch (Exception e) {
            log.error("批量保存异常,数据:{},异常信息:{}", JSON.toJSONString(list), ExceptionUtils.getStackTrace(e));
            throw new RuntimeException(e.getMessage());
        } finally {
            if (Objects.nonNull(conn)) {
                conn.close();
            }
            if (Objects.nonNull(preparedStatement)) {
                preparedStatement.close();
            }
        }
    }

    /**
     * 准备执行批处理
     *
     * @param list              列表
     * @param fieldList         字段列表
     * @param preparedStatement 事先准备好声明中
     * @throws SQLException           sqlexception异常
     * @throws IllegalAccessException 非法访问异常
     */
    private static <T> void prepareExecuteBatch(List<T> list, List<Field> fieldList, PreparedStatement preparedStatement) throws SQLException, IllegalAccessException {
        fieldList.forEach(ele -> ele.setAccessible(Boolean.TRUE));
        int count = 0;
        for (T fileInfo : list) {
            for (int i = 1; i <= fieldList.size(); i++) {
                Field declaredField = fieldList.get(i - 1);
                Object o = declaredField.get(fileInfo);
                setStatement(preparedStatement, i, declaredField, o);
            }
            preparedStatement.addBatch();
            ++count;
            if (count > batchSize) {
                preparedStatement.executeBatch();
                preparedStatement.clearBatch();
                count = 0;
            }
        }
        preparedStatement.executeBatch();
        preparedStatement.clearBatch();
    }

    /**
     * preparedStatement 设值,下面只对部分类型进行判断,如果有需要,可以自行增加
     * @param preparedStatement
     * @param i 参数序号
     * @param declaredField 字段
     * @param o 值
     * @throws SQLException
     */
    private static void setStatement(PreparedStatement preparedStatement, int i, Field declaredField, Object o) throws SQLException {
        if (declaredField.getType().equals(String.class)) {
            if (Objects.isNull(o)) {
                preparedStatement.setString(i, "");
                return;
            }
            preparedStatement.setString(i, (String) o);
            return;
        }
        if (declaredField.getType().equals(Integer.class)) {
            if (Objects.isNull(o)) {
                preparedStatement.setInt(i, 0);
                return;
            }
            preparedStatement.setInt(i, (Integer) o);
            return;
        }
        if (declaredField.getType().equals(Double.class)) {
            if (Objects.isNull(o)) {
                preparedStatement.setDouble(i, 0L);
                return;
            }
            preparedStatement.setDouble(i, (Double) o);
            return;
        }
        if (declaredField.getType().equals(Float.class)) {
            if (Objects.isNull(o)) {
                preparedStatement.setFloat(i, 0f);
                return;
            }
            preparedStatement.setFloat(i, (Float) o);
            return;
        }
        if (declaredField.getType().equals(BigDecimal.class)) {
            if (Objects.isNull(o)) {
                preparedStatement.setBigDecimal(i, BigDecimal.valueOf(0L));
                return;
            }
            preparedStatement.setBigDecimal(i, (BigDecimal) o);
            return;
        }
        if (declaredField.getType().equals(Byte.class)) {
            if (Objects.isNull(o)) {
                preparedStatement.setByte(i, (byte) 0);
                return;
            }
            preparedStatement.setByte(i, (Byte) o);
            return;
        }
        if (declaredField.getType().equals(Boolean.class)) {
            if (Objects.isNull(o)) {
                preparedStatement.setBoolean(i, Boolean.FALSE);
                return;
            }
            preparedStatement.setBoolean(i, (Boolean) o);
            return;
        }
        if (declaredField.getType().equals(Long.class)) {
            if (Objects.isNull(o)) {
                preparedStatement.setLong(i, 0);
                return;
            }
            preparedStatement.setLong(i, (long) o);
            return;
        }
        if (declaredField.getType().equals(Collection.class)) {
            if (Objects.isNull(o)) {
                preparedStatement.setString(i, "");
                return;
            }
            preparedStatement.setString(i, String.valueOf(o));
            return;
        }
        if (declaredField.getType().equals(java.util.Date.class) || declaredField.getType().equals(java.sql.Date.class)) {
            if (Objects.isNull(o)) {
                preparedStatement.setDate(i, new java.sql.Date(System.currentTimeMillis()));
                return;
            }
            Date date = (Date) o;
            preparedStatement.setTimestamp(i, new Timestamp(date.getTime()));
        }
    }

    /**
     * 生成预编译sql
     *
     * @param info      信息
     * @param fieldList 字段列表
     * @return {@link String}
     */
    private static <T> String generalPrepareSql(T info, List<Field> fieldList) {
        TableName annotation = info.getClass().getAnnotation(TableName.class);
        if (Objects.isNull(annotation)) {
            throw new IllegalArgumentException("相关类请使用注解@TableName修饰");
        }
        StringBuilder sqlBuilder = new StringBuilder();
        String tableName = annotation.value();
        sqlBuilder.append("insert into ");
        sqlBuilder.append(tableName);

        List<String> fieldNameList = fieldList.stream()
                .map(ele -> {
                    Column column = ele.getAnnotation(Column.class);
                    if (Objects.nonNull(column) && StringUtils.isNotBlank(column.value())) {
                        return column.value();
                    }
                    return camelToUnderscore(ele.getName());
                })
                .collect(Collectors.toList());
        String fieldName = Joiner.on(",").join(fieldNameList);
        sqlBuilder.append("(").append(fieldName).append(") ");
        sqlBuilder.append("values");
        int size = fieldList.size();
        List<String> markList = new ArrayList<>();
        for (int i = 0; i < size; i++) {
            markList.add("?");
        }
        sqlBuilder.append("(").append(Joiner.on(",").join(markList)).append(")");
        return sqlBuilder.toString();
    }

    /**
     * 将驼峰格式的字符串转换为下划线格式
     *
     * @param camel 驼峰格式的字符串
     * @return 下划线格式的字符串
     */
    private static String camelToUnderscore(String camel) {
        // 使用正则表达式将小驼峰格式的字符串替换为下划线格式
        return camel.replaceAll("([A-Z])", "_$1").toLowerCase();
    }

自定义注解

@TableName 和 @Column 可以自己定义,可以使用现成的一些注解,改动注解名后,需要改动上面的代码,通过注解或者表名或者列名

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TableName {
    String value() default "";
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Column {
    String value() default "";
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值