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

        近期开发的项目是一个基金管理的项目,由于交易数据比较大,为了提高响应速度,在数据访问层增加了缓存机制。但是,随着系统运行了一段时间,由于缓存加载的数据量扩大,系统内存占用越来越高。为了减轻系统压力,需要增加归档功能,即通过归档操作,将数据库里面符合特定条件(如:交易完成)的数据进行清理。但是,由于基金交易数据的特殊性,这些被清理的数据仍需要保存,而且必要的情况下还要支持数据的恢复。这篇文章就是这个功能开发完成的产物。

        最开始,由于dump文件格式的不确定,加上要用Java代码生成,我们考虑生成dump文件的内容为需要归档的数据的insert语句。首先,我们通过需要归档的表名,查询user_tab_columns可以拿到该表的所有列信息(主要是列名和数据类型),然后根据表名和列信息拼成insert语句,最后加上归档条件。根据该思路,我们有了下面的代码。

package com.code.dump;

import java.io.File;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
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 DumpFile {
	/** 数据库连接参数 */
	static final String DB_HOST = "127.0.0.1:1526";
	static final String DB_SID = "orcl";
	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";
	static Connection connection = null;

	public static void main(String[] args) {
		try {
			long start = System.currentTimeMillis();
			String sql = generateInsertSql();
			generateDumpFileByFile(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();
	}

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

		FileChannel dumpFileChannel = FileChannel.open(Paths.get(dumpFilePath), StandardOpenOption.READ, StandardOpenOption.WRITE);
		List<String> insertSqls = new ArrayList<String>();
		ResultSet results = connection.createStatement().executeQuery(sql);
		while (results.next()) {
			String insertSql = results.getString(1);
			insertSqls.add(insertSql);
		}
		System.out.println("---------------------------\n"+"rows count is :"+insertSqls.size()+"\n-----------------------------\n");
		for (String insertSql : insertSqls) {
			dumpFileChannel.write(ByteBuffer.wrap((insertSql + "\n").getBytes()));
		}

		dumpFileChannel.close();
	}

	static String getTimeStamp() {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
		return sdf.format(new Date());
	}

}

class ColumnInfo {
	private String columnName;
	private String dataType;

	public String getColumnName() {
		return columnName;
	}

	public void setColumnName(String columnName) {
		this.columnName = columnName;
	}

	public String getDataType() {
		return dataType;
	}

	public void setDataType(String dataType) {
		this.dataType = dataType;
	}

	public ColumnInfo(String columnName, String dataType) {
		super();
		this.columnName = columnName;
		this.dataType = dataType;
	}
}

        程序的输出结果:

---------------------------
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
-----------------------------

---------------------------
rows count is :23099
-----------------------------

---------------------------
all cost time is 63195 ms
-----------------------------   

dump文件的内容截取了一部分:

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('CSMS',12275,12231,'AH3811(Asset Unit)-AH3811',6,128,'AH3811',12275,'N',2785513347,1,42,'lzsysadmin','2016051807044700','xuxj194','2010102801403483','lzsysadmin','2016051807044700','分红交易-投管-PLR-场外(资产单元)-AH3811','','');
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('CSMS',12276,12231,'AH3871(Asset Unit)-AH3871',6,128,'AH3871',12276,'N',2785513353,1,42,'lzsysadmin','2016051807045600','xuxj194','2010102801403491','lzsysadmin','2016051807045600','分红交易-投管-ALR-场外(资产单元)-AH3871','','');
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('CSMS',12185,12122,'034708LAFS(Asset Unit)-AG2G41',6,128,'AG2G41',12185,'N',null,null,null,'','','xuxj194','2010102801393155','fundPostn','2016051806431500','分红-存管-LAFS-场内-深A-0899034705(资产单元)-AG2G41','','');
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('CSMS',12186,12122,'B49944LAFS(Asset Unit)-AG2G42',6,128,'AG2G42',12186,'N',null,null,null,'','','xuxj194','2010102801393160','fundPostn','2016051806425500','分红-存管-LAFS-场内-沪A-B880349945(资产单元)-AG2G42','','');
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('CSMS',12187,12122,'124224LAFS(Asset Unit)-AG2H51',6,128,'AG2H51',12187,'N',null,null,null,'','','xuxj194','2010102801393164','fundPostn','2016051806444000','分红-存管(基金)-LAFS-场内-深A-0019124224(资产单元)-AG2H51','','');
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('CSMS',12188,12122,'03470eLAFS(Asset Unit)-AG2I61',6,128,'AG2I61',12188,'N',null,null,null,'','','xuxj194','2010102801393169','fundPostn','2016051806445500','分红-存管-LAFS-场内-深A-0899034705(资产单元)-AG2I61','','');

        程序的思路很简单:先调用generateInsertSql()生成select  ‘insert into ...‘,再调用generateDumpFileByFile(sql)将上面生成的sql语句查询出来的结果存到dump文件。

        该方法完全依赖Java代码,逻辑和执行情况(如异常回滚操作)可控。但是,由于需要通过io写dump文件,效率比较慢,通过上面的输出我们看到,23099条数据总共花费了63秒多。另外,该方法由于需要拼接 insert into 语句,不支持CLOB、BLOB等特殊类型属性。

        为了提高dump文件的生成效率,我们有了Java项目生成Oracle数据库dump文件(二)的改造。

猜你喜欢

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