项目中需要提供程序上去备份数据库,基础代码来自网上,但是却留了个坑,好不容易才趟过去……
直接上代码:
一、备份(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.备份和恢复,都要设置相同的字符编码