由于项目需求,最近用到了在系统中给用户提供数据备份与还原功能,数据库为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