项目中需要提供程序上去备份数据库,基础代码来自网上,但是却留了个坑,好不容易才趟过去……
直接上代码:
一、备份(2018/01/03 )
/**
* 备份mysql数据库
* @param root mysql登录名
* @param rootPass 登录密码
* @param dbName 要备份的数据库名称
* @param backupsPath 备份的路径
* @param backupsSqlFileName 备份文件的名字
* @return
*/
public static String dbBackUp(String root,String rootPass,String dbName,String backupsPath,String backupsSqlFileName)
{
//生成临时备份文件
// SimpleDateFormat sd=new SimpleDateFordckupsSqlFileName;
String pathSql = backupsPath+backupsSqlFileName;
try {
File fileSql = new File(pathSql);
if(!fileSql.exists()){
fileSql.createNewFile();
}
StringBuffer sbs = new StringBuffer();
sbs.append(CMDPrefix+"mysqldump ");
sbs.append(" -h 127.0.0.1 ");
sbs.append(" -u ");
sbs.append(root+" ");
sbs.append("-p"+rootPass+" ");
sbs.append(dbName);
sbs.append(" --default-character-set=utf8 ");
// sbs.append(">"+pathSql);
sbs.append(" --result-file="+pathSql);
System.out.println("cmd命令为:——>>>"+sbs.toString());
Runtime runtime = Runtime.getRuntime();
Process child = runtime.exec(sbs.toString());
//读取备份数据并生成临时文件
InputStream in = child.getInputStream();
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(pathSql), "utf8");
BufferedReader reader = new BufferedReader(new InputStreamReader(in, "utf8"));
String line=reader.readLine();
while (line != null) {
writer.write(line+"\n");
line=reader.readLine();
System.out.println(line);
}
writer.flush();
System.out.println("数据库已备份到——>>"+pathSql);
} catch (Exception e) {
}
return pathSql;
}
注意这里的--default-character-set=utf8设置了编码
二、恢复
出现坑的地方--default-character-set=utf8设置了编码,如果不加这一句,很有可能出现通道关闭的错误提示,另外,流的关闭顺序也要注意
writer.close();
br.close();
out.close();
代码:
public static boolean load() {//还原
try {
String fPath = "e:/emaster2000DB_20180103151600.sql";
Runtime rt = Runtime.getRuntime();
// 调用 mysql 的 cmd:
Process child = rt.exec(CMDPrefix+"mysql -uroot -ptime emaster2000 --default-character-set=utf8 ");
OutputStream out = child.getOutputStream();//控制台的输入信息作为输出流
String inStr;
StringBuffer sb = new StringBuffer("");
String outStr;
BufferedReader br = new BufferedReader(new InputStreamReader(
new FileInputStream(fPath), "utf8"));
OutputStreamWriter writer = new OutputStreamWriter(out, "utf8");
int i=0;
while ((inStr = br.readLine()) != null) {
sb.append(inStr+"\r\n");
System.out.println(inStr);
}
outStr = sb.toString();
writer.write(outStr);
// 别忘记关闭输入输出流
writer.close();
br.close();
out.close();
System.out.println("/* Load OK! */");
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
三、补充(2018/01/08)
1.补充的原因:备份的方式没有问题,但是恢复的方式会有一定的问题,当备份出来的SQL文件很大时,在恢复时,由于代码是将SQL文件全部转成流发送给cmd命令窗口,这样会导致内存溢出。解决方式将SQL文件一部分一部分的写,也就是使用缓存,但是由于SQL执行的原因,个人很担心会出现SQL执行问题(本人未测试),另外一种方式是使用类似我们在cmd下执行mysql的source + SQL文件路径 这样的命令,此处选择第二种方式。
2.备份
/**
* 备份mysql数据库
*
* @param root
* mysql登录名
* @param rootPass
* 登录密码
* @param dbName
* 要备份的数据库名称
* @param backupsPath
* 备份的路径
* @param backupsSqlFileName
* 备份文件的名字
* @return
*/
public static String dbBackUp(String root, String rootPass, String dbName, String host,String CMDPrefix, String backupsPath,
String backupsSqlFileName) {
try {
Runtime rt = Runtime.getRuntime();
Process pro = rt.exec(getBackupCommand(root, rootPass, dbName, host, CMDPrefix, backupsPath, backupsSqlFileName));
BufferedReader br = new BufferedReader(new InputStreamReader(pro.getErrorStream()));
String errorLine = null;
while ((errorLine = br.readLine()) != null) {
logger.error("####################"+errorLine+"##################");
}
br.close();
int result = pro.waitFor();
if (result != 0) {
logger.error("####################数据库备份失败##################");
}
} catch (IOException e) {
logger.error("####################数据库备份失败,"+e.getMessage()+"##################");
} catch (InterruptedException e) {
logger.error("####################数据库备份失败,"+e.getMessage()+"##################");
} catch (Exception e) {
logger.error("####################数据库备份失败,"+e.getMessage()+"##################");
}
return backupsSqlFileName;
}
/**
*
* (Javadoc)
* @Title: getBackupCommand
* @Description:
* @param @param root 数据库用户名
* @param @param rootPass 数据库用户密码
* @param @param dbName 数据库名称
* @param @param host 主机好
* @param @param CMDPrefix 命令前缀 最后需要加上\\
* @param @param backupsPath 备份路径
* @param @param backupsSqlFileName SQL文件名称
* @param @return
* @return String[]
* @throws
*/
private static String[] getBackupCommand(String root, String rootPass, String dbName, String host,String CMDPrefix, String backupsPath,
String backupsSqlFileName) {
BackupsDB.CMDPrefix=CMDPrefix;
String[] cmd = new String[3];
String os = System.getProperties().getProperty("os.name");
if (os.startsWith("Win")) {
cmd[0] = "cmd.exe";
cmd[1] = "/c";
} else {
cmd[0] = "/bin/sh";
cmd[1] = "-c";
}
StringBuilder arg = new StringBuilder();
arg.append(BackupsDB.CMDPrefix+"mysqldump ");
arg.append("-u");
arg.append(root);
arg.append(" -p");
arg.append(rootPass);
arg.append(" --default-character-set=");
arg.append(CHARSET);
// arg.append(" --skip-opt ");
arg.append(" --add-drop-database ");
// arg.append("--routines ");
arg.append("--triggers ");
//arg.append("--compress ");
arg.append("-r ");
arg.append(backupsPath);
arg.append(backupsSqlFileName);
arg.append(".sql ");
arg.append("--databases ");
arg.append(dbName);
cmd[2] = arg.toString();
return cmd;
}
3.恢复
/**
*
* @param root
* 数据库用户名
* @param rootPass
* 数据库密码
* @param backupsPath
* 备份文件路径
* @param dbName
* 数据库名
* @return true 备份成功,false 备份失败
*/
public static boolean load(String root, String rootPass, String host,String CMDPrefix,String backupsPath, String dbName) {// 还原
//BackupsDB.CMDPrefix=CMDPrefix;
Runtime rt = Runtime.getRuntime();
try {
Process pro = rt.exec(getLoadCommand(root, rootPass, host, CMDPrefix, backupsPath, dbName));
BufferedReader br = new BufferedReader(new InputStreamReader(pro.getErrorStream()));
String errorLine = null;
while ((errorLine = br.readLine()) != null) {
System.out.println(errorLine);
}
br.close();
int result = pro.waitFor();
if (result != 0) {
logger.error("####################数据库恢复失败##################");
return false;
}
return true;
} catch (IOException e) {
e.printStackTrace();
logger.error("####################数据库恢复失败,"+e.getMessage()+"##################");
} catch (InterruptedException e) {
logger.error("####################数据库恢复失败,"+e.getMessage()+"##################");
} catch (Exception e) {
logger.error("####################数据库恢复失败,"+e.getMessage()+"##################");
}
return false;
}
/**
*
* (Javadoc)
* @Title: getLoadCommand
* @Description:
* @param @param root 数据库用户名
* @param @param rootPass 数据库密码
* @param @param host 数据库主机好
* @param @param CMDPrefix 命令前缀路径
* @param @param backupsPath SQL文件路径
* @param @param dbName 数据库名
* @param @return
* @return String[]
* @throws
*/
private static String[] getLoadCommand(String root, String rootPass, String host,String CMDPrefix,String backupsPath, String dbName) {
String[] cmd = new String[3];
String os = System.getProperties().getProperty("os.name");
if (os.startsWith("Win")) {
cmd[0] = "cmd.exe";
cmd[1] = "/c";
} else {
cmd[0] = "/bin/sh";
cmd[1] = "-c";
}
StringBuilder arg = new StringBuilder();
arg.append(BackupsDB.CMDPrefix+"mysql ");
arg.append("-u");
arg.append(root);
arg.append(" -p");
arg.append(rootPass);
arg.append(" --default-character-set=");
arg.append(CHARSET);
arg.append(" ");
arg.append(dbName);
arg.append(" <");
arg.append(backupsPath);
cmd[2] = arg.toString();
return cmd;
}
注意点:1.CMDPrefix 命令前缀路径一定是你的mysqldump和mysql的路径
2.此处命令执行的写法是有原因的(主要指我们使用 < 这个符号,在java中没有这个定向符,cmd中是有的,所有就算拼接好的命令使用java执行和使用cmd执行是不一样的),
3.备份和恢复,都要设置相同的字符编码