Java项目生成oracle数据库dump文件(二)

        Oracle数据库客户端有一个sqlplus组件,这个组件能够让我们通过命令行的方式执行一些数据库操作。为了提高dump文件的写入效率,我们尝试使用sqlplus命令来生成dump文件。

        sqlplus可以指定运行脚本,例如:

sqlplus admin/pwd@//127.0.0.1:1526/orcl@./tmp_sql/NODE_dump_sql_20180516130228316.sql

        我们在这个脚本里面可以通过spool命令打开一个文件,然后将sql查询出来的结果写入这个文件。

        具体代码如下:

package com.code.dump;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class DumpFile2 {
	/** 数据库连接参数 */
	static final String DB_HOST = "127.0.0.1:1526";
	static final String DB_SID = "oracl";
	static final String DB_USER = "admin";
	static final String DB_PWD = "pwd";
	static final String TABLE_NAME = "NODE";
	/** 拼接的sql每行字符数,超过ROW_CHAR_COUNT就换行,防止sql过长 */
	static final int ROW_CHAR_COUNT = 1000;
	/** dump文件保存路径 */
	static final String DUMP_FILE_PATH = "./dump_file";
	/** sqlPlus执行脚本存放路径 */
	static final String SQL_PLUS_FILE_PATH = "./tmp_sql";
	static Connection connection = null;

	public static void main(String[] args) {
		try {
			long start = System.currentTimeMillis();
			String sql = generateInsertSql();
			generateDumpFileBySqlPlus(sql);
			System.out.println("---------------------------\n"+"all cost time is " + (System.currentTimeMillis() - start) + " ms"+"\n-----------------------------\n");
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (connection != null) {
				try {
					connection.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/** 生成使用的sql语句 */
	static String generateInsertSql() throws Exception {
		// 存储需要备份的表的列的信息(column_name,data_type)
		List<ColumnInfo> cols = new ArrayList<ColumnInfo>();
		// 创建oracle数据库连接
		Class.forName("oracle.jdbc.driver.OracleDriver");
		connection = DriverManager.getConnection("jdbc:oracle:thin:@" + DB_HOST + ":" + DB_SID, DB_USER, DB_PWD);
		Statement sql = connection.createStatement();

		// 查询需备份表的所有列信息
		ResultSet columns = sql.executeQuery("select column_name, data_type from user_tab_columns where table_name='" + TABLE_NAME.toUpperCase() + "'");
		while (columns.next()) {
			ColumnInfo columnInfo = new ColumnInfo(columns.getString(1), columns.getString(2));
			cols.add(columnInfo);
		}

		// 拼接insert的前半部分
		// eg:insert into charge_value_lookup(RESULT_ID,TAX_IDS,FEE_IDS) values(
		StringBuffer insertSql = new StringBuffer("insert into " + TABLE_NAME + "(");
		int index = 1;
		for (ColumnInfo col : cols) {
			if (insertSql.length() > ROW_CHAR_COUNT * index) {
				insertSql.append("\n");
				index++;
			}
			insertSql.append(col.getColumnName()).append(",");
		}
		insertSql.delete(insertSql.length() - 1, insertSql.length());
		insertSql.append(") values(");
		System.out.println("---------------------------\n"+insertSql.toString()+"\n-----------------------------\n");

		// 拼接sql调用的查询语句
		// eg:select 'insert into charge_value_lookup(RESULT_ID,TAX_IDS,FEE_IDS) values(' ||''''||RESULT_ID||''''||','||''''||TAX_IDS||''''||','||''''||FEE_IDS||''''||');' from charge_value_lookup
		StringBuffer excSql = new StringBuffer("select '").append(insertSql).append("' ");
		index = 1;
		for (int i = 0; i < cols.size(); i++) {
			if (excSql.length() > ROW_CHAR_COUNT * index) {
				excSql.append("\n");
				index++;
			}
			ColumnInfo columnInfo = cols.get(i);
			switch (columnInfo.getDataType()) {
			case "CHAR":
			case "VARCHAR2":
				excSql.append("||").append("''''").append("||").append(columnInfo.getColumnName()).append("||").append("''''");
				break;
			case "NUMBER":
			case "FLOAT":
			case "LONG":
				excSql.append("||").append("nvl(to_char(" + columnInfo.getColumnName() + ")," + columnInfo.getColumnName() + "||'null')");
				break;
			case "DATE":
				break;
			default:
				throw new Exception("表的列类型不支持");
			}
			if (i != cols.size() - 1) {
				excSql.append("||").append("','");
			} else {
				excSql.append("||');'");
			}
		}
		excSql.append(" from " + TABLE_NAME + "");

		System.out.println("---------------------------\n"+excSql.toString()+"\n-----------------------------\n");


		return excSql.toString();
	}

	/** 通过sqlPlus操作生成dump文件 */
	private static void generateDumpFileBySqlPlus(String sql) throws Exception {
		// 创建文件存储目录
		File directory = new File(DUMP_FILE_PATH);
		if (!directory.exists()) {
			directory.mkdirs();
		}
		directory = new File(SQL_PLUS_FILE_PATH);
		if (!directory.exists()) {
			directory.mkdirs();
		}
		// sqlPlus调用的sql脚本
		String genSqlPlusFilePath = SQL_PLUS_FILE_PATH + "/" + TABLE_NAME + "_dump_sql_" + getTimeStamp() + ".sql";
		if (!Files.exists(Paths.get(genSqlPlusFilePath))) {
			Files.createFile(Paths.get(genSqlPlusFilePath));
		}
		// 生成的dump文件
		String dumpFilePath = DUMP_FILE_PATH + "/" + TABLE_NAME + "_dump_" + getTimeStamp() + ".dump";
		if (!Files.exists(Paths.get(dumpFilePath))) {
			Files.createFile(Paths.get(dumpFilePath));
		}

		// sqlPlus 命令头
		final String[] headers = new String[] { "set echo off;", "set feedback off;", "set term off;", "set heading off;", "set sqlblanklines off;",
				"set linesize 5000;", "spool " + dumpFilePath + ";" };

		final String[] tails = new String[] { "spool off;", "exit;" };

		// 创建sqlplus可执行的sql文件
		FileChannel fileChannel = FileChannel.open(Paths.get(genSqlPlusFilePath), StandardOpenOption.READ, StandardOpenOption.WRITE);
		for (String header : headers) {
			fileChannel.write(ByteBuffer.wrap((header + "\n").getBytes()));
		}
		fileChannel.write(ByteBuffer.wrap((sql + ";\n").getBytes()));
		for (String tail : tails) {
			fileChannel.write(ByteBuffer.wrap((tail + "\n").getBytes()));
		}
		fileChannel.close();

		String sqlPlusCmd = "sqlplus " + DB_USER + "/" + DB_PWD + "@//" + DB_HOST + "/" + DB_SID + " @" + genSqlPlusFilePath;
		System.out.println(sqlPlusCmd);
		Process process = Runtime.getRuntime().exec(sqlPlusCmd);
		process.waitFor();
		InputStream is = process.getInputStream();
		printCmdOutMsg(is);
		
	}

	static String getTimeStamp() {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
		return sdf.format(new Date());
	}
	
	static void printCmdOutMsg(InputStream is) throws IOException {
		System.out.println("----------------start----------------------");
		BufferedReader reader = new BufferedReader(new InputStreamReader(is, Charset.forName("gbk")));
		String out = reader.readLine();
		while (out != null) {
			System.out.println(out);
			out = reader.readLine();
		}
		System.out.println("----------------end----------------------");
	}

}

        程序的输出结果:

---------------------------
insert into NODE(TENANT_ID,NODE_ID,PARENT_NODE_ID,NAME,NODE_TYPE_ID,HIERARCHY_TYPE_ID,ACCOUNT_ID,SHORTCUT_NODE_ID,SHORTCUT,CHECK_ID,CHECK_STATUS,SCHEDULE_ID,CHECKED_BY,CHECKED_DATE,ADDED_BY,ADDED_DATE,UPDATED_BY,UPDATED_DATE,NAME_1,NAME_2,NAME_3) values(
-----------------------------

---------------------------
select 'insert into NODE(TENANT_ID,NODE_ID,PARENT_NODE_ID,NAME,NODE_TYPE_ID,HIERARCHY_TYPE_ID,ACCOUNT_ID,SHORTCUT_NODE_ID,SHORTCUT,CHECK_ID,CHECK_STATUS,SCHEDULE_ID,CHECKED_BY,CHECKED_DATE,ADDED_BY,ADDED_DATE,UPDATED_BY,UPDATED_DATE,NAME_1,NAME_2,NAME_3) values(' ||''''||TENANT_ID||''''||','||nvl(to_char(NODE_ID),NODE_ID||'null')||','||nvl(to_char(PARENT_NODE_ID),PARENT_NODE_ID||'null')||','||''''||NAME||''''||','||nvl(to_char(NODE_TYPE_ID),NODE_TYPE_ID||'null')||','||nvl(to_char(HIERARCHY_TYPE_ID),HIERARCHY_TYPE_ID||'null')||','||''''||ACCOUNT_ID||''''||','||nvl(to_char(SHORTCUT_NODE_ID),SHORTCUT_NODE_ID||'null')||','||''''||SHORTCUT||''''||','||nvl(to_char(CHECK_ID),CHECK_ID||'null')||','||nvl(to_char(CHECK_STATUS),CHECK_STATUS||'null')||','||nvl(to_char(SCHEDULE_ID),SCHEDULE_ID||'null')||','||''''||CHECKED_BY||''''||','||''''||CHECKED_DATE||''''||','||''''||ADDED_BY||''''||','||''''||ADDED_DATE||''''||','||''''||UPDATED_BY||''''||','||''''||UPDATED_DATE||''''||','||''''||NAME_1||''''||','
||''''||NAME_2||''''||','||''''||NAME_3||''''||');' from NODE
-----------------------------

sqlplus admin/pwd@//127.0.0.1:1526/orcl@./tmp_sql/NODE_dump_sql_20180516130228316.sql
----------------start----------------------

SQL*Plus: Release 11.2.0.1.0 Production on 星期三 5月 16 13:02:32 2018

Copyright (c) 1982, 2010, Oracle.  All rights reserved.


连接到: 
Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Data Mining and Real Application Testing options

从 Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Data Mining and Real Application Testing options 断开
----------------end----------------------
---------------------------
all cost time is 57403 ms
-----------------------------

        dump文件内容跟Java项目生成Oracle数据库dump文件(一)里面的dump文件一样。

        在这个实现方法里面,我们首先也是拼出里select  'insert into...'语句,然后我们将这个sql语句和一些sqlplus的命令头写入了tmp_sql下面的一个临时文件,这个文件的内容如下:

set echo off;
set feedback off;
set term off;
set heading off;
set sqlblanklines off;
set linesize 5000;
spool ./dump_file/NODE_dump_20180516130228320.dump;
select 'insert into NODE(TENANT_ID,NODE_ID,PARENT_NODE_ID,NAME,NODE_TYPE_ID,HIERARCHY_TYPE_ID,ACCOUNT_ID,SHORTCUT_NODE_ID,SHORTCUT,CHECK_ID,CHECK_STATUS,SCHEDULE_ID,CHECKED_BY,CHECKED_DATE,ADDED_BY,ADDED_DATE,UPDATED_BY,UPDATED_DATE,NAME_1,NAME_2,NAME_3) values(' ||''''||TENANT_ID||''''||','||nvl(to_char(NODE_ID),NODE_ID||'null')||','||nvl(to_char(PARENT_NODE_ID),PARENT_NODE_ID||'null')||','||''''||NAME||''''||','||nvl(to_char(NODE_TYPE_ID),NODE_TYPE_ID||'null')||','||nvl(to_char(HIERARCHY_TYPE_ID),HIERARCHY_TYPE_ID||'null')||','||''''||ACCOUNT_ID||''''||','||nvl(to_char(SHORTCUT_NODE_ID),SHORTCUT_NODE_ID||'null')||','||''''||SHORTCUT||''''||','||nvl(to_char(CHECK_ID),CHECK_ID||'null')||','||nvl(to_char(CHECK_STATUS),CHECK_STATUS||'null')||','||nvl(to_char(SCHEDULE_ID),SCHEDULE_ID||'null')||','||''''||CHECKED_BY||''''||','||''''||CHECKED_DATE||''''||','||''''||ADDED_BY||''''||','||''''||ADDED_DATE||''''||','||''''||UPDATED_BY||''''||','||''''||UPDATED_DATE||''''||','||''''||NAME_1||''''||','
||''''||NAME_2||''''||','||''''||NAME_3||''''||');' from NODE;
spool off;
exit;

        最后,我们通过 Runtime.getRuntime().exec方法调用sqlplus命令,启动一个进程去执行这个脚本,得到我们的dump文件。

        我们通过输出结果看到,对于同一个表的dump文件生成,这个方法的耗时为57秒,比之前的63秒快了6秒,速度提高的并不明显。

        而且,该方法需要在服务器安装sqlplus工具,由于是通过Runtime启动进程来生成dump文件,异常的回滚操作也不太好处理。

        后来经过确认,我们需要生成的dump文件其实并不是我们这里自己生成的insert的文本文件,而是Oracle数据库导入/导出数据的一种文件格式。因此,有了下面的方案,Java项目生成oracle数据库dump文件(三)

猜你喜欢

转载自blog.csdn.net/u014730001/article/details/80335829