最近项目到了收尾的阶段,为了使我们的系统功能更加完成,也为了使客户更加安心,决定添加一个oracle备份和还原的功能。现做一下分享。
一、导入导出命令
oracle备份和还原除了使用工具比如PL/SQL进行导出导入还可以利用命令,在程序中也就只能调用外部命令了,首先我们得知晓oracle备份和还原的命令,根据需要我是需要备份和还原项目数据库的所有的表的,以下命令为备份和还原所有表命令。
备份:exp username/password@TEST file=D:\export.dmp
还原:imp username/password@TEST file=D:\export.dmp full=y ignore=y
二、Java代码实现
调用runtime方法,因为是远程的数据库,所以SID为ip+"/"+数据库名
/**
* 实现Oracle数据库导出
* @author weiwenwen
* userName 进入数据库所需要的用户名
* password 进入数据库所需要的密码
* SID 用户所在的数据库名
* avePath 数据库导出文件保存路径
* fileName 数据库导出文件文件名
* @return 返回0表示导出成功
* @throws Exception
*/
public int oraBackup() throws IOException{
//读取配置文件配置项
String userName=Global.getConfig("jdbc.username");
String password=Global.getConfig("jdbc.password");
String savePath=Global.getConfig("oraBackupPath");
String SID=Global.getConfig("SID");
String id=IdGen.uuid();
File saveFile = new File(savePath);
if (!saveFile.exists()) {// 如果目录不存在
saveFile.mkdirs();// 创建文件夹
}
//将记录插入数据库
PageData pd=new PageData();
pd.put("id", id);
pd.put("user_name", UserUtils.getUser().getName());
pd.put("op_dt", new Date());
oracleBackupDao.insertOraBackup(pd);
//所有空表
List tablesList=oracleBackupDao.findAltTables();
//为所有空表执行语句
for (int i = 0; i < tablesList.size(); i++) {
oracleBackupDao.doAltSql(tablesList.get(i));
}
String command ="exp " + userName + "/" + password + "@" + SID + " file=" + savePath + id + ".dmp";
System.out.println("备份数据库命令操作:" + command);
Process process = Runtime.getRuntime().exec(command);
final InputStream is1 = process.getInputStream();
new Thread(new Runnable() {
public void run() {
BufferedReader br = new BufferedReader(new InputStreamReader(is1));
String info;
try {
while ((info=br.readLine()) != null){
System.out.println("info: "+info);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start(); // 启动单独的线程来清空process.getInputStream()的缓冲区
InputStream is2 = process.getErrorStream();
BufferedReader br2 = new BufferedReader(new InputStreamReader(is2));
// 保存输出结果
StringBuilder buf = new StringBuilder();
String line = null;
int i=0;
while ((line = br2.readLine()) != null){
// 循环等待ffmpeg进程结束
System.out.println("info: " +line);
buf.append(line);
}
try {
if(buf.toString().contains("ORA-")&&buf.toString().contains("EXP-")){
System.err.println("备份失败!");
process.destroy();
}else{
i=process.waitFor();
System.out.println("over status: "+i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("备份结束...");
//备份完成将文件大小更新到表,备份的文件路径
String dmpPath = savePath + id + ".dmp";
//获取文件大小
pd.put("file_size", new File(dmpPath).length());
oracleBackupDao.updateFileSize(pd);
return 0;
}
PS:因为是oracle11g,发现11G中新特性,当表无数据时,不分配segment,以节省空间。而使用exp命令时,无Segment的表不会被导出。所以如果单纯直接执行导出语句是不可以的,必须查询所有的空表后,执行alter命令。
select table_name from user_tables where NUM_ROWS=0;
如果平时导出的话直接执行这句:
select 'alter table '||table_name||' allocate extent;' from user_tables where num_rows=0
最后为所有的表执行alter命令
alter table '表名' allocate extent;
导入还原的代码是类似的,根据命令自己拼字符串即可,不再累述
小结:
1.看似一个小的功能,当简单代码的完成后的测试更是重头戏,要考虑表是不是全部导出,每个表的数据是不是完全,要考虑备份导出过程中的突发情况,比如网线断连接。
2.导入还原时添加full=y ignore=y,否则导入会报错
3.如果原数据库是有数据的直接导入的话数据会在原基础上添加,所以可能存在数据重复现象,试了很多办法不可以,所以我的处理是在还原时查询所有表,用truncate强制执行清表的操作,再重新导入。