• 数据备份文件生成--根据表名生成对应的sql语句文件


    最近客户有个需求,希望在后台增加手动备份功能,将数据导出下载保存。

    当然,此方法不适用于海量数据的备份,这只适用于少量数据的sql备份。

    这是我生成的sql文件,以及sql文件里的insert语句,已亲测,可以直接执行:

    项目是SSM框架,接下来就展示我的实现代码:

    首先是接受字段的实体类:

    1. @Data
    2. public class ColumnsDto {
    3. /**
    4. * 表结构的主要字段*
    5. */
    6. private String column_name;
    7. //该字段则是表字段的数据类型 暂时不需要
    8. private String data_type;
    9. }

    然后是用的到两个主要的sql:

    1.此sql用于查询表的有效字段信息(table_schema:当前的数据库名

    1. <select id="queryColumnsByTableName" resultType="com.hle.monitor.entity.vo.ColumnsDto">
    2. SELECT
    3. column_name,
    4. data_type
    5. FROM
    6. information_schema.COLUMNS
    7. WHERE
    8. table_name = #{tableName}
    9. AND table_schema = 'supervision_data'
    10. ORDER BY ordinal_position
    11. </select>

    2.再用sql查询表的所有数据:(注意:此处表名需要要用$而不是#号)

    1. <select id="findBackupAll" resultType="java.util.Map">
    2. select * from ${tableName}
    3. </select>

    此处我省略了相应的service和mapper文件内容,直接展示最重要的controller代码:

    1. import com.hle.monitor.entity.Results;
    2. import com.hle.monitor.entity.vo.ColumnsDto;
    3. import com.hle.monitor.service.UserService;
    4. import com.hle.monitor.util.DateUtils;
    5. import com.hle.monitor.util.MinIoUtil;
    6. import com.hle.monitor.util.ParameterUtil;
    7. import io.swagger.annotations.Api;
    8. import io.swagger.annotations.ApiOperation;
    9. import org.apache.http.entity.ContentType;
    10. import org.springframework.beans.factory.annotation.Autowired;
    11. import org.springframework.context.annotation.Scope;
    12. import org.springframework.mock.web.MockMultipartFile;
    13. import org.springframework.web.bind.annotation.*;
    14. import org.springframework.web.multipart.MultipartFile;
    15. import javax.annotation.Resource;
    16. import java.io.*;
    17. import java.lang.reflect.Field;
    18. import java.util.Arrays;
    19. import java.util.Date;
    20. import java.util.List;
    21. import java.util.Map;
    22. /**
    23. * @author
    24. */
    25. @CrossOrigin(origins = "*")
    26. @RestController()
    27. @RequestMapping("/backup")
    28. @Api(tags = "备份相关接口类")
    29. @Scope("prototype")
    30. public class BackupController {
    31. @Autowired
    32. private UserService userService;
    33. @Resource
    34. MinIoUtil minIoUtil;
    35. //本地文件夹路径
    36. private static String backupFilePath = "./sql/";
    37. @ApiOperation(value = "根据表名备份信息")
    38. @GetMapping("/{tableName}")
    39. public Results backupTable(@PathVariable("tableName") String tableName) {
    40. //表有效字段信息
    41. List<ColumnsDto> columnsDtoList = userService.queryColumnsByTableName(tableName);
    42. if(columnsDtoList.isEmpty()) return new Results(500, "该表无有效字段信息!");
    43. //文件名--先在本地创建写入后再进行删除
    44. String fileName = System.currentTimeMillis() + "-" + tableName + ".sql";
    45. try {
    46. //获取所有备份数据
    47. List<Map<String, Object>> records = userService.findBackupAll(tableName);
    48. String path = backupFilePath + fileName;
    49. //不存在文件夹则创建
    50. File directory = new File(backupFilePath);
    51. directory.mkdirs();
    52. BufferedWriter writer = new BufferedWriter(new FileWriter(path));
    53. for (Map record : records) {
    54. String insertStatement = generateInsertStatement(tableName, record, columnsDtoList) + ";";
    55. writer.write(insertStatement);
    56. writer.newLine();
    57. }
    58. writer.close();
    59. File file = new File(path);
    60. InputStream inputStream = new FileInputStream(file);
    61. MultipartFile multipartFile = new MockMultipartFile(ContentType.APPLICATION_OCTET_STREAM.toString(), inputStream);
    62. minIoUtil.upload(multipartFile, ParameterUtil.bucketNameParam, fileName);
    63. //上传后 删除本地文件
    64. file.delete();
    65. System.out.println("Backup created successfully!");
    66. } catch (IOException e) {
    67. e.printStackTrace();
    68. return new Results(500, "操作失败!");
    69. }
    70. return new Results(200, "操作成功!", fileName);
    71. }
    72. //此方法需要以数据库取出来的字段信息为准--转换成sql的方法
    73. private String generateInsertStatement(String tableName, Map<String, Object> record, List<ColumnsDto> columnsDtoList) {
    74. StringBuilder builder = new StringBuilder();
    75. builder.append("INSERT INTO ").append(tableName).append(" (");
    76. //拼接列名
    77. columnsDtoList.forEach(columns -> {
    78. builder.append(columns.getColumn_name()).append(", ");
    79. });
    80. builder.setLength(builder.length() - 2);
    81. builder.append(") VALUES (");
    82. //拼接值
    83. columnsDtoList.forEach(columns -> {
    84. Object value = record.get(columns.getColumn_name());
    85. if(value instanceof Date){
    86. Date date = (Date) value;
    87. builder.append("'").append(DateUtils.parseDateToStr(date)).append("', ");
    88. }
    89. else if(value instanceof String){
    90. builder.append("'").append(value).append("', ");
    91. }else{
    92. builder.append(value).append(", ");
    93. }
    94. });
    95. builder.setLength(builder.length() - 2);
    96. builder.append(")");
    97. return builder.toString();
    98. }
    99. //此方法用于直接取Object的字段信息做sql拼接--转换成sql的方法--已验证过
    100. private String generateInsertStatement(String tableName, Object record) {
    101. StringBuilder builder = new StringBuilder();
    102. builder.append("INSERT INTO ").append(tableName).append(" (");
    103. Arrays.stream(record.getClass().getDeclaredFields())
    104. .map(Field::getName)
    105. .forEach(fieldName -> builder.append(convertCamelCaseToSnakeCase(fieldName)).append(", "));
    106. builder.setLength(builder.length() - 2);
    107. builder.append(") VALUES (");
    108. Arrays.stream(record.getClass().getDeclaredFields())
    109. .map(field -> {
    110. field.setAccessible(true);
    111. try {
    112. return field.get(record);
    113. } catch (IllegalAccessException e) {
    114. e.printStackTrace();
    115. return null;
    116. }
    117. })
    118. .forEach(value -> {
    119. if(value instanceof Date){
    120. //时间格式 转换为-YYYY_MM_DD_HH_MM_SS
    121. Date date = (Date) value;
    122. builder.append("'").append(DateUtils.parseDateToStr(date)).append("', ");
    123. }
    124. else if(value instanceof String){
    125. builder.append("'").append(value).append("', ");
    126. }else{
    127. //int/double类型 不需要单引号
    128. builder.append(value).append(", ");
    129. }
    130. });
    131. builder.setLength(builder.length() - 2);
    132. builder.append(")");
    133. return builder.toString();
    134. }
    135. //驼峰命令转换 userName-转换为-user_name
    136. public static String convertCamelCaseToSnakeCase(String input) {
    137. return input.replaceAll("([a-z])([A-Z]+)", "$1_$2").toLowerCase();
    138. }
    139. }

    在controller中提供了两种方法,可以参考一下~,然后直接调用接口就行:

    因为项目需要,我是写入文件后再上传至minio文件服务器,所以我要查看还需要去minio服务器查看下载,或者调用现有的下载接口下载~

    这样就完成了根据sql文件数据备份~

  • 相关阅读:
    5.5-6.2读书笔记
    DAY51
    java基于springboot+Vue社区居民医疗健康网站
    VC++删除文件夹
    API 文档搜索引擎
    4、乐趣国学—“满招损,谦受益。”
    线程安全(上)
    网络传输方式
    【蓝桥杯冲击国赛计划第4天】栈
    hash模式和history模式
  • 原文地址:https://blog.csdn.net/qq_36090537/article/details/133029246