Chinaunix首页 | 论坛 | 博客
  • 博客访问: 91313
  • 博文数量: 81
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1007
  • 用 户 组: 普通用户
  • 注册时间: 2013-11-20 14:50
文章分类

全部博文(81)

文章存档

2014年(21)

2013年(60)

我的朋友

分类: Java

2013-11-20 15:06:06

  一、项目需求

  最近项目中需要对数据库(Sql Server系列数据库)进行备份。项目中的需求不是简单的整个数据库的备份,而是根据用户来备份,具体的备份策略如下:

  ①系统为某一赛事管理类型的系统,整个系统分为几部分,前半部分的处理是在服务器上处理,后半部分的处理,是在用户自己的客户端中处理。不同的赛事对应不同的用户,用户将需要的数据提交给系统进行处理。

  ②系统在处理完数据之后,用户可以导出自己赛事相关的数据了,这个导出实际上做的就是个备份数据库,只不过只备份属于自己的数据库。然后下载系统的处理结果,很显然,各个用户的数据是不相同的。

  ③用户下载自己备份的数据库文件(这些个文件会打包成zip压缩包),再导入自己的客户端系统中。

  整个系统使用的关键是:程序中调用bcp命令的时候传递用户相关的参数过去就OK了。处理流程如图1-1所示:


  图1-1 处理流程

  二、解决方案

  本文提出的是使用Runtime.getRuntime().exec()调用批处理文件,程序中传递相关参数给bcp命令。关于BCP命令的详解,微软的MSDN上面写的非常的详细,在此就不赘述了!下面的一段话引用自微软的msdn上面的一段介绍:

  bcp 实用工具可以在 Microsoft SQL Server 实例和用户指定格式的数据文件间大容量复制数据。使用 bcp 实用工具可以将大量新行导入 SQL Server 表,或将表数据导出到数据文件。除非与 queryout 选项一起使用,否则使用该实用工具不需要了解 Transact-SQL 知识。若要将数据导入表中,必须使用为该表创建的格式文件,或者必须了解表的结构以及对于该表中的列有效的数据类型。

  参见bcp实用工具.

  三、系统的实现

  实现主要分为批处理文件的编写和Java程序中的调用两部分,这两部分完成之后,第一部分中的后续问题就好解决了。

  ①批处理编写:

  编写的批处理文件如下,也就是使用bcp工具来进行备份

  这里只列出部分表,代码都是一样的,使用的时候稍微修改下即可!

  @Title 根据userId导出的表 @bcp "SELECT * FROM sportSys.dbo.competitions where sportSys.dbo.competitions.userId="+%1 queryout %2competitions.xls -T -c >>%3log.txt @echo competitions表备份已完成!>>%3log.txt @bcp "SELECT * FROM sportSys.dbo.item where sportSys.dbo.item.userId="+%1 queryout %2item.xls -T -c >>%3log.txt @echo item表备份已完成!>>%3log.txt @bcp "SELECT * FROM sportSys.dbo.itemType where sportSys.dbo.itemType.userId="+%1 queryout %2itemType.xls -T -c >>%3log.txt @echo itemType表备份已完成!>>%3log.txt @bcp "SELECT * FROM sportSys.dbo.agenda where sportSys.dbo.agenda.userId="+%1 queryout %2agenda.xls -T -c >>%3log.txt @echo agenda表备份已完成!>>%3log.txt @bcp "SELECT * FROM sportSys.dbo.allroundInfo where sportSys.dbo.allroundInfo.userId="+%1 queryout %2allroundInfo.xls -T -c >>%3log.txt @echo allroundInfo表备份已完成!>>%3log.txt @bcp "SELECT * FROM sportSys.dbo.athlete where sportSys.dbo.athlete.userId="+%1 queryout %2athlete.xls -T -c >>%3log.txt @echo athlete表备份已完成!>>%3log.txt @exit

  下面,我们来分析下这个批处理

  bcp "SELECT * FROM sportSys.dbo.competitions where sportSys.dbo.competitions.userId="+%1 queryout %2competitions.xls -T -c >>%3log.txt

  bcp 这个是语法

  双引号里面写的是Sql语句

  %1、%2、%3是要传递给bat文件的参数

  %1表示第一个参数,%2表示第二个参数,%3表示第三个参数。

  本人的这三个参数的意思分别是:

  第一个参数:查询条件

  第二个参数:导出文件保存的位置

  第三个参数:输出日志保存的位置

  ②Java程序中调用

  导出文件

  public String backUp() throws Exception { try { HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); Map session = ActionContext.getContext().getSession(); String userId = session.get("userId").toString(); String homeDir1 = request.getSession().getServletContext() .getRealPath("/"); //Runtime.getRuntime().exec("notepad"); Runtime.getRuntime().exec( "cmd /k start /b "+homeDir1+"backUp\\backupBy.bat" + " " + userId + " " + homeDir1+"download"+File.separatorChar + userId+File.separatorChar+ " " + homeDir1+"download"+File.separatorChar + userId+File.separatorChar); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); return INPUT; } return SUCCESS; }

  Java中调用命令行程序,的方式就是第11行

  Runtime.getRuntime().exec( "cmd /k start /b "+homeDir1+"backUp\\backupBy.bat" + " " + userId + " " + homeDir1+"download"+File.separatorChar + userId+File.separatorChar+ " " + homeDir1+"download"+File.separatorChar + userId+File.separatorChar);
sducc1120
sducc1120
sducc1120

  Runtime:

  每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime方法获取当前运行时。

  应用程序不能创建自己的 Runtime 类实例。

  public Process exec(String command) throws IOException在单独的进程中执行指定的字符串命令。 这是一个很有用的方法。对于 exec(command) 形式的调用而言,其行为与调用 exec(command, null, null) 完全相同。 参数: command - 一条指定的系统命令。 返回: 一个新的 Process 对象,用于管理子进程 抛出: SecurityException - 如果安全管理器存在,并且其 checkExec 方法不允许创建子进程 IOException - 如果发生 I/O 错误 NullPointerException - 如果 command 为 null IllegalArgumentException - 如果 command 为空 另请参见: exec(String[], String[], File), ProcessBuilder

  为了使调用bat文件来运行的时候,不至于弹出一个黑框,

  加入/b参数 即可解决!

  ③导入

  bcp sportSys.dbo.competitions in %1competitions.xls -c -t

  以上便是导入的指令! 同样也可以设置将待导入的文件全部放在目录中,然后导入即可!

  ④压缩成Zip文件-工具类(网上有写好的ZipUtil)

  package com.yaxing.util; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; publicclass ZipUtil { protectedstaticbyte[] buf = newbyte[1024]; /** * 私有构造函数防止被构建 */private ZipUtil() { } /** * 遍历目录并添加文件. * * @param jos * - JAR 输出流 * @param file * - 目录文件名 * @param pathName * - ZIP中的目录名 * @throws IOException * @throws FileNotFoundException */privatestaticvoid recurseFiles(final JarOutputStream jos, final File file, final String pathName) throws IOException, FileNotFoundException { // 文件夹则往下遍历 if (file.isDirectory()) { final String sPathName = pathName + file.getName() + "/"; jos.putNextEntry(new JarEntry(sPathName)); final String[] fileNames = file.list(); if (fileNames != null) { for (int i = 0; i < fileNames.length; i++) { recurseFiles(jos, new File(file, fileNames[i]), sPathName); } } } // 读取文件到ZIP/JAR文件条目 else { // 使用指定名称创建新的 ZIP/JAR 条目 final JarEntry jarEntry = new JarEntry(pathName + file.getName()); final FileInputStream fin = new FileInputStream(file); final BufferedInputStream in = new BufferedInputStream(fin); // 开始写入新的 ZIP 文件条目并将流定位到条目数据的开始处。 jos.putNextEntry(jarEntry); int len; while ((len = in.read(buf)) >= 0) { // 将字节数组写入当前 ZIP 条目数据 jos.write(buf, 0, len); } in.close(); // 关闭当前 ZIP 条目并定位流以写入下一个条目 jos.closeEntry(); } } /** * 创建 ZIP/JAR 文件. * * @param directory * - 要添加的目录 * @param zipFile * - 保存的 ZIP 文件名 * @param zipFolderName * - ZIP 中的路径名 * @param level * - 压缩级别(0~9) * @throws IOException * @throws FileNotFoundException */publicstaticvoid makeDirectoryToZip(final File directory, final File zipFile, final String zipFolderName, finalint level) throws IOException, FileNotFoundException { FileOutputStream fos = null; try { // 输出文件流 fos = new FileOutputStream(zipFile); } catch (final Exception e) { // 建立打包后的空文件 new File(zipFile.getParent()).mkdirs(); zipFile.createNewFile(); fos = new FileOutputStream(zipFile); } // 使用指定的 Manifest 创建新的 JarOutputStream。清单作为输出流的第一个条目被写入 final JarOutputStream jos = new JarOutputStream(fos, new Manifest()); jos.setLevel(checkZipLevel(level)); final String[] fileNames = directory.list(); if (fileNames != null) { for (int i = 0; i < fileNames.length; i++) { // 对一级目录下的所有文件或文件夹进行处理 recurseFiles(jos, new File(directory, fileNames[i]), zipFolderName == null ? "" : zipFolderName); } } // 关闭 ZIP 输出流和正在过滤的流。 jos.close(); } /** * 检查并设置有效的压缩级别,避免压缩级别设置错的异常 * * @param level * - 压缩级别 * @return 有效的压缩级别或者默认压缩级别 */publicstaticint checkZipLevel(finalint level) { if (level < 0 || level > 9) { return7; } else { return level; } } publicstaticvoid main(final String args[]) throws FileNotFoundException, IOException { // makeDirectoryToZip(); final String homeDir = System.getProperty("user.dir"); System.out.println(homeDir); final File zipFile = new File(homeDir, "download" + File.separatorChar + "test.zip"); final File pagesDirectory = new File(homeDir, "src"); System.out.println("Making zip file from folder /src to " + zipFile); ZipUtil.makeDirectoryToZip(pagesDirectory, zipFile, "", 9); System.out.println("Zip file " + zipFile + " has been made."); } }

  ⑤Struts2执行Action里面的方法

  public String execute() throws Exception { HttpServletRequest request = ServletActionContext.getRequest(); HttpServletResponse response = ServletActionContext.getResponse(); Map session = ActionContext.getContext().getSession(); String userId = session.get("userId").toString(); final String homeDir = request.getSession().getServletContext() .getRealPath("/");// 打包后文件的位置及名称 final File zipFile = new File(homeDir, "download" + File.separatorChar + userId + File.separatorChar + userId + "bak.zip");// 要打包的文件夹路径 // if((new File(userId).isDirectory())){ // System.out.println("文件夹"+userId+"已存在!创建失败!"); // }else{ // new File(userId).mkdir(); // System.out.println("创建文件夹"+userId+"成功!"); // } final File pagesDirectory = new File(homeDir, "download/" + userId); LOG.info("Making zip file from folder /" + userId + " to " + zipFile);// 压缩文件 ZipUtil.makeDirectoryToZip(pagesDirectory, zipFile, "", 9); LOG.info("Zip file " + zipFile + " has been made."); response.sendRedirect("../download/" + userId + "/" + userId + "bak.zip"); returnnull; // return super.execute(); }

  压缩文件创建之后,返回该压缩包,提供用户下载!用户将下载的文件导入客户端系统中,即可,

  四、总结

  本文总结了Java中调用批处理文件,给批处理文件传递参数的使用方法。依据此,可以实现项目中多种用途的数据备份。

  欢迎各位交流,如有更好的方法,欢迎指出,谢谢!

阅读(1458) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~