数据库备份基础框架构建
This commit is contained in:
@@ -0,0 +1,93 @@
|
|||||||
|
package com.zyplayer.doc.data.repository.manage.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份记录信息
|
||||||
|
*
|
||||||
|
* @author diantu
|
||||||
|
* @since 2023年3月3日
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@TableName("backup_log")
|
||||||
|
public class BackupLog implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 数据源id
|
||||||
|
*/
|
||||||
|
private Long dbId;
|
||||||
|
/**
|
||||||
|
* 来源(手动备份|自动备份)
|
||||||
|
*/
|
||||||
|
private String category;
|
||||||
|
/**
|
||||||
|
* 库名
|
||||||
|
*/
|
||||||
|
private String databaseName;
|
||||||
|
/**
|
||||||
|
* 表名
|
||||||
|
*/
|
||||||
|
private String tablesName;
|
||||||
|
/**
|
||||||
|
* 备份方式
|
||||||
|
*/
|
||||||
|
private Integer dataType;
|
||||||
|
/**
|
||||||
|
* 是否压缩
|
||||||
|
*/
|
||||||
|
private Boolean isCompress;
|
||||||
|
/**
|
||||||
|
* 是否上传
|
||||||
|
*/
|
||||||
|
private Boolean isUpload;
|
||||||
|
/**
|
||||||
|
* 是否删除
|
||||||
|
*/
|
||||||
|
private Boolean isDelete;
|
||||||
|
/**
|
||||||
|
* 文件路径
|
||||||
|
*/
|
||||||
|
private String filePath;
|
||||||
|
/**
|
||||||
|
* 文件大小
|
||||||
|
*/
|
||||||
|
private Long fileSize;
|
||||||
|
/**
|
||||||
|
* 备份状态(-1-失败0-备份中1-上传中200-成功)
|
||||||
|
*/
|
||||||
|
private Integer status;
|
||||||
|
/**
|
||||||
|
* 备份信息
|
||||||
|
*/
|
||||||
|
private String msg;
|
||||||
|
/**
|
||||||
|
* 开始时间
|
||||||
|
*/
|
||||||
|
private Date startTime;
|
||||||
|
/**
|
||||||
|
* 结束时间
|
||||||
|
*/
|
||||||
|
private Date endTime;
|
||||||
|
/**
|
||||||
|
* 耗时(ms)
|
||||||
|
*/
|
||||||
|
private Long spendTime;
|
||||||
|
/**
|
||||||
|
* 删除状态(0--未删除1--已删除)
|
||||||
|
*/
|
||||||
|
private Integer delFlag;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package com.zyplayer.doc.data.repository.manage.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份任务信息
|
||||||
|
*
|
||||||
|
* @author diantu
|
||||||
|
* @since 2023年3月3日
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@TableName(value = "backup_task", autoResultMap = true)
|
||||||
|
public class BackupTask implements Serializable {
|
||||||
|
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
/**
|
||||||
|
* 数据源id
|
||||||
|
*/
|
||||||
|
private Long dbId;
|
||||||
|
/**
|
||||||
|
* cron表达式
|
||||||
|
*/
|
||||||
|
private String cron;
|
||||||
|
/**
|
||||||
|
* 参数
|
||||||
|
*/
|
||||||
|
private String param;
|
||||||
|
/**
|
||||||
|
* 状态(0-停止1-启动)
|
||||||
|
*/
|
||||||
|
private Boolean status;
|
||||||
|
/**
|
||||||
|
* 库名
|
||||||
|
*/
|
||||||
|
private String databaseName;
|
||||||
|
/**
|
||||||
|
* 表名
|
||||||
|
*/
|
||||||
|
private String tablesName;
|
||||||
|
/**
|
||||||
|
* 备份方式
|
||||||
|
*/
|
||||||
|
private Integer dataType;
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
private Date createTime;
|
||||||
|
/**
|
||||||
|
* 删除状态(0--未删除1--已删除)
|
||||||
|
*/
|
||||||
|
private Integer delFlag;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.zyplayer.doc.data.utils;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建一个线程类来不停地来读出Process调用脚本的输出数据,防止缓冲区被缓冲数据塞满而线程阻塞
|
||||||
|
* @author diantu
|
||||||
|
* @since 2023年3月3日
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class CleanInputCache extends Thread{
|
||||||
|
|
||||||
|
private final String type;
|
||||||
|
private final String charSet;
|
||||||
|
private final StringBuilder msg;
|
||||||
|
private final InputStream inputStream;
|
||||||
|
|
||||||
|
public CleanInputCache(InputStream inputStream, String type, String charSet, StringBuilder msg) {
|
||||||
|
this.type = type;
|
||||||
|
this.msg = msg;
|
||||||
|
this.charSet = charSet;
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, charSet));
|
||||||
|
String line = null;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if ("error".equals(type)) {
|
||||||
|
msg.append(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,7 +51,12 @@
|
|||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
<dependency>
|
||||||
|
<groupId>org.quartz-scheduler</groupId>
|
||||||
|
<artifactId>quartz</artifactId>
|
||||||
|
<version>2.3.2</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.zyplayer.doc.db.framework.db.enums;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份类型枚举类
|
||||||
|
*
|
||||||
|
* @author diantu
|
||||||
|
* @since 2023年3月8日
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
public enum BackupCategoryEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动备份
|
||||||
|
*/
|
||||||
|
MANUAL("0", "手动备份"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动备份
|
||||||
|
*/
|
||||||
|
AUTO("1", "自动备份");
|
||||||
|
/**
|
||||||
|
* 编码
|
||||||
|
*/
|
||||||
|
private final String code;
|
||||||
|
/**
|
||||||
|
* 信息
|
||||||
|
*/
|
||||||
|
private final String msg;
|
||||||
|
|
||||||
|
public String getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMsg() {
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.zyplayer.doc.db.framework.db.job;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.zyplayer.doc.data.repository.manage.entity.BackupLog;
|
||||||
|
import com.zyplayer.doc.db.framework.db.vo.BackupJobVO;
|
||||||
|
import com.zyplayer.doc.db.framework.utils.DatabaseBackupUtils;
|
||||||
|
import com.zyplayer.doc.db.framework.utils.QuartzManagerUtils;
|
||||||
|
import org.quartz.Job;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.quartz.JobExecutionException;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据库备份定时任务执行
|
||||||
|
*
|
||||||
|
* @author diantu
|
||||||
|
* @since 2023年2月8日
|
||||||
|
*/
|
||||||
|
public class BackupJob implements Job {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DatabaseBackupUtils databaseBackupUtils;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(JobExecutionContext context) throws JobExecutionException {
|
||||||
|
// 解析参数
|
||||||
|
BackupJobVO jobVO = JSONObject.parseObject(context.getJobDetail().getJobDataMap().getString(QuartzManagerUtils.PARAM_KEY), BackupJobVO.class);
|
||||||
|
// TODO 保存备份记录
|
||||||
|
BackupLog backupLog = new BackupLog();
|
||||||
|
//开始备份
|
||||||
|
databaseBackupUtils.saveBackUp(jobVO, backupLog);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package com.zyplayer.doc.db.framework.db.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份任务数据
|
||||||
|
*
|
||||||
|
* @author diantu
|
||||||
|
* @since 2023年3月3日
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class BackupJobVO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据源ID
|
||||||
|
*/
|
||||||
|
private Integer dbId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地址
|
||||||
|
*/
|
||||||
|
private String host;
|
||||||
|
/**
|
||||||
|
* 端口号
|
||||||
|
*/
|
||||||
|
private String port;
|
||||||
|
/**
|
||||||
|
* 用户名
|
||||||
|
*/
|
||||||
|
private String username;
|
||||||
|
/**
|
||||||
|
* 密码
|
||||||
|
*/
|
||||||
|
private String password;
|
||||||
|
/**
|
||||||
|
* 数据库名
|
||||||
|
*/
|
||||||
|
private String databaseName;
|
||||||
|
/**
|
||||||
|
* 数据库表名
|
||||||
|
*/
|
||||||
|
private String[] tables;
|
||||||
|
/**
|
||||||
|
* 备份形式
|
||||||
|
* 0-只备份表结构
|
||||||
|
* 1-只备份表数据
|
||||||
|
* 2-备份表结构+表数据
|
||||||
|
*/
|
||||||
|
private Integer dataType;
|
||||||
|
/**
|
||||||
|
* 是否压缩
|
||||||
|
*/
|
||||||
|
private Boolean isCompress = false;
|
||||||
|
/**
|
||||||
|
* 是否上传
|
||||||
|
*/
|
||||||
|
private Boolean isUpload = false;
|
||||||
|
/**
|
||||||
|
* 是否删除
|
||||||
|
*/
|
||||||
|
private Boolean isDelete = false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.zyplayer.doc.db.framework.db.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备份响应数据
|
||||||
|
*
|
||||||
|
* @author diantu
|
||||||
|
* @since 2023年3月3日
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class BackupRespVO {
|
||||||
|
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
private File file;
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return null != this.file && 0 < this.file.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,14 @@
|
|||||||
package com.zyplayer.doc.db.framework.utils;
|
package com.zyplayer.doc.db.framework.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import com.alibaba.excel.util.DateUtils;
|
||||||
|
import com.zyplayer.doc.data.repository.manage.entity.BackupLog;
|
||||||
|
import com.zyplayer.doc.data.utils.CleanInputCache;
|
||||||
|
import com.zyplayer.doc.db.framework.db.vo.BackupJobVO;
|
||||||
|
import com.zyplayer.doc.db.framework.db.vo.BackupRespVO;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.time.LocalDate;
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 数据库备份恢复工具类
|
* 数据库备份恢复工具类
|
||||||
@@ -12,113 +19,211 @@ import java.time.LocalDate;
|
|||||||
public class DatabaseBackupUtils {
|
public class DatabaseBackupUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MySQL数据库导出
|
* 项目路径
|
||||||
*
|
|
||||||
* @param hostIP MySQL数据库所在服务器地址IP
|
|
||||||
* @param userName 进入数据库所需要的用户名
|
|
||||||
* @param password 进入数据库所需要的密码
|
|
||||||
* @param savePath 数据库导出文件保存路径
|
|
||||||
* @param fileName 数据库导出文件文件名
|
|
||||||
* @param databaseName 要导出的数据库名
|
|
||||||
* @return 返回true表示导出成功,否则返回false。
|
|
||||||
*/
|
*/
|
||||||
public static boolean exportDatabaseForMysql(String mysqldumpPath, String hostIP, String userName, String password, String savePath, String fileName, String databaseName) throws InterruptedException {
|
public static final String PROJECT_PATH = System.getProperty("user.dir");
|
||||||
File saveFile = new File(savePath);
|
|
||||||
if (!saveFile.exists()) {// 如果目录不存在
|
|
||||||
saveFile.mkdirs();// 创建文件夹
|
|
||||||
}
|
|
||||||
if(!savePath.endsWith(File.separator)){
|
|
||||||
savePath = savePath + File.separator;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrintWriter printWriter = null;
|
/**
|
||||||
BufferedReader bufferedReader = null;
|
* 当前系统类型
|
||||||
try {
|
*/
|
||||||
|
public static final String OS_NAME = System.getProperty("os.name");
|
||||||
|
|
||||||
printWriter = new PrintWriter(new OutputStreamWriter(new FileOutputStream(savePath + fileName), "utf8"));
|
/**
|
||||||
Process process = Runtime.getRuntime().exec(" "+mysqldumpPath+"/mysqldump -h" + hostIP + " -u" + userName + " -p" + password + " " + databaseName);
|
* 编码格式
|
||||||
InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream(), "utf8");
|
*/
|
||||||
bufferedReader = new BufferedReader(inputStreamReader);
|
public static final String CHAR_SET = OS_NAME.startsWith("Win") ? "GBK" : "UTF-8";
|
||||||
String line;
|
|
||||||
while((line = bufferedReader.readLine())!= null){
|
/**
|
||||||
printWriter.println(line);
|
* 异步执行备份任务,更新备份记录
|
||||||
}
|
*
|
||||||
printWriter.flush();
|
* @param jobVO 备份参数
|
||||||
if(process.waitFor() == 0){//0 表示线程正常终止。
|
* @param backupLog 备份记录
|
||||||
return true;
|
*/
|
||||||
}
|
@Async
|
||||||
}catch (IOException e) {
|
public void saveBackUp(BackupJobVO jobVO, BackupLog backupLog) {
|
||||||
e.printStackTrace();
|
// 执行备份
|
||||||
} finally {
|
BackupRespVO respVO = doBackup(jobVO);
|
||||||
try {
|
backupLog.setStatus(respVO.isSuccess() ? (jobVO.getIsUpload() ? 1 : 2) : -1);
|
||||||
if (bufferedReader != null) {
|
backupLog.setMsg(respVO.getMsg());
|
||||||
bufferedReader.close();
|
// 备份成功
|
||||||
}
|
if (respVO.isSuccess()) {
|
||||||
if (printWriter != null) {
|
// 文件相对路径
|
||||||
printWriter.close();
|
backupLog.setFilePath(respVO.getFile().getPath().replace(PROJECT_PATH + File.separator, ""));
|
||||||
}
|
backupLog.setFileSize(respVO.getFile().length());
|
||||||
} catch (IOException e) {
|
}
|
||||||
e.printStackTrace();
|
// 备份失败
|
||||||
|
if (!respVO.isSuccess()) {
|
||||||
|
if (null != respVO.getFile()) {
|
||||||
|
respVO.getFile().delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
// 计算耗时
|
||||||
|
backupLog.setEndTime(new Date());
|
||||||
|
backupLog.setSpendTime(backupLog.getEndTime().getTime() - backupLog.getStartTime().getTime());
|
||||||
|
//@TODO 存入备份记录信息
|
||||||
|
// 备份文件上传至文件服务器
|
||||||
|
if (!jobVO.getIsUpload()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//@TODO 备份文件上传至文件服务器
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void backup(String savePath) {
|
/**
|
||||||
File file = new File(savePath);
|
* 执行备份命令(mysql)
|
||||||
if (!file.exists()) {
|
*/
|
||||||
file.mkdirs();
|
public static BackupRespVO doBackup(BackupJobVO jobVO) {
|
||||||
}
|
// 返回对象
|
||||||
String fileName = savePath + "/" + LocalDate.now() + ".sql";
|
BackupRespVO respVO = new BackupRespVO();
|
||||||
/** 默认使用linux*/
|
|
||||||
//String cmdPrefix = "/bin/sh -c ";
|
|
||||||
String c1 = "/bin/sh";
|
|
||||||
String c2 = "-c";
|
|
||||||
String os_name = System.getProperty("os.name");
|
|
||||||
// 判断是否是windows系统
|
|
||||||
if (os_name.toLowerCase().startsWith("win")){
|
|
||||||
//cmdPrefix = "cmd /c ";
|
|
||||||
c1 = "cmd";
|
|
||||||
c2 = "/c";
|
|
||||||
}
|
|
||||||
//参考示例:# /usr/local/mysql/bin/mysqldump -uroot -p123456 -P3306 shuju > shuju.sql
|
|
||||||
String cmd = "mysqldump" // mysqldump的绝对路径,配置环境变量,直接写mysqldump即可
|
|
||||||
+ " -h" + "127.0.0.1" // 数据库端口号
|
|
||||||
+ " -P" + "3306" // 数据库端口号
|
|
||||||
+ " -u" + "root" // 数据库用户名
|
|
||||||
+ " -p" + "root" // 数据库密码
|
|
||||||
+ " " + "zyplayer_doc_manage" // 数据库名
|
|
||||||
+ " > " + fileName; // 最终写入的文件路径
|
|
||||||
try {
|
try {
|
||||||
System.out.println("第一个参数 " + c1);
|
// 当前年月日yyyyMMdd
|
||||||
System.out.println("第二个参数 " + c2);
|
String date = DateUtils.format(new Date(),"yyyyMMdd");
|
||||||
System.out.println("具体命令 " + cmd);
|
// 文件目录
|
||||||
|
String path = PROJECT_PATH + File.separator + "static" + File.separator + date + File.separator;
|
||||||
|
// 文件名
|
||||||
|
String fileName = IdUtil.fastSimpleUUID() + ".sql" + (jobVO.getIsCompress() ? ".gz" : "");
|
||||||
|
// 创建文件
|
||||||
|
File file = new File(path, fileName);
|
||||||
|
// 路径不存在,则新建
|
||||||
|
if (!file.getParentFile().exists()) {
|
||||||
|
boolean flag = file.getParentFile().mkdirs();
|
||||||
|
if (!flag) {
|
||||||
|
respVO.setMsg("文件夹创建失败");
|
||||||
|
return respVO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
respVO.setFile(file);
|
||||||
|
// shell 命令脚本
|
||||||
|
String[] commands = createBackupCommandForMysql(jobVO, path, fileName);
|
||||||
|
|
||||||
//log.error("数据库备份START" + LocalDateTime.now());
|
ProcessBuilder processBuilder = new ProcessBuilder();
|
||||||
/**
|
Process process = processBuilder.command(commands).start();
|
||||||
* exec重载方法有一个参数的,window下执行正常,linux下无法完成备份。
|
//Process process = Runtime.getRuntime().exec(commands);
|
||||||
* 使用多参数重载方法都可以正常备份
|
// 创建一个线程类来不停地来读出Process调用脚本的输出数据,防止缓冲区被缓冲数据塞满而线程阻塞
|
||||||
*/
|
new CleanInputCache(process.getInputStream(), "info", CHAR_SET, null).start();
|
||||||
Process process = Runtime.getRuntime().exec(new String[]{c1, c2, cmd});
|
// 错误信息
|
||||||
process.waitFor();
|
StringBuilder msg = new StringBuilder("【" + jobVO.getDatabaseName() + "】备份失败,原因:");
|
||||||
//log.error("数据库备份END" + LocalDateTime.now());
|
new CleanInputCache(process.getErrorStream(), "error", CHAR_SET, msg).start();
|
||||||
|
// 备份成功
|
||||||
|
if (process.waitFor() == 0) {
|
||||||
|
respVO.setMsg("备份成功");
|
||||||
|
}
|
||||||
|
// 备份失败
|
||||||
|
else {
|
||||||
|
respVO.setMsg(msg.toString());
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
respVO.setMsg("【" + jobVO.getDatabaseName() + "】备份失败,原因:" + e.getMessage());
|
||||||
//log.error("数据库备份失败:{}", e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
return respVO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建命令头(只支持Windows平台和Linux平台)
|
||||||
|
*/
|
||||||
|
public static String[] createBaseCommand() {
|
||||||
|
// shell 命令
|
||||||
|
String[] commands = new String[3];
|
||||||
|
if (OS_NAME.startsWith("Win")) {
|
||||||
|
commands[0] = "cmd.exe";
|
||||||
|
commands[1] = "/c";
|
||||||
|
} else {
|
||||||
|
commands[0] = "/bin/sh";
|
||||||
|
commands[1] = "-c";
|
||||||
|
}
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args){
|
/**
|
||||||
try {
|
* 拼接备份命令(mysql)
|
||||||
if (exportDatabaseForMysql("C:/Program Files/MySQL/MySQL Server 5.7/bin","127.0.0.1", "root", "root", "D:/backupDatabase", "2023-2-8.sql", "zyplayer_doc_manage")) {
|
*
|
||||||
System.out.println("数据库成功备份!!!");
|
* @param jobVO 备份参数
|
||||||
} else {
|
* @param path 备份文件目录
|
||||||
System.out.println("数据库备份失败!!!");
|
* @param fileName 备份文件名
|
||||||
|
*/
|
||||||
|
public static String[] createBackupCommandForMysql(BackupJobVO jobVO, String path, String fileName) {
|
||||||
|
String[] commands = createBaseCommand();
|
||||||
|
// 拼接命令
|
||||||
|
StringBuilder mysqldump = new StringBuilder();
|
||||||
|
//需配置mysql安装路径bin目录环境变量
|
||||||
|
mysqldump.append("mysqldump");
|
||||||
|
mysqldump.append(" --opt");
|
||||||
|
|
||||||
|
// 用户,密码
|
||||||
|
mysqldump.append(" --user=").append(jobVO.getUsername());
|
||||||
|
mysqldump.append(" --password=\"").append(jobVO.getPassword()).append("\"");
|
||||||
|
|
||||||
|
// ip,端口
|
||||||
|
mysqldump.append(" --host=").append(jobVO.getHost());
|
||||||
|
mysqldump.append(" --port=").append(jobVO.getPort());
|
||||||
|
|
||||||
|
// 使用的连接协议,包括:tcp, socket, pipe, memory
|
||||||
|
mysqldump.append(" --protocol=tcp");
|
||||||
|
|
||||||
|
// 设置默认字符集,默认值为utf8
|
||||||
|
mysqldump.append(" --default-character-set=utf8");
|
||||||
|
// 在导出数据之前提交一个BEGIN SQL语句,BEGIN 不会阻塞任何应用程序且能保证导出时数据库的一致性状态
|
||||||
|
mysqldump.append(" --single-transaction=TRUE");
|
||||||
|
|
||||||
|
// 导出存储过程以及自定义函数
|
||||||
|
mysqldump.append(" --routines");
|
||||||
|
// 导出事件
|
||||||
|
mysqldump.append(" --events");
|
||||||
|
|
||||||
|
// 只备份表结构
|
||||||
|
if (null != jobVO.getDataType()) {
|
||||||
|
if (0 == jobVO.getDataType()) {
|
||||||
|
mysqldump.append(" --no-data");
|
||||||
|
}
|
||||||
|
// 只备份表数据
|
||||||
|
else if (1 == jobVO.getDataType()) {
|
||||||
|
mysqldump.append(" --no-create-info");
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
//backup("D:/backupDatabase");
|
|
||||||
|
// 数据库名
|
||||||
|
mysqldump.append(" ").append(jobVO.getDatabaseName());
|
||||||
|
|
||||||
|
// 数据表名
|
||||||
|
if (null != jobVO.getTables() && 0 < jobVO.getTables().length) {
|
||||||
|
for (String item : jobVO.getTables()) {
|
||||||
|
mysqldump.append(" ").append(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存文件路径
|
||||||
|
if (jobVO.getIsCompress()) {
|
||||||
|
// gzip压缩
|
||||||
|
mysqldump.append(" | gzip");
|
||||||
|
}
|
||||||
|
mysqldump.append(" > ").append(path).append(fileName);
|
||||||
|
|
||||||
|
commands[2] = mysqldump.toString();
|
||||||
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接备份命令(oracle)
|
||||||
|
* @param jobVO 备份参数
|
||||||
|
* @param path 备份文件目录
|
||||||
|
* @param fileName 备份文件名
|
||||||
|
*/
|
||||||
|
public static String[] createBackupCommandForOracle(BackupJobVO jobVO, String path, String fileName) {
|
||||||
|
|
||||||
|
String[] commands = createBaseCommand();
|
||||||
|
// 拼接命令
|
||||||
|
StringBuilder oracledump = new StringBuilder();
|
||||||
|
//需配置mysql安装路径bin目录环境变量
|
||||||
|
oracledump.append("expdp ");
|
||||||
|
|
||||||
|
// 用户,密码
|
||||||
|
oracledump.append(jobVO.getUsername());
|
||||||
|
oracledump.append("/").append(jobVO.getPassword());
|
||||||
|
|
||||||
|
//数据库名
|
||||||
|
oracledump.append("@").append(jobVO.getDatabaseName());
|
||||||
|
//@TODO 待完善
|
||||||
|
|
||||||
|
return commands;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package com.zyplayer.doc.db.framework.utils;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.quartz.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* quartz工具类
|
||||||
|
*
|
||||||
|
* @author diantu
|
||||||
|
* @since 2023年3月8日
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class QuartzManagerUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 参数传递key
|
||||||
|
*/
|
||||||
|
public static final String PARAM_KEY = "params";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行任务类名
|
||||||
|
*/
|
||||||
|
public static final String CLASS_NAME = "com.zyplayer.doc.db.framework.db.job.BackupJob";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 程序调度器
|
||||||
|
*/
|
||||||
|
@Autowired
|
||||||
|
private Scheduler scheduler;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加定时任务
|
||||||
|
*/
|
||||||
|
public void add(Integer id, String cronExpression, String param, Boolean status) {
|
||||||
|
try {
|
||||||
|
// 构建job信息
|
||||||
|
JobDetail jobDetail = JobBuilder.newJob(getClass(CLASS_NAME).getClass()).withIdentity(getKey(id)).usingJobData(PARAM_KEY, param).build();
|
||||||
|
// 表达式调度构建器(即任务执行的时间)
|
||||||
|
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cronExpression);
|
||||||
|
// 按新的cronExpression表达式构建一个新的trigger
|
||||||
|
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getKey(id)).withSchedule(scheduleBuilder).build();
|
||||||
|
// 创建定时任务
|
||||||
|
scheduler.scheduleJob(jobDetail, trigger);
|
||||||
|
// 停止
|
||||||
|
if (!status) {
|
||||||
|
stop(id);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("添加定时任务失败:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编辑定时任务
|
||||||
|
*/
|
||||||
|
public void update(Integer id, String cronExpression, String param, Boolean status) {
|
||||||
|
try {
|
||||||
|
// 判断是否存在,存在先删除
|
||||||
|
if (scheduler.checkExists(JobKey.jobKey(getKey(id)))) {
|
||||||
|
scheduler.deleteJob(JobKey.jobKey(getKey(id)));
|
||||||
|
}
|
||||||
|
// 再创建
|
||||||
|
add(id, cronExpression, param, status);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("修改定时任务失败:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂停任务
|
||||||
|
*/
|
||||||
|
public void stop(Integer id) {
|
||||||
|
try {
|
||||||
|
scheduler.pauseJob(JobKey.jobKey(getKey(id)));
|
||||||
|
} catch (SchedulerException e) {
|
||||||
|
// 暂停定时任务失败
|
||||||
|
log.error("暂停定时任务失败:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 恢复任务
|
||||||
|
*/
|
||||||
|
public void start(Integer id) {
|
||||||
|
try {
|
||||||
|
scheduler.resumeJob(JobKey.jobKey(getKey(id)));
|
||||||
|
} catch (SchedulerException e) {
|
||||||
|
// 暂停定时任务失败
|
||||||
|
log.error("启动定时任务失败:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 立即执行一次
|
||||||
|
*/
|
||||||
|
public void run(Integer id) {
|
||||||
|
try {
|
||||||
|
scheduler.triggerJob(JobKey.jobKey(getKey(id)));
|
||||||
|
} catch (SchedulerException e) {
|
||||||
|
// 暂停定时任务失败
|
||||||
|
log.error("执行定时任务失败:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除定时任务
|
||||||
|
*/
|
||||||
|
public void delete(Integer id) {
|
||||||
|
try {
|
||||||
|
// 停止触发器
|
||||||
|
scheduler.pauseTrigger(TriggerKey.triggerKey(getKey(id)));
|
||||||
|
// 移除触发器
|
||||||
|
scheduler.unscheduleJob(TriggerKey.triggerKey(getKey(id)));
|
||||||
|
// 删除任务
|
||||||
|
scheduler.deleteJob(JobKey.jobKey(getKey(id)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除定时任务失败:{}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据类名获取类
|
||||||
|
*/
|
||||||
|
private Job getClass(String className) throws Exception {
|
||||||
|
Class<?> class1 = Class.forName(className);
|
||||||
|
return (Job) class1.newInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拼接key
|
||||||
|
*/
|
||||||
|
public String getKey(Integer id) {
|
||||||
|
return "dbBackUp-" + id;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -405,6 +405,42 @@ CREATE TABLE `system_config` (
|
|||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT ='系统配置表';
|
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT ='系统配置表';
|
||||||
|
|
||||||
|
CREATE TABLE `backup_log` (
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键自增ID',
|
||||||
|
`dbId` bigint(20) NULL DEFAULT NULL COMMENT '数据源ID',
|
||||||
|
`category` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '来源(手动备份|自动备份)',
|
||||||
|
`database_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '库名',
|
||||||
|
`tables_name` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '表名',
|
||||||
|
`data_type` tinyint(1) NULL DEFAULT NULL COMMENT '备份方式',
|
||||||
|
`is_compress` tinyint(1) NULL DEFAULT NULL COMMENT '是否压缩',
|
||||||
|
`is_upload` tinyint(1) NULL DEFAULT NULL COMMENT '是否上传',
|
||||||
|
`is_delete` tinyint(1) NULL DEFAULT NULL COMMENT '是否删除',
|
||||||
|
`file_path` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '文件路径',
|
||||||
|
`file_size` bigint(20) NULL DEFAULT NULL COMMENT '文件大小',
|
||||||
|
`status` tinyint(1) NULL DEFAULT NULL COMMENT '备份状态(-1-失败0-备份中1-上传中2-成功)',
|
||||||
|
`msg` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '返回信息',
|
||||||
|
`start_time` datetime(0) NULL DEFAULT NULL COMMENT '开始时间',
|
||||||
|
`end_time` datetime(0) NULL DEFAULT NULL COMMENT '结束时间',
|
||||||
|
`spend_time` bigint(20) NULL DEFAULT NULL COMMENT '耗时(ms)',
|
||||||
|
`del_flag` tinyint(1) NULL DEFAULT NULL COMMENT '删除状态(0--未删除1--已删除)',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
|
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT ='备份记录表';
|
||||||
|
|
||||||
|
CREATE TABLE `backup_task` (
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '备份任务信息主键',
|
||||||
|
`dbId` bigint(20) NULL DEFAULT NULL COMMENT '数据源ID',
|
||||||
|
`cron` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'cron表达式',
|
||||||
|
`param` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '参数',
|
||||||
|
`status` tinyint(1) NULL DEFAULT NULL COMMENT '状态(0-停止1-启动)',
|
||||||
|
`database_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '库名',
|
||||||
|
`tables_name` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '表名',
|
||||||
|
`data_type` tinyint(1) NULL DEFAULT NULL COMMENT '备份方式',
|
||||||
|
`remark` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
|
||||||
|
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`del_flag` tinyint(4) NULL DEFAULT NULL COMMENT '删除状态(0--未删除1--已删除)',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
|
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '备份任务信息';
|
||||||
|
|
||||||
-- -------------------插入必要的数据-------------------
|
-- -------------------插入必要的数据-------------------
|
||||||
-- 用户信息
|
-- 用户信息
|
||||||
INSERT INTO `user_info` (id, user_no, password, user_name, email, del_flag, creation_time, update_time, sex)
|
INSERT INTO `user_info` (id, user_no, password, user_name, email, del_flag, creation_time, update_time, sex)
|
||||||
|
|||||||
Reference in New Issue
Block a user