一个具有使用价值的数据整理系统应该具有数据库的备份功能。数据库管理软件例如Navicat就可以很简单地备份,并且可以提取备份的sql文件存在本地。项目具有删除用户数据库之前备份和用户数据库定时备份的功能,本篇博客先来讨论删除数据库之前如何备份。
备份数据库的命令为mysqldump -u用户名 -p密码 数据库名 > 路径\\文件名
,其中mysqldump是MySQL Server的bin目录下的一个exe可执行文件,在命令行执行备份语句时要先把工作目录转到bin目录下,且密码位置省略以保证数据安全,回车后执行命令再输入密码。在Java代码中不能再用JDBC执行备份命令,可以使用Runtime.getRuntime().exec(command);
调用执行cmd命令。前面我提到在命令行执行备份命令时要转到MySQL Server的bin目录,所以在Java代码中也要转到此目录下后再执行语句。可在mysqldump前加上bin目录的地址,也可在项目目录中新建一个文件夹,将mysqldump.exe复制进去。目录前还要加上cmd /c
,cmd /c dir 是执行完dir命令后关闭命令窗口,不加上则命令不会执行。具体代码在serviceImpl文件中,如下所示:
//删除之前先备份!
@Override
public int backupDB(int id) throws IOException {
DB db=dbMapper.findDBById(id);
String dbName=db.getDBEN();
System.out.println(dbName);
String user = "root";
String password = "xxxxxxxx";
logger.info("系统对将要删除的数据库备份");
// 当前时间 年月日格式
LocalDate localDate = LocalDate.now();
// 备份文件名称
String fileName = dbName+localDate.toString() + ".sql";
String outFilePath = "D:\\backup\\"+fileName;
String command="cmd /c D:\\mysql\\mysqldump -u" + user + " -p" + password +" "+ dbName+ " > " +outFilePath;
Runtime.getRuntime().exec(command);
System.out.println("备份完毕!");
return 1;
}
在实现备份功能时我遇到了很多错误。
- mysqldump命令的执行环境必须在MySQL Server的bin目录下,还必须有cmd /c,而且命令中不能有多余的空格,“>”后的文件名要加上绝对路径,一方面出现问题就可能获得0KB的文件。
- 网上的许多教程都偏向于Runtime对象执行命令后存入一个Process对象,然后再将此对象以InputStream输入流的格式存入BufferedReader对象中,最后生成一个文件输出流将备份内容写入新创建的文件中。这么做不仅麻烦,而且无效,会得到一个0KB的文件。直接将命令中“>”后的文件名改为绝对路径+文件名,后缀为.sql,就可以直接得到备份内容的sql文件。
- 得到的备份文件缺乏表结构和表数据。解决了上述两个问题后,我得到了一个非0KB的sql文件,但是打开后只有数据库名信息和一些注释,没有数据库中的表结构和数据。我反复尝试后发现,只要在执行备份命令后进行删除数据库操作,得到的sql文件就只有注释。可能是语句执行顺序出现偏差。所以我选择改变一下逻辑,在删除数据库界面增添了一个备份按钮,可以选择先备份再删除,如果直接点击删除按钮,会提示可以先备份。这样就可以解决sql文件缺乏表结构和数据的问题了。
经过测试,备份后的数据库可通过命令行恢复其表结构和表数据。注意要先建立同名数据库再执行命令!命令为mysql -u用户名 -p 数据库名 < 绝对路径\\文件名
,同样要在bin目录下执行命令。
controller层代码为:
//备份数据库
@RequestMapping("/backupDB/{id}")
public Map<String, Object> backupDB(@PathVariable int id) throws Exception {
Map<String, Object> map = new HashMap<>();
//先备份!
System.out.println("传到后端的id:"+id);
int bu=db.backupDB(id);
System.out.println("备份用户数据库的结果:"+bu);
if (bu>0) {
map.put("state", true);
map.put("msg", "删除成功!");
} else {
map.put("state", false);
map.put("msg", "删除失败!");
}
return map;
}
前端页面展示: