本人在一次项目中,遇到了需要在系统管理中提供给用户备份还原系统数据的功能,由于项目特殊性,项目底层数据库使用了国产人大金仓数据库(版本V8)。由于本人也是第一次使用金仓数据库,所以在功能实现过程中,踩到了一些坑,特此记录一下,共大家参考,避免踩到和我一样的坑。
实现思路及解决方案如下:
1.实现数据备份,无非使用java代码调用数据库提供的备份、还原指令;
2.查阅金仓数据库官方文档,了解备份还原指令:
根据官方文档的参考手册-客户端应用参考手册,发现到官方提供了备份数据库的工具sys_dump(参考链接:13. sys_dump — KingbaseES产品手册)
官方提供的命令行参数语法如下:
sys_dump
[ connection-option
...] [ option
...] [ dbname
]
于是我很快就写出来备份指令:
sys_dump -d 数据库名 -h 127.0.0.1 -p 54321 -U root -W -Fp -f d:/aaa.sql
运行指令后需要输入密码(如下图),即可备份成功!
3.开始编码的实现。
于是本人拿着这条看似没问题的备份指令,一顿操作猛如虎,写下了如下代码:
public AjaxResult backups() {
String sqlFileName = "D:/test/" + dbName + "_" + new Date().getTime() + ".sql";
File file = new File(sqlScriptPath + File.separator + sqlFileName);
//目录生成
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
try {
ProcessBuilder processBuilder= new ProcessBuilder("sys_dump -d test -h 127.0.0.1 -p 54321 -U root -W 123456 -Fp -f " + sqlFileName);
Process process= processBuilder.start();
if (process.waitFor() == 0) {
return AjaxResult.success("备份成功!");
} else {
return AjaxResult.error("备份失败!");
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("备份失败!");
}
}
结果执行报错如下:
所以:sys_dump -d 数据库名 -h 127.0.0.1 -p 54321 -U root -W 123456 这个指令是有问题的,于是我又打开了命令窗口,调试了指令发现指令语法错误 -W 123456
然后我又一遍查阅了文档,知道了-W后面不可以直接加密码,想要加密码,官方提供的示例并没有涉及。
没办法了,我只能去社区问一下,然后发现了
于是我get到了,便优化了我的指令
sys_dump -Fp -f d:/bbb.sql "host=127.0.0.1 port=54321 user=root password=123456 dbname=test"
执行后,sql脚本成功生成,哈哈哈,问题解决了。
最后执行如下备份代码,成功执行备份数据库。
public AjaxResult backups() {
String sqlFileName = "D:/test/" + new Date().getTime() + ".sql";
File file = new File(File.separator + sqlFileName);
//目录生成
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
try {
//不可行
//ProcessBuilder processBuilder = new ProcessBuilder("sys_dump -Fp -f d:/bbb.sql \"host=127.0.0.1 port=54321 user=root password=123456 dbname=test\"");
//原因:new ProcessBuilder(command)的command参数不支持空格和’>’等特殊号
ProcessBuilder processBuilder = new ProcessBuilder("sys_dump", "-Fp", "-f", sqlFileName,
"\"host=127.0.0.1", "port=54321", "user=root", "password=123456", "dbname=test\"");
Process process = processBuilder.start();
if (process.waitFor() == 0) {
return AjaxResult.success("备份成功!");
} else {
return AjaxResult.error("备份失败!");
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("备份失败!");
}
}
数据库还原代码如下:
public AjaxResult reduction() {
String sqlFileName = "d:/bbb.sql";
try {
//金仓数据库还原指令:ksql -f d:\bbb.sql "host=127.0.0.1 port=54321 user=root password=123456 dbname=test"
ProcessBuilder processBuilder = new ProcessBuilder("ksql", "-f", sqlFileName,
"\"host=127.0.0.1", "port=54321", "user=root", "password=123456", "dbname=test\"");
Process process = processBuilder.start();
if (process.waitFor() == 0) {
return AjaxResult.success("还原成功!");
} else {
return AjaxResult.error("还原失败!");
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("还原成功!");
}
}
备注:使用ksql执行sql文件还原数据库,如果库里的表存在,执行sql脚本将会报错(因为金仓数据库还原库机制都是要基于先清空库),上面的还原代码将会一直堵塞,所以该还原代码执行成功的前提条件就是手动删除数据库里的表才能执行还原成功。
解决方案:金仓数据库备份还原最好使用dmp文件导入导出的形式,官方对于本地备份还原库操作提供了exp/imp(3. exp/imp工具介绍 — KingbaseES产品手册),具体命令参数参考(5. 附录A:exp导出参数说明 — KingbaseES产品手册和6. 附录B:imp导入参数说明 — KingbaseES产品手册)
具体指令参考如下:
//导出:exp host=127.0.0.1 port=54321 dbname=test user=root password=123456 OWNER=public FILE=d:\ccc.dmp //导入:imp host=127.0.0.1 port=54321 dbname=test user=root password=123456 FROMUSER=public CLEAN=y FILE=d:\ccc.dmp
4.最终备份还原代码实现如下:
public AjaxResult backups() {
String sqlFileName = "d:/test/ccc.dmp";
File file = new File(File.separator + sqlFileName);
//目录生成
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
try {
//导出:exp host=127.0.0.1 port=54321 dbname=test user=root password=123456 OWNER=public FILE=d:test\ccc.dmp
ProcessBuilder processBuilder = new ProcessBuilder("exp", "host=127.0.0.1", "port=54321", "dbname=test",
"user=root", "password=123456", "OWNER=public", "password=123456", "FILE=" + sqlFileName );
Process process = processBuilder.start();
if (process.waitFor() == 0) {
return AjaxResult.success("备份成功!");
} else {
return AjaxResult.error("备份失败!");
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("备份失败!");
}
}
public AjaxResult reduction() {
String sqlFileName = "d:/test/ccc.dmp";
try {
//金仓数据库还原指令:ksql -f d:\bbb.sql "host=127.0.0.1 port=54321 user=root password=123456 dbname=test"
// ProcessBuilder processBuilder = new ProcessBuilder("ksql", "-f", sqlFileName,
// "\"host=127.0.0.1", "port=54321", "user=root", "password=123456", "dbname=test\"");
//导入:imp host=127.0.0.1 port=54321 dbname=test user=root password=123456 FROMUSER=public CLEAN=y FILE=d:\ccc.dmp
ProcessBuilder processBuilder = new ProcessBuilder("imp", "host=127.0.0.1", "port=54321",
"dbname=test", "user=root", "password=123456", "FROMUSER=public" , "CLEAN=y", "FILE=" + sqlFileName);
Process process = processBuilder.start();
if (process.waitFor() == 0) {
return AjaxResult.success("还原成功!");
} else {
return AjaxResult.error("还原失败!");
}
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("还原成功!");
}
}
注意!!!!
本人忘提了一件事:金仓数据库一定要配置环境系统变量PATH环境,否则执行不了数据库操作指令。
配置完环境后,需要重启金仓数据库服务!!!!!
最后附上:
我从社区提问了关于指令的问题,为什么 -W+ 密码 需要再手动输入密码?得到了版主的及时回复
然后我在版主哪里看到了他之前发的帖子-非交互式备份还原指令
这些非交互式备份还原指令就可以用我们的java程序通过process进程来执行备份还原数据库!!!