JavaExcel:自动生成数据表并插入数据

news2024/11/16 18:33:39

故事背景

出于好奇,当下扫描excel读取数据进数据库 or 导出数据库数据组成excel的功能层出不穷,代码也是前篇一律,poi或者easy excel两种SDK的二次利用带来了各种封装方法。

那么为何不能直接扫描excel后根据列的属性名与行数据的属性建立SQL数据表,并将数据插入到数据表中,再通过前端与用户交互进行SQL组装,得到用户想要的数据结果。

模块架构图

采取原始的数据库信息读取,配置编程式事务(代码块维度锁事务),封装原始的JDBC执行模板。

excel读取列属性名,转换成英文作为表的属性名,行解析器解析数据行数据属性作为表属性的数据类型。

代码构成

基础实体:

ColumnEntity.java
@Data
@EqualsAndHashCode
public class ColumnEntity {
    private String columnName;
    private String columnSqlInfo;
    public void clear(){
        setColumnName(null);
        setColumnSqlInfo(null);
    }
}
ValueEntity.java
@Data
public class ValueEntity{
    private String columnName;
    /*数据拼接到SQL上也是字符串类型*/
    private String valueOfString;
    public void clear(){
        setColumnName(null);
        setValueOfString(null);
    }
}
JsonResponse.java
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JsonResponse {
    private int code = -1;
    private String message;
    private String json;

    public static JsonResponse success(String json){
        JsonResponse jsonResponse = new JsonResponse();
        jsonResponse.setCode(0);
        jsonResponse.setJson(json);
        jsonResponse.setMessage("访问成功");
        return jsonResponse;
    }

    public static JsonResponse fail(String massage){
        JsonResponse jsonResponse = new JsonResponse();
        jsonResponse.setMessage(massage);
        return jsonResponse;
    }
}

api对前端提供特性:

WebMvcConfiguration.java
@Configuration
@Slf4j
public class WebMvcConfiguration implements WebMvcConfigurer , HandlerInterceptor {
    @Override
    public void addInterceptors(@NonNull InterceptorRegistry registry) {
        registry.addInterceptor(this);
        WebMvcConfigurer.super.addInterceptors(registry);
    }

    @Override
    public void addCorsMappings(@NonNull CorsRegistry registry) {
        WebMvcConfigurer.super.addCorsMappings(registry);
    }

