今天在做毕设的最后一个模块,数据库的备份和还原。按照拿来主义的惯例,先百度一下,找找资料,很快就搞定了数据库的备份,但还原却总是出问题,最后还是搞定,在此写下记录,希望对大家有帮助。

 

至于备份还原数据库,肯定少不了一些基本的配置信息,如数据库的主机地址啦(host)、端口啦(port)、还有用户名、密码,以及你要操作的那个数据库的名称、存放备份文件(*.sql文件)的路径等等。

这些信息我在Spring中配置好了,如下:

<!-- 用于读取数据库的基本信息 -->
	<bean id="dataInfo"
		class="com.shutao.hr.assist.DBInfo">
		<property name="dbName" value="hr"/>
		<property name="username" value="root"/>
		<property name="password" value="shutao"/>
		<property name="host" value="127.0.0.1"/>
		<property name="port" value="3306"/>
		<property name="dbToolsPath" value="D:/MySQL/MySQL Server 5.1/bin/"/>
		<property name="tablesPath" value="F:/hr/hr_tables/"/>
		<property name="databasePath" value="F:/hr/hr_database/"/>
	</bean>

然后新建一个用来操作数据库备份还原的类:DBManager。

第一步要做的当然就是读取配置信息,如下是该类的一部分代码:

static String dbName = "";
	static String username = "";
	static String password = "";
	static String host = "";
	static String port = "";
	static String dbToolsPath = "";
	static String databasePath = "";
	static String tablesPath = "";
	static String filePath = ""; //备份文件的路径
	static ApplicationContext context = new ClassPathXmlApplicationContext(
	"applicationContext.xml");
	
	static {
		DBInfo dbInfo = (DBInfo) context.getBean("dataInfo");
		dbName = dbInfo.getDbName();
		username = dbInfo.getUsername();
		password = dbInfo.getPassword();
		host = dbInfo.getHost();
		port = dbInfo.getPort();
		dbToolsPath = dbInfo.getDbToolsPath();
		databasePath = dbInfo.getDatabasePath();
		tablesPath = dbInfo.getTablesPath();
	}

生成用于备份数据库的shell命令类:

/**
	 * 生成用于备份数据库的shell命令
	 * @param 
	 * targetName 要备份的对象名:只能为表名和数据库名称				
	 * @return 实际执行的shell命令
	 */
	public static String getBackupShellString(String targetName) {
		String basepath = Thread.currentThread().getContextClassLoader().getResource("").toString();
		String database_tools_path = dbToolsPath;
		//备份的文件
		String backFilePath = "";
		// 若要备份整个数据库
		if (targetName.equals(dbName)) {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
			// 要备份的文件
			backFilePath = databasePath + targetName + "_"+ sdf.format(new Date()) + ".sql";
			targetName = "";
		} else {
			backFilePath = tablesPath + targetName + ".sql";
		}
		// 判断要备份的文件/文件夹是否已存在
		File backFileFolder_db = new File(databasePath);
		File backFileFolder_tab = new File(tablesPath);
		
		if (!backFileFolder_db.exists()) {
			backFileFolder_db.mkdirs();
		}
		if (!backFileFolder_tab.exists()) {
			backFileFolder_tab.mkdirs();
		}
	
		String OSType = System.getProperty("os.name");
		String shellStr = "";
			shellStr = database_tools_path + "mysqldump -h " + host
					+ " -P " + port + " -u " + username + " -p" //★第二个-p后面不能有空格,否则将被认为是数据库的名称
					+ password + " --result-file=" + backFilePath
					+ " --default-character-set=gbk " + dbName + " "
					+ targetName;
		System.out.print("##############" + shellStr);
		return shellStr;
	}

进行备份操作的类:

