由於項目需求,最近用到了在系統中給用戶提供數據備份與還原功能,數據庫為SQLserver2005,第一次遇到這種需求,做的過程也遇到了不少問題,特此記錄下來,以備不時之需,同時也供有類似需求的童鞋借鑒,或者有啥好的方案,也可以拿出來大家一起交流嘛。
代碼如下:
1.數據庫工具類用於獲取數據庫連接,執行sql
1 package com.bhne.web.util; 2 3 import java.io.BufferedInputStream; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.sql.Connection; 8 import java.sql.DriverManager; 9 import java.sql.SQLException; 10 import java.util.Properties; 11 12 import javax.servlet.http.HttpServletRequest; 13 14 import org.apache.struts2.ServletActionContext; 15 16 import com.opensymphony.xwork2.ActionContext; 17 18 19 public class DataBaseUtil { 20 /** 21 * 獲取數據庫連接 22 * @return Connection 對象 23 */ 24 public static Connection getConnection(HttpServletRequest request) { 25 Properties prop = new Properties(); 26 Connection conn = null; 27 try{ 28 InputStream in = new BufferedInputStream(new FileInputStream(request.getSession().getServletContext().getRealPath("/")+"dbConfig.properties")); 29 prop.load(in); //加載屬性列表 30 in.close(); 31 32 String url = prop.getProperty("url"); 33 String username = prop.getProperty("username"); 34 String password = prop.getProperty("password"); 35 String driver = prop.getProperty("driver"); 36 37 Class.forName(driver); 38 conn = DriverManager.getConnection(url, username, password); 39 40 } catch (ClassNotFoundException e) { 41 e.printStackTrace(); 42 } catch (SQLException e) { 43 e.printStackTrace(); 44 } catch (IOException e) { 45 e.printStackTrace(); 46 } 47 return conn; 48 } 49 50 public static void closeConn(Connection conn) { 51 if (conn != null) { 52 try { 53 conn.close(); 54 } catch (SQLException e) { 55 e.printStackTrace(); 56 } 57 } 58 } 59 }
2. 資源文件(dbConfig.properties),用來配置一些路徑和數據庫配置
#數據庫配置
#url
url=jdbc:sqlserver://GSEIVR8NBYQ0FXO:1433;DatabaseName=master
#驅動程序
driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
#用戶名
username=sa
#密碼
password =admin
#數據庫名稱
dbname=TaskAssignment
#備份還原路徑(該路徑在數據庫服務器下)
back_path=D:/back_path/
3.備份
/** * 備份數據庫 * @return backup * @throws IOException * @throws Exception */ @RequestMapping("/backup.call") public void backup(HttpServletRequest request,HttpServletResponse response) throws IOException { Map<String,Object> map = new HashMap<String,Object>(); String message = "數據庫備份失敗"; map.put("success", false); map.put("message", message); try { Properties prop = new Properties(); InputStream in = new BufferedInputStream(new FileInputStream(request.getSession().getServletContext().getRealPath("/")+"dbConfig.properties")); prop.load(in); in.close(); String back_path = prop.getProperty("back_path"); String dbname = prop.getProperty("dbname"); //數據庫名 String name =dbname+ new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()); //文件名 File file = new File(back_path); String path = file.getPath() + File.separator + name + ".bak";// name文件名 String str = "backup database "+dbname+" to disk=? with init"; PreparedStatement ps = DataBaseUtil.getConnection(request).prepareStatement(str.toString()); ps.setString(1,path); boolean bl = ps.execute(); if(!bl){ //將生成的備份文件信息寫入配置文件中 FileOutputStream oFile = new FileOutputStream(request.getSession().getServletContext().getRealPath("/")+"dbConfig.properties");//true表示追加打開 prop.setProperty("back_file", name); prop.store(oFile, "The New properties file"); oFile.close(); map.put("success", true); map.put("message", "數據庫備份成功"); JSONObject rt = new JSONObject(map); response.getWriter().write(rt.toString()); } } catch (Exception e) { map.put("success", false); map.put("message", "數據庫備份異常"); JSONObject rt = new JSONObject(map); response.getWriter().write(rt.toString()); e.printStackTrace(); } }
5.還原(需要調用一個存儲過程)
/** * 數據庫還原 * @return recovery * @throws IOException */ @RequestMapping("/recovery.call") public void recovery(HttpServletRequest request,HttpServletResponse response) throws IOException { Map<String,Object> map = new HashMap<String,Object>(); map.put("success", false); map.put("message", "數據庫還原失敗"); try { Properties prop = new Properties(); InputStream in = new BufferedInputStream(new FileInputStream(request.getSession().getServletContext().getRealPath("/")+"dbConfig.properties")); prop.load(in); in.close(); String dbname = prop.getProperty("dbname"); //數據庫名 //備份文件路徑 String name = prop.getProperty("back_file"); String back_path = prop.getProperty("back_path"); File file= new File(back_path); String path = file.getPath()+File.separator+name+".bak"; //恢復所有連接 sql String recoverySql = "alter database "+dbname+" set online with rollback immediate"; StringBuffer restoreSql = new StringBuffer(); restoreSql.append("RESTORE DATABASE ").append(dbname); restoreSql.append(" FROM DISK=N'").append(path).append("'"); restoreSql.append(" WITH REPLACE,FILE = 1,RECOVERY,STATS = 5"); PreparedStatement ps = DataBaseUtil.getConnection(request).prepareStatement(recoverySql); PreparedStatement rs = DataBaseUtil.getConnection(request).prepareStatement(restoreSql.toString()); CallableStatement cs = DataBaseUtil.getConnection(request).prepareCall("{call killrestore(?,?)}"); cs.setString(1, dbname); // 數據庫名 cs.setString(2, "'"+path+"'"); // 已備份數據庫所在路徑 boolean bl = cs.execute(); // 關閉數據庫鏈接進程 boolean flg = rs.execute(); boolean recoverFlg = ps.execute(); // 恢復數據庫連接 if((!bl)&&(!recoverFlg)&&(!flg)){ map.put("success", true); map.put("message", "數據庫還原成功"); } } catch (Exception e) { e.printStackTrace(); } JSONObject rt = new JSONObject(map); response.getWriter().write(rt.toString()); }
6.存儲過程(需放入mster數據庫中)
CREATE proc killrestore (@dbname varchar(20),@dbpath varchar(40)) as begin declare @sql nvarchar(500) declare @spid int set @sql='declare getspid cursor for select spid from sys.sysprocesses where dbid=db_id('''+@dbname+''')' exec (@sql) open getspid fetch next from getspid into @spid while @@fetch_status <> -1 begin exec('kill '+@spid) fetch next from getspid into @spid end close getspid deallocate getspid end
全部代碼供上。
注意事項:
1.執行數據庫備份時,生成的數據庫備份文件會保存在數據庫服務器所在的機器中,而不是客戶機中。
2.還原功能,大多數項目開發都是使用框架來搞的,很多人估計很疑惑,為啥還單獨寫一個jdbc工具類呢,是這樣的,在執行數據還原之前,首先要關閉數據鏈接,調用的那個存儲過程就是用來殺死進程的,如果使用系統鏈接的那個數據庫來操作的話,會出現【不能kill自己的進程】,所有通過master數據鏈接來進行操作就可以避免這個問題了。
3.原作者在通過存儲過程進行數據還原的,但是不知道啥原因,存儲過程執行的時候老是報【系統錯誤2】並且路徑也不正確,所有我就把還原的命令拿出來單獨執行了。
參考鏈接:http://blog.csdn.net/tkd03072010/article/details/6668940