    @Override
    public void postHandle(@NonNull HttpServletRequest request,@NonNull  HttpServletResponse response,@NonNull  Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public boolean preHandle(HttpServletRequest request,@NonNull HttpServletResponse response,@NonNull Object handler) throws Exception {
        String sql = request.getParameter("sql");
        String tableName = request.getParameter("tableName");
        if (StringUtils.isNotEmpty(sql)){
            if (!sql.contains("test") || !tableName.contains("test")){
                log.error("过滤非法请求:{}",sql);
                return false;
            }
            if (sql.contains("DELETE") || sql.contains("delete") || sql.contains("UPDATE") || sql.contains("update")){
                log.error("过滤非法请求:{}",sql);
                return false;
            }
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void afterCompletion(@NonNull HttpServletRequest request,@NonNull HttpServletResponse response,@NonNull Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}
ExcelDataController.java
@RestController
@RequestMapping("/data")
@CrossOrigin
@Slf4j
public class ExcelDataController {

    @Resource
    private SqlDataProvider sqlDataProvider;

    @GetMapping("/handle/sql")
    @ResponseBody
    public JsonResponse handleData(@RequestParam String sql) {
        String executed = sqlDataProvider.executeQuerySql(sql);
        return Objects.equals("", executed)
                ? JsonResponse.fail("sql执行错误")
                : JsonResponse.success(executed);
    }
}
ExcelResolveController.java
@RestController
@RequestMapping("/excel")
@CrossOrigin
@Slf4j
public class ExcelResolveController {

    @Resource
    private ExcelEntityService excelEntityService;

    @RequestMapping("/createAndInsert")
    @ResponseBody
    public void uploadExcel(@RequestParam("fileName") MultipartFile file) {
        excelEntityService.createTable(file);
        excelEntityService.insertEntity(file);
    }

    @RequestMapping("/insert")
    @ResponseBody
    public void insertExcel(@RequestParam("fileName") MultipartFile file) {
        excelEntityService.insertEntity(file);
    }

    @RequestMapping("/drop")
    @ResponseBody
    public void delData(@RequestParam("fileName")String fileName){
        excelEntityService.dropTable(fileName);
    }

}
ExcelEntityService.java
public interface ExcelEntityService {
    /**
     * 创建数据表
     *
     * @param file 文件
     */
    void createTable(MultipartFile file);

    /**
     * 插入数据实体
     *
     * @param file 文件
     */
    void insertEntity(MultipartFile file);

    /**
     * 删除数据表
     * @param fileName 文件名
     */
    void dropTable(String fileName);
}
ExcelEntityServiceImpl.java
@Service
@Slf4j
@ConditionalOnBean({SqlDataProvider.class,SqlSpliceProvider.class})
public class ExcelEntityServiceImpl implements ExcelEntityService {

    @Resource
    private SqlDataProvider sqlDataProvider;

    @Resource
    private SqlSpliceProvider spliceProvider;

    @Resource
    private ReadExcelService readExcelService;

    @Override
    public void createTable(MultipartFile file) {
        String dropTableSql = spliceProvider.dropTableSql(file.getName());
        log.info("删表SQL:{}",dropTableSql);
        sqlDataProvider.executeSql(dropTableSql);
        List<ColumnEntity> excelColumnList = readExcelService.getExcelColumnList(file);
        StringBuffer stringBuffer = spliceProvider.spliceCreateTableSql(excelColumnList, file.getName());
        log.warn("建表SQL:{}", stringBuffer);
        sqlDataProvider.executeSql(stringBuffer.toString());
    }

    @Override
    public void insertEntity(MultipartFile file) {
        String existsTableSql = spliceProvider.existsTableSql(file.getName());
        Object aReturn = sqlDataProvider.executeSqlAndGetReturn(existsTableSql);
        if (Objects.nonNull(aReturn)) {
            List<ColumnEntity> excelColumnList = readExcelService.getExcelColumnList(file);
            Map<Integer, List<ValueEntity>> excelRowDataMap = readExcelService.getExcelRowDataMap(file, excelColumnList);
            List<String> stringBuffer1 = spliceProvider.spliceInsertValueSql(excelRowDataMap, file.getName());
            stringBuffer1.forEach(s -> sqlDataProvider.executeSql(s));
        }else{
            log.warn("不存在数据表:{}",file.getName());
        }
    }

    @Override
    public void dropTable(String fileName) {
        String dropTableSql = spliceProvider.dropTableSql(fileName);
        log.info("删表SQL:{}",dropTableSql);
        sqlDataProvider.executeSql(dropTableSql);
    }
}

 excel解析器:

ReadExcelService.java
public interface ReadExcelService {
    /**
     * 读取EXCEL的列属性列表
     *
     * @param file 文件
     * @return 列属性实体列表(只含有列的属性)
     */
    List<ColumnEntity> getExcelColumnList(MultipartFile file);

    /**
     * 读取每一行的行实体列表,一个LIST为一行
     *
     * @param file             文件
     * @param columnEntityList 列实体列表
     * @return 全部的值MAP<行号 , 行的属性值LIST>
     */
    Map<Integer, List<ValueEntity>> getExcelRowDataMap(MultipartFile file, List<ColumnEntity> columnEntityList);

}
ReadExcel.java
@Slf4j
@Service
public class ReadExcel implements ReadExcelService {
    private static final Integer LIMIT_SCAN_NUM = 500 * 1000;

    /**
     * 根据文件名读取Excel文件获取列信息列表
     *
     * @param file 文件
     * @return List<列实体>
     */
    @Override
    public List<ColumnEntity> getExcelColumnList(MultipartFile file) {
        List<ColumnEntity> list;
        Workbook workbook = getWorkbook(file);
        if (Objects.isNull(workbook)) {
            return new LinkedList<>();
        }
        list = getExcelColumnList(workbook);
        return list;
    }

    /**
     * 获取行数与对应行得值SQL实体列表
     *
     * @param file             工作薄文件
     * @param columnEntityList 列信息对象
     * @return map<行号 , 行内每一个单元格得值SQL实体列表>
     */
    @Override
    public Map<Integer, List<ValueEntity>> getExcelRowDataMap(MultipartFile file, List<ColumnEntity> columnEntityList) {
        Workbook workbook = getWorkbook(file);
        if (Objects.isNull(workbook)) {
            return new HashMap<>();
        }
        Sheet sheet = workbook.getSheetAt(0);
        int lastRowNum = sheet.getLastRowNum();
        Map<Integer, List<ValueEntity>> map = new HashMap<>();
        if (lastRowNum <= LIMIT_SCAN_NUM) {
            for (int i = 1; i <= lastRowNum; i++) {
                Row row = sheet.getRow(i);
                List<ValueEntity> list = new LinkedList<>();
                for (int j = 0; j < row.getPhysicalNumberOfCells(); j++) {
                    ValueEntity valueEntity = new ValueEntity();
                    valueEntity.setColumnName(columnEntityList.get(j).getColumnName());
                    valueEntity.setValueOfString(ExcelReadStringUtil.getValueSqlString(row.getCell(j)));
                    list.add(valueEntity);
                }
                map.put(i, list);
            }
        }else{
            throw new RuntimeException("扫描的Excel文件数据量超过限定值,请检查核定容量");
        }
        return map;
    }

    /**
     * 解析返回excel数据表第一行属性信息列表数据
     *
     * @param workbook 数据工作薄
     * @return List<列实体>
     */
    private static List<ColumnEntity> getExcelColumnList(Workbook workbook) {
        /*默认取第一个工作表的第一行数据与第二行数据(第一行用来感知属性名称、第二行用来感知属性类型)*/
        Sheet dataSheet = workbook.getSheetAt(0);
        Row topRow = dataSheet.getRow(0);
        Row typeRow = dataSheet.getRow(1);
        if (topRow.getPhysicalNumberOfCells() != typeRow.getPhysicalNumberOfCells()) {
            log.error("数据表列行与数据行列数不一致!退出解析,请整理数据表格式!");
            throw new RuntimeException("DataSheet is error: com.runjing.resolve_excel_auto.excel.ReadExcel.getExcelColumnList(org.apache.poi.ss.usermodel.Workbook)");
        }
        List<ColumnEntity> columnEntityList = new LinkedList<>();
        for (int i = 0; i < topRow.getPhysicalNumberOfCells(); i++) {
            ColumnEntity columnEntity = new ColumnEntity();
            Cell nameCell = topRow.getCell(i);
            Cell typeRowCell = typeRow.getCell(i);
            columnEntity.setColumnName(ExcelReadStringUtil.transferPinYin(nameCell.getStringCellValue()));
            columnEntity.setColumnSqlInfo(ExcelReadStringUtil.switchCellDataSqlInfo(typeRowCell));
            columnEntityList.add(columnEntity);
        }
        return columnEntityList;
    }

    /**
     * 文件转换工作簿对象
     *
     * @param file excel文件
     * @return 工作簿对象
     */
    private static Workbook getWorkbook(MultipartFile file) {
        InputStream is = null;
        try {
            is = file.getInputStream();
            Workbook workbook;
            workbook = WorkbookFactory.create(is);
            return workbook;
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

SQL拼接器:

ExcelReadStringUtil.java
public class ExcelReadStringUtil {

    /*获取构建表格数据属性SQL*/
    public static String switchCellDataSqlInfo(Cell dataCell) {
        return switch (dataCell.getCellType()) {
            case NUMERIC -> " double default 0.00 ";
            case STRING -> " varchar(100) default null";
            case FORMULA -> " varchar default null";
            case BOOLEAN -> " tinyint(1) default 0";
            default -> " varchar(64) default null";
        } + ",";
    }

    /*获取单元格值SQL*/
    public static String getValueSqlString(Cell dataCell) {
        return switch (dataCell.getCellType()) {
            case NUMERIC -> String.valueOf(dataCell.getNumericCellValue());
            case STRING -> quotesHandle(dataCell.getStringCellValue());
            case FORMULA -> quotesHandle(dataCell.getCellFormula());
            case BOOLEAN -> transferBool(dataCell.getBooleanCellValue());
            default -> "null";
        };
    }

    /**
     * 单引号包裹字段
     *
     * @param fieldValue 字段值
     * @return 包裹后字段串
     */
    public static String quotesHandle(String fieldValue) {
        return "'" + fieldValue + "'";
    }

    /**
     * 布尔类型转换
     *
     * @param arg1 入参
     * @return 转换值
     */
    public static String transferBool(Boolean arg1) {
        return arg1 ? "0" : "1";
    }
    /**
     * 将汉字串转成拼音串
     *
     * @param columnChineseName 汉字字段名
     * @return 字段拼音
     */
    public static String transferPinYin(String columnChineseName) {
        /*转换中文为简体拼音*/
        return LanguageUtil.convertChineseLan2PinYinAbbreviation(columnChineseName, LanguageUtil.CHINESE_CHAR_REG_SIMPLIFIED);
    }
}
SqlSpliceStringUtil.java
public class SqlSpliceStringUtil {

    /**
     * 反引号处理
     *
     * @param fieldName 字段名称
     * @return 被反引号包裹得字段名称
     */
    public static String quotesHandle(String fieldName) {
        return "`" + fieldName + "`";
    }

    /**
     * 将汉字串转成拼音串
     *
     * @param columnChineseName 汉字字段名
     * @return 字段拼音
     */
    public static String transferPinYin(String columnChineseName) {
        /*转换中文为简体拼音*/
        return LanguageUtil.convertChineseLan2PinYinAbbreviation(columnChineseName, LanguageUtil.CHINESE_CHAR_REG_SIMPLIFIED);
    }
}
LanguageUtil.java
@Slf4j
public class LanguageUtil {

    /**
     * 定义输出格式
     */
    public static HanyuPinyinOutputFormat hpFormat = new HanyuPinyinOutputFormat();
    /**
     * 匹配所有东亚区的语言
     */
    public static String CHINESE_CHAR_REG_SOUTHEAST_ASIA ="^[\u2E80-\u9FFF]+$";
    /**
     * 匹配简体和繁体
     */
    public static String CHINESE_CHAR_REG_SIMPLIFIED_OR_TRADITIONAL ="^[\u4E00-\u9FFF]+$";
    /**
     * 匹配简体
     */
    public static String CHINESE_CHAR_REG_SIMPLIFIED ="[\u4E00-\u9FA5]+$";

    static{
        // 大写格式输出
        hpFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);
        // 不需要语调输出
        hpFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
    }

    /***
     * 将汉字转成拼音(取首字母或全拼)
     * @param singleChar 中文字符
     * @param full 是否全拼
     * @return 转换后拼音
     */
    public static String convertChineseChar2Pinyin(String singleChar, boolean full,String regExp ) {

        StringBuffer sb = new StringBuffer();
        if (singleChar == null || "".equals(singleChar.trim())) {
            return "";
        }
        String pinyin = "";
        for (int i = 0; i < singleChar.length(); i++) {
            char unit = singleChar.charAt(i);
            //是汉字,则转拼音
            if (match(String.valueOf(unit), regExp))
            {
                pinyin = convertSingleChineseChar2Pinyin(unit);
                if (full) {
                    sb.append(pinyin);
                } else {
                    sb.append(pinyin.charAt(0));
                }
            } else {
                sb.append(unit);
            }
        }
        return sb.toString();
    }

    /***
     * 将单个汉字转成拼音
     * @param singleChar 中文汉字
     * @return 拼音
     */
    private static String convertSingleChineseChar2Pinyin(char singleChar) {
        String[] res;
        StringBuffer sb = new StringBuffer();
        try {
            res = PinyinHelper.toHanyuPinyinStringArray(singleChar, hpFormat);
            //对于多音字,只用第一个拼音
            sb.append(res[0]);
        } catch (Exception e) {
            log.error("单个汉字转换成字符失败:{}",e.getMessage());
            return "";
        }
        return sb.toString();
    }

    /***
     * @param str 源字符串
     * @param regex 正则表达式
     * @return 是否匹配
     */
    public static boolean match(String str, String regex) {
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(str);
        return matcher.find();
    }

    /**
     * 汉字字符串的的首拼拼成字符串
     * @param chineseLan 中文字符串
     * @return 拼音字符串
     */
    public static String convertChineseLan2PinYinAbbreviation(String chineseLan,String regExp) {
        String ret = "";
        // 将汉字转换为字符数组
        char[] charChineseLan = chineseLan.toCharArray();
        try {
            for (int i = 0; i < charChineseLan.length; i++) {
                if(String.valueOf(charChineseLan[i]).matches(regExp)) {
                    // 如果字符是中文,则将中文转为汉语拼音(获取全拼则去掉红色的代码即可)
                    ret += PinyinHelper.toHanyuPinyinStringArray(charChineseLan[i], hpFormat)[0].substring(0, 1);
                } else {
                    // 如果字符不是中文,则不转换
                    ret += charChineseLan[i];
                }
            }
        } catch (BadHanyuPinyinOutputFormatCombination e) {
            log.error("获取汉字的的首拼失败:{}",e.getMessage());
        }
        return ret;
    }

    /**
     * 判断字符串中是否包含中文汉字
     *
     * @param content 字符串内容
     * @return true至少包含1个
     */
    public static boolean hasChinese(CharSequence content) {
        if (null == content) {
            return false;
        }
        String regex = "[\u2E80-\u2EFF\u2F00-\u2FDF\u31C0-\u31EF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uD840\uDC00-\uD869\uDEDF\uD869\uDF00-\uD86D\uDF3F\uD86D\uDF40-\uD86E\uDC1F\uD86E\uDC20-\uD873\uDEAF\uD87E\uDC00-\uD87E\uDE1F]+";
        Pattern pattern = Pattern.compile(regex);
        return pattern.matcher(content).find();
    }

    /**
     * 判断字符串是否为中文汉字
     *
     * @param content 字符串内容
     * @return true都是汉字
     */
    public static boolean isChinese(CharSequence content) {
        if (null == content) {
            return false;
        }
        String regex = "[\u2E80-\u2EFF\u2F00-\u2FDF\u31C0-\u31EF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\uD840\uDC00-\uD869\uDEDF\uD869\uDF00-\uD86D\uDF3F\uD86D\uDF40-\uD86E\uDC1F\uD86E\uDC20-\uD873\uDEAF\uD87E\uDC00-\uD87E\uDE1F]+";
        Pattern pattern = Pattern.compile(regex);
        return pattern.matcher(content).matches();
    }
}
JsonUtil.java
public class JsonUtil {

    private final static String arg = "\"";

    /**
     * Map转成JSON字符串
     *
     * @param map
     * @return JSON
     */
    public static String mapToJsonString(Map<String, Object> map) {
        return CollectionUtils.isEmpty(map) ? "" : JSONObject.toJSONString(map).replace(arg,"'");
    }

}

SQL配置与执行:

SqlConfiguration.java
@Component("SqlConfiguration")
@ConfigurationProperties(prefix = "jdbc-config")
@Data
public class SqlConfiguration {
    private String driver;
    private String url;
    private String userName;
    private String password;
}
SqlDataSourceConfiguration.java
@Configuration
@ConditionalOnBean(SqlConfiguration.class)
@Slf4j
public class SqlDataSourceConfiguration {
    @Resource
    private SqlConfiguration sqlConfiguration;

    @Bean("DriverManagerDataSource")
    @Scope(value = "singleton")
    public DriverManagerDataSource getDataSource(){
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setDriverClassName(sqlConfiguration.getDriver());
        driverManagerDataSource.setUrl(sqlConfiguration.getUrl());
        driverManagerDataSource.setUsername(sqlConfiguration.getUserName());
        driverManagerDataSource.setPassword(sqlConfiguration.getPassword());
        log.info("扫描生成自定义配置JDBC数据源:{}",sqlConfiguration.getUrl());
        return driverManagerDataSource;
    }
}
JdbcTransactionManagerConfiguration.java
@Configuration
@ConditionalOnBean({SqlDataSourceConfiguration.class})
@Slf4j
public class JdbcTransactionManagerConfiguration {

    @Resource
    private DriverManagerDataSource driverManagerDataSource;
    @Bean("JdbcTransactionManager")
    @Scope("singleton")
    public JdbcTransactionManager getJdbcTransactionManager(){
        JdbcTransactionManager jdbcTransactionManager = new JdbcTransactionManager();
        log.info("开始配置JDBC事务管理者");
        jdbcTransactionManager.setDataSource(driverManagerDataSource);
        jdbcTransactionManager.setRollbackOnCommitFailure(true);
        jdbcTransactionManager.setFailEarlyOnGlobalRollbackOnly(true);
        jdbcTransactionManager.setGlobalRollbackOnParticipationFailure(true);
        return jdbcTransactionManager;
    }

}
TransactionTemplateConfiguration.java
@Configuration
@Slf4j
public class TransactionTemplateConfiguration {
    @Resource
    private JdbcTransactionManager jdbcTransactionManager;

    @Bean("ReadCommittedTransactionTemplate")
    @Scope("singleton")
    @ConditionalOnBean({JdbcTransactionManager.class})
    @Lazy
    public TransactionTemplate getReadCommittedTransactionTemplate(){
        TransactionTemplate transactionTemplate = new TransactionTemplate();
        log.info("生成事务模板,注入事务管理器,设置事务隔离级别为读已提交");
        transactionTemplate.setTransactionManager(jdbcTransactionManager);
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
        return transactionTemplate;
    }

    @Bean("ReadUnCommittedTransactionTemplate")
    @Scope("singleton")
    @ConditionalOnBean({JdbcTransactionManager.class})
    @Lazy
    public TransactionTemplate getReadUnCommittedTransactionTemplate(){
        TransactionTemplate transactionTemplate = new TransactionTemplate();
        log.info("生成事务模板,注入事务管理器,设置事务隔离级别为读未提交");
        transactionTemplate.setTransactionManager(jdbcTransactionManager);
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
        return transactionTemplate;
    }

    @Bean("RepeatableReadTransactionTemplate")
    @Scope("singleton")
    @ConditionalOnBean({JdbcTransactionManager.class})
    @Lazy
    public TransactionTemplate getRepeatableReadTransactionTemplate(){
        TransactionTemplate transactionTemplate = new TransactionTemplate();
        log.info("生成事务模板,注入事务管理器,设置事务隔离级别为可重复读");
        transactionTemplate.setTransactionManager(jdbcTransactionManager);
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
        return transactionTemplate;
    }

    @Bean("SerializableTransactionTemplate")
    @Scope("singleton")
    @ConditionalOnBean({JdbcTransactionManager.class})
    @Lazy
    public TransactionTemplate getSerializableTransactionTemplate(){
        TransactionTemplate transactionTemplate = new TransactionTemplate();
        log.info("生成事务模板,注入事务管理器,设置事务隔离级别为可串行化");
        transactionTemplate.setTransactionManager(jdbcTransactionManager);
        transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_SERIALIZABLE);
        return transactionTemplate;
    }

}
SqlSpliceProvider.java
public interface SqlSpliceProvider {
    /**
     * 拼接建表SQL
     *
     * @param columnEntityList excel的列属性列表
     * @param tableName        表名
     * @return SQL
     */
    StringBuffer spliceCreateTableSql(List<ColumnEntity> columnEntityList, String tableName);

    /**
     * 拼接删表SQL
     *
     * @param tableName 表名
     * @return SQL
     */
    String dropTableSql(String tableName);

    /**
     * 拼接判断表存在SQL
     *
     * @param tableName 表名
     * @return SQL
     */
    String existsTableSql(String tableName);

    /**
     * 拼接插值SQL列表循环执行即可
     *
     * @param map       值map<行号,对应行的值LIST>
     * @param tableName 表名
     * @return SQL
     */
    List<String> spliceInsertValueSql(Map<Integer, List<ValueEntity>> map, String tableName);

}
SqlSplicer.java
@Service
public class SqlSplicer implements SqlSpliceProvider {

    /**
     * 拼接建表SQL
     *
     * @param columnEntityList 列信息实体列表
     * @param tableName        表格名称
     * @return SQL
     */
    @Override
    public StringBuffer spliceCreateTableSql(List<ColumnEntity> columnEntityList, String tableName) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("CREATE TABLE ").append(SqlSpliceStringUtil.quotesHandle(SqlSpliceStringUtil.transferPinYin(tableName))).append(" ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自动主键',");
        /*扫描属性列表,填充建表SQL*/
        stringBuffer.append(scanColumnListToSql(columnEntityList));
        stringBuffer.append(" PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; ");
        return stringBuffer;
    }

    /**
     * 删除数据表,防止表重复
     *
     * @param tableName 表名称
     * @return SQL
     */
    @Override
    public  String dropTableSql(String tableName) {
        return "DROP TABLE IF EXISTS " + SqlSpliceStringUtil.quotesHandle(SqlSpliceStringUtil.transferPinYin(tableName));
    }

    /**
     * 查询数据表是否存在
     * @param tableName 表名称
     * @return SQL
     */
    @Override
    public  String existsTableSql(String tableName) {
        return "SELECT * FROM information_schema.TABLES WHERE TABLE_NAME = " + SqlSpliceStringUtil.quotesHandle(SqlSpliceStringUtil.transferPinYin(tableName));
    }

    /**
     * 拼接插值SQL(单插入SQL集合)
     *
     * @param map       值实体列表Map
     * @param tableName 表名
     * @return 插值SQL
     */
    @Override
    public  List<String> spliceInsertValueSql(Map<Integer, List<ValueEntity>> map, String tableName) {
        List<String> sqlList = new LinkedList<>();
        map.values().forEach(valueEntityList -> sqlList.add(scanValueListToSql(tableName, valueEntityList)));
        return sqlList;
    }


    /**
     * 拼接单一数据行值SQL
     *
     * @param tableName       表名
     * @param valueEntityList 一行数据值列表
     * @return SQL
     */
    private static String scanValueListToSql(String tableName, List<ValueEntity> valueEntityList) {
        StringBuilder stringBuffer = new StringBuilder();
        stringBuffer.append("INSERT INTO ").append(SqlSpliceStringUtil.quotesHandle(SqlSpliceStringUtil.transferPinYin(tableName)));
        stringBuffer.append(" VALUES( null,");
        for (ValueEntity element : valueEntityList) {
            if ((valueEntityList.indexOf(element) + 1) != valueEntityList.toArray().length) {
                stringBuffer.append(element.getValueOfString()).append(",");
            } else {
                stringBuffer.append(element.getValueOfString());
            }
        }
        stringBuffer.append(");");
        return stringBuffer.toString();
    }

    /**
     * 将列信息实体列表转SQL
     *
     * @param columnEntityList 列信息实体列表
     * @return 处理完成得列属性SQL串
     */
    private static String scanColumnListToSql(List<ColumnEntity> columnEntityList) {
        StringBuilder fieldSql = new StringBuilder();
        for (ColumnEntity element : columnEntityList) {
            fieldSql.append(SqlSpliceStringUtil.quotesHandle(element.getColumnName())).append(element.getColumnSqlInfo());
        }
        return fieldSql.toString();
    }
}
SqlDataProvider.java
public interface SqlDataProvider {
    /**
     * 执行非查询SQL
     *
     * @param sql
     */
    void executeSql(String sql);

    /**
     * 执行查询判断某些存在SQL
     *
     * @param sql
     * @return 是否存在的对象 为空则不存在
     */
    Object executeSqlAndGetReturn(String sql);

    /**
     * 执行查询SQL
     *
     * @param sql
     * @return 结果的Json字符串
     */
    String executeQuerySql(String sql);
}
SqlDataService.java
@Service
@Slf4j
public class SqlDataService implements SqlDataProvider {
    @Resource
    private JdbcTemplate jdbcTemplate;

    @Qualifier("ReadCommittedTransactionTemplate")
    @Resource
    private TransactionTemplate transactionTemplate;


    @Override
    public void executeSql(String sql) {
        log.info("执行非查询操作SQL,开启事务执行:{}", sql);
        transactionTemplate.executeWithoutResult(status -> {
            try {
                jdbcTemplate.execute(sql);
            } catch (Exception e) {
                log.error("事务异常,开启回滚:{}", e.getMessage());
                status.setRollbackOnly();
            }
        });
    }


    @Override
    public Object executeSqlAndGetReturn(String sql) {
        log.info("执行查询SQL:{}", sql);
        return jdbcTemplate.queryForObject(sql, Object.class);
    }


    @Override
    public String executeQuerySql(String sql) {
        log.info("执行查询SQL:{}", sql);
        List<Map<String, Object>> mapList = jdbcTemplate.queryForList(sql);
        if (CollectionUtils.isEmpty(mapList)) {
            return "";
        }
        List<String> result = new LinkedList<>();
        mapList.forEach(map -> result.add(JsonUtil.mapToJsonString(map)));
        return result.toString();
    }
}

具体的讲解就不说了,分享代码案例,感兴趣的同学可以gitee上搜git@gitee.com:huanglinchun/resolve_excel_auto.git

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/957095.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C 语言控制台打印不同颜色字体方法

方法 1&#xff0c;使用 printf 格式化打印&#xff0c;该方法在 visual Studio 中生效&#xff0c;在 Dev C 中未生效 示例代码&#xff1a; #include <stdio.h>#define CLOUR_ON "\033[41;37m" #define CLOUR_OFF "\033[0m"int…

GIT命令只会抄却不理解?看完原理才能事半功倍!

系列文章目录 手把手教你安装Git&#xff0c;萌新迈向专业的必备一步 GIT命令只会抄却不理解&#xff1f;看完原理才能事半功倍&#xff01; 系列文章目录一、Git 的特征1. 文件系统2. 分布式 二、GIT的术语1. 区域术语2. 名词术语1. 提交对象2. 分支3. HEAD4. 标签&#xff0…

Unity UI与粒子 层级问题Camera depth Sorting Layer Order in Layer RenderQueue

Unity游戏开发中&#xff0c;模型、界面、特效等&#xff0c;需要规划好layer的概念&#xff0c;涉及到摄像机&#xff08;Camera&#xff09;、画布&#xff08;Canvas&#xff09;、Shader等相关内容。 在 Unity 中&#xff0c;渲染顺序是由多个因素共同决定的&#xff0c;大…

java八股文面试[多线程]——线程间通信方式

多个线程在并发执行的时候&#xff0c;他们在CPU中是随机切换执行的&#xff0c;这个时候我们想多个线程一起来完成一件任务&#xff0c;这个时候我们就需要线程之间的通信了&#xff0c;多个线程一起来完成一个任务&#xff0c;线程通信一般有4种方式&#xff1a; 通过 volat…

详解IP协议

在介绍IP协议之前&#xff0c;先抛出一个概念&#xff1a;IP地址的作用——定位主机&#xff0c;具有将数据从主机A跨网络传输到主机B的能力&#xff0c;有了TCP提供的策略&#xff0c;例如滑动窗口、拥塞控制等&#xff0c;IP去执行它&#xff0c;所以我们通常叫TCP/IP协议&am…

阿里云centos9stream安装宝塔+vscode(code-server)集成云端开发环境

一、 安装宝塔面板 官网 https://www.bt.cn/new/download.htm 题外话&#xff1a;虽然感觉现在宝塔没以前好用了&#xff0c;而且有centos7、8 mysql编译导致OOM服务器挂掉无法ssh登录的情况&#xff0c;但他还是远程管理服务器的好选择&#xff0c;提示宝塔只支持最新的centos…

基于广义正态分布算法优化的BP神经网络(预测应用) - 附代码

基于广义正态分布算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码 文章目录 基于广义正态分布算法优化的BP神经网络&#xff08;预测应用&#xff09; - 附代码1.数据介绍2.广义正态分布优化BP神经网络2.1 BP神经网络参数设置2.2 广义正态分布算法应用 4.测试结…

[管理与领导-60]:IT基层管理者 - 扩展技能 - 3 - 通过面试招到合适的人选

目录 前言&#xff1a; 一、招聘 1.1 什么是招聘 1.2 招聘 VS 招募 1.3 甄选 1.4 招聘中的重要原则 1.5 招聘的本质 1.6 人才匹配的维度 1.7 人员招聘中的误区 二、面试 2.1 何为面试 2.2 为什么面试 2.3 面试的注意事项 2.4 面试的误区 2.5 如何进行面试 前言…

Vulnstack----5、ATTCK红队评估实战靶场五

文章目录 一 环境搭建二 外网渗透三 内网信息收集3.1 本机信息收集3.2 域内信息收集 四 横向移动4.1 路由转发和代理通道4.2 抓取域用户密码4.3 使用Psexec登录域控4.4 3389远程登录 五、痕迹清理 一 环境搭建 1、项目地址 http://vulnstack.qiyuanxuetang.net/vuln/detail/7/ …

交互设计都有哪些准则?

UI交互设计的本质不是完全基于用户的需求&#xff0c;而是交互设计师需要学习根据用户描述的产品形式来了解用户需要什么。 在交互设计过程中&#xff0c;遵循科学交互设计的本质是整个交互设计过程的重要组成部分&#xff0c;这与产品使用过程中给用户带来的体验密切相关。本…

matlab函数 状态空间系统ss、能控性矩阵ctrb、矩阵的秩rank、能控标准型canon、零极点配置place、系统极点pole等函数(线性定常系统)

matlab函数 能控性矩阵ctrb、能控标准型canon、零极点配置place 第一章&#xff0c;线性定常系统 ss 如果已知线性定常系统的ABCD四个矩阵&#xff0c;可以得到状态空间系统 其他更具体的用法请直接看帮助文档。 用法&#xff1a;ss(A,B,C,D) 假如 可以输入 A [-1.5,-2…

Java连接websocket优雅断线、重连功能

为了实现优雅重连和重试&#xff0c;您需要在代码中添加一些逻辑来处理连接失败或断开连接的情况。 实现代码如下&#xff1a; import javax.websocket.*; import java.io.IOException;ClientEndpoint public class WebSocketClientEndpoint {private Session userSession n…

【区块链 | IPFS】浅谈 | IPFS数据存储原理

IPFS在数据存储方面采用的是分散式的文件存储,区别于HTTP协议的位置寻址,IPFS是基于内容寻址,当文件上传到IPFS节点存储时,节点会对文件进行Merkle DAG(默克尔有向无环图)的格式组织分块存储,在存储完毕后,文件将以Merkle DAG的根哈希数来表示该文件,用户可以从IPFS构…

【JAVA基础——JAVA虚拟机JVM】

JVM 文章目录 JVM一.JVM结构1.1.JVM包含两个子系统和两个组件1.2.运行时数据区1.2.1.简介1.2.2.程序计数器1.2.3.虚拟机栈1.2.4.堆1.2.5.本地方法栈1.2.6.方法区(永久代实现)java8-1.2.7.元空间(Metaspace)1.2.8.JVM字节码执行引擎1.2.9.直接内存(Direct Memory)1.2.10.垃圾收集…

three.js(三):three.js的渲染结构

three.js 的渲染结构 概述 three.js 封装了场景、灯光、阴影、材质、纹理和三维算法&#xff0c;不必在直接用WebGL 开发项目&#xff0c;但有的时候会间接用到WebGL&#xff0c;比如自定义着色器。three.js 在渲染三维场景时&#xff0c;需要创建很多对象&#xff0c;并将它…

C语言柔性数组详解:让你的程序更灵活

柔性数组 一、前言二、柔性数组的用法三、柔性数组的内存分布四、柔性数组的优势五、总结 一、前言 仔细观察下面的代码&#xff0c;有没有看出哪里不对劲&#xff1f; struct S {int i;double d;char c;int arr[]; };还有另外一种写法&#xff1a; struct S {int i;double …

Sublime Text汉化,主打简单明了

在Sublime中设置中文的步骤如下&#xff1a; 1.打开Sublime Text&#xff0c;使用快捷键ShiftCtrlP&#xff08;MacOS下cmdShiftP&#xff09;&#xff0c;弹出查找栏。 2.在搜索框中输入关键字"install"&#xff0c;出现下拉选项&#xff0c;点击选择其中的"P…

Dubbo 应用切换 ZooKeeper 注册中心实例,流量无损迁移

首先思考一个问题&#xff1a;如果 Dubbo 应用使用 ZooKeeper 作为注册中心&#xff0c;现在需要切换到新的 ZooKeeper 实例&#xff0c;如何做到流量无损&#xff1f; 本文提供解决这个问题的一种方案。 场景 有两个基于 Dubbo 的微服务应用&#xff0c;一个是服务提供者&…

北京收录2023开学了《乡村振兴战略下传统村落文化旅游设计》中建博后许少辉八一新书

北京收录2023开学了《乡村振兴战略下传统村落文化旅游设计》中建博后许少辉八一新书

【业务功能篇92】微服务-springcloud-多线程-异步处理-异步编排-CompletableFutrue

三、CompletableFutrue 一个商品详情页 展示SKU的基本信息 0.5s展示SKU的图片信息 0.6s展示SKU的销售信息 1sspu的销售属性 1s展示规格参数 1.5sspu详情信息 1s 1.ComplatableFuture介绍 Future是Java 5添加的类&#xff0c;用来描述一个异步计算的结果。你可以使用 isDone方…