/**
	 * 备份数据库
	 * @param 
	 * targetName 要备份的对象名:只能为表名和数据库名称
	 * @return 成功:TRUE 失败:FALSE 
	 * 
	 */
	public static boolean backup(String targetName) {
			Runtime runt = Runtime.getRuntime();
			Process proc;
			try {
				proc = runt.exec(getBackupShellString(targetName));
				int tag = proc.waitFor();// 等待进程终止
				if (tag == 0) {
					return true;
				} else {
					return false;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			return true;
	}
///还原数据库/
public static void restoreDB(String targetName) {
		//要读取的备份文件的路径
		String fPath="";
        try {
           if(targetName.equals(dbName)){
        	   //这里用于测试,先指定一个数据库文件,实际应用中应根据选择来确定文件名称
        	   fPath = databasePath+"hr_20110415161235.sql";   
           }else{
        	   fPath = tablesPath+targetName+".sql";
           }  
           System.out.println("fPath:"+fPath);
        Runtime rt = Runtime.getRuntime();    
            // 调用 mysql 的 cmd:    
        Process child = rt.exec(dbToolsPath+"mysql -uroot -pshutao "+dbName);   
        
    //    int tag = child.waitFor();
  
        OutputStream out = child.getOutputStream();//控制台的输入信息作为输出流    
        String inStr;    
        StringBuffer sb = new StringBuffer("");    
        String outStr; 
        //下面的InputStreamReader和OutputStreamWriter的第二个参数为数据的编码格式,    
        // 注意要跟备份的格式一样,否则会有异常:java.io.IOException: 管道已结束。
     BufferedReader br=new BufferedReader(new InputStreamReader( new FileInputStream(fPath), "gbk"));    
            while ((inStr = br.readLine()) != null) {
                sb.append(inStr + "/r/n");    
            }    
            outStr = sb.toString();    
            OutputStreamWriter writer = new OutputStreamWriter(out, "gbk");    
            writer.write(outStr);    
            writer.flush();    
            // 别忘记关闭输入输出流    
            out.close();    
            br.close();    
            writer.close();    
            System.out.println("/* Load OK! */");    
        } catch (Exception e) {    
            e.printStackTrace();    
        }    
    }

上面的几个方法经过测试都可以运行。

 

 

还原的时候切记注意那个编码格式,一开始用的是utf8,一直出现那个管道已结束的异常.......

 

一开始用的是直接用Mysql的命令来还原,但一直不成功,命令倒是可以执行,如下:

/**
	* 根据路径生成恢复数据库的Shell字符串
	* @param targetName  要还原的对象名:只能为表名和数据库名称
	* @return 恢复数据时实际执行的shell
	*/
	public static String getRestoreShellString(String targetName) {
		String database_tools_path = dbToolsPath;
		String backFile = "";// 已备份的文件
		if (targetName.indexOf(dbName) == -1) {// 还原表
			backFile = tablesPath + targetName + ".sql";
		} else {// 还原库
			backFile = databasePath + targetName;
		}
		String shellStr = "";
		shellStr = database_tools_path + "mysql -h" + host + " -P"
					+ port + " -u" + username + " -p"
					+ password + " --default-character-set=gbk "
					+ dbName + " < " + backFile;
	
		return shellStr;
	}
	/**
	* 恢复数据库
	* @param targetName 要备份的对象名:只能为表名和数据库名称
	* @return 成功:TRUE 失败:FALSE
	*/
	public static boolean restore(String targetName) {
		try {
			Runtime runt = Runtime.getRuntime();
			Process proc ;
			String cmdtext = getRestoreShellString(targetName);
			if (System.getProperty("os.name").indexOf("Windows") != -1) {
				String[] cmd = { "cmd", "/c", cmdtext };
				proc = runt.exec(cmd);
			} else {
				String[] cmd = { "sh", "-c", cmdtext };
				proc = runt.exec(cmd);
			}
			System.out.println("cmdtext: "+cmdtext);
			int tag = proc.waitFor();// 等待进程终止
			System.out.println("进程返回值为tag:" + tag);
			if (tag == 0) {
				return true;
			} else {
				return false;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}

具体原因暂且不去研究了,还有其他事情要做了~~乖乖用第一中读取文件流的方法吧。