All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ru.abyss.settings.exporter.DBExporter Maven / Gradle / Ivy

/*
 * Copyright Бездна (c) 2018.
 */
package ru.abyss.settings.exporter;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;

import java.net.URL;

import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.stream.Collectors;

import org.h2.Driver;

import org.postgresql.util.PGobject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.sf.sevenzipjbinding.IOutCreateArchive7z;
import net.sf.sevenzipjbinding.IOutCreateCallback;
import net.sf.sevenzipjbinding.IOutItem7z;
import net.sf.sevenzipjbinding.ISequentialInStream;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.impl.OutItemFactory;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.impl.RandomAccessFileOutStream;

import ru.abyss.settings.ProgressCallback;

/**
 * @author Minu <[email protected]>
 * @since 18.05.2018 11:19:57
 */
public class DBExporter {

	private static Logger logger = LoggerFactory.getLogger(DBExporter.class);

	private static final transient String ID = "id";
	private static final transient String DEL = "del";
	private static final transient String SLAVE_ID = "slave_id";
	private static final transient String DATE_CREATE = "date_create";
	private static final transient String DATE_UPDATE = "date_update";
	private static final transient String LAST_TUSER_ID = "last_tuser_id";
	private static final transient String TFACTORY_ID = "tfactory_id";
	private static final transient String TWORK_NUM_ID = "twork_num_id";

	private static final List EXPORT_TABLES = new ArrayList() {
		private static final long serialVersionUID = 1473786915022151125L;
	{
		add("accessories.taccessories");
		add("accessories.talways");
		add("accessories.tapp_types");
		add("accessories.tcalculated");
		add("accessories.tcells");
		add("accessories.tcells_always");
		add("accessories.tcells_calculated");
		add("accessories.tgroups");
		add("accessories.tlocations");
		add("accessories.tranges");
		add("accessories.tranges_calculated");
		add("accessories.ttypes");
		add("catalog.taccount_numbers");
		add("catalog.tcalc_types");
		add("catalog.tcertificate_product_types");
		add("catalog.tdocuments");
		add("catalog.tpayments");
		add("catalog.tproduct_types");
		add("catalog.tservices");
		add("scheduling.tstage_properties");
		add("scheduling.tstage_schedules");
		add("config.tcurrency_names");
		add("contractors.taddresses");
		add("contractors.tbanks");
		add("contractors.tcategories");
		add("contractors.tcustomers");
		add("contractors.tdirections");
		add("contractors.tdocuments");
		add("contractors.tnotes");
		add("contractors.tsupplier_prices");
		add("contractors.tsuppliers");
		add("factory.tscript_materials");
		add("logistics.tbrigades");
		add("logistics.tdirection_timetables");
		add("logistics.tdirections");
		add("logistics.tdistances");
		add("logistics.tmeasurers");
		add("logistics.tplatforms");
		add("logistics.tregions");
		add("logistics.troutes");
		add("logistics.ttransport_marks");
		add("logistics.ttransports");
		add("orders.tdaytime_task");
		add("orders.tmarker_types");
		add("settings.taccessories");
		add("settings.taccessory_connection_catalog_names");
		add("settings.taccessory_connection_elements");
		add("settings.taccessory_connection_groups");
		add("settings.taccessory_connection_types");
		add("settings.taccessory_groups");
		add("settings.taccessory_subgroups");
		add("settings.tcatalog_changes");
		add("settings.tcatalog_elements");
		add("settings.tcatalog_names");
		add("settings.tchanges");
		add("settings.tclearance_groups");
		add("settings.tcorrection_groups");
		add("settings.tcnc");
		add("settings.tcnc_codes");
		add("settings.tcomment_elements");
		add("settings.tcomment_notes");
		add("settings.tcomments");
		add("settings.tconnections");
		add("settings.tconnection_elements");
		add("settings.tconstruct_elements");
		add("settings.tconstruct_types");
		add("settings.tconstructs");
		add("settings.telements");
		add("settings.tfindings");
		add("settings.tlimits");
		add("settings.tmaterials");
		add("settings.tparents");
		add("settings.tprice_lists");
		add("settings.tprices");
		add("settings.tprocessing_cnc");
		add("settings.tproperties_arc_frames");
		add("settings.tproperties_glazings");
		add("settings.tproperties_imposts");
		add("settings.tproperties_layouts");
		add("settings.tproperties_leafs");
		add("settings.tproperties_moreovers");
		add("settings.tproperties_rect_frames");
		add("settings.tproperties_round_frames");
		add("settings.ttechnologies");
		add("stock.talternative_articles");
		add("stock.tcolor_bindings");
		add("stock.tcolor_binding_names");
		add("stock.tcolor_change_groups");
		add("stock.tcolor_groups");
		add("stock.tcolor_groups_ext");
		add("stock.tcolor_names");
		add("stock.tcolor_standarts");
		add("stock.tcolors");
		add("stock.tconnection_groups");
		add("stock.tconsist_remains");
		add("stock.tgroups");
		add("stock.tmaterial_type_groups");
		add("stock.tmaterial_types");
		add("stock.tmaterials");
		add("stock.tproperties");
		add("stock.tscript_steps");
		add("stock.tscripts");
		add("stock.ttask_cnc");
		add("stock.tcnc_macros");
		add("stock.ttask_finished_goods");
		add("stock.ttasks");
		add("stock.ttechnologies");
		add("stock.tunits");
	}};

	private static void writeVersion(Connection h2Connect, Long factoryId) throws Exception {
		Properties prop = new Properties();
		Enumeration systemResources = DBExporter.class.getClassLoader().getResources("release.properties");
    	logger.debug("search config file 'release.properties'");
	    while (systemResources.hasMoreElements()) {
	    	URL url = systemResources.nextElement();
	    	logger.debug(url.getPath());
	    	if (url.getPath().contains("abyss-export")) {
	    		try (InputStream is = url.openStream()) {
	    			prop.load(is);
	    		}
	    		break;
	    	}
	    }
	    if (prop.isEmpty())
	    	throw new Exception("Не найден файл конфигурации выгрузки");

		try (Statement st = h2Connect.createStatement()) {
			st.addBatch("CREATE TABLE version (num BIGINT, rev BIGINT, tfactory_id BIGINT, export_date TIMESTAMP default now())");
			st.addBatch("INSERT INTO version (num, rev, tfactory_id) VALUES ("
					+ prop.getProperty("build.number") + ", "
					+ prop.getProperty("build.revision") + ","
					+ factoryId + ")");
			st.executeBatch();
			h2Connect.commit();
		} catch (Exception e) {
			h2Connect.rollback();
			throw e;
		}
	}

	private static String castType(String pgType) throws Exception {
		switch (pgType.toLowerCase().replace("\"", "")) {
		case "int8":
		case "bigserial":
			return "bigint";
		case "bool":
			return "boolean";
		case "timestamp":
			return "timestamp";
		case "float8":
			return "double precision";
		case "varchar":
		case "accessories.calc_type":
		case "accessories.calc_side":
		case "catalog.element_type_group":
		case "catalog.delivery_type":
		case "catalog.operation_type":
		case "catalog.stage_type":
		case "catalog.start_type":
		case "catalog.day_type":
		case "contractors.contractor_type":
		case "contractors.legal_type":
		case "factory.saw_type":
		case "settings.angle_type":
		case "settings.arc_type":
		case "settings.arc_def_type":
		case "settings.geometry_type":
		case "settings.orientation":
		case "settings.limit_type":
		case "settings.mark_type":
		case "settings.open_type":
		case "settings.price_calc_type":
		case "settings.price_unit_type":
		case "settings.side_type":
		case "stock.debit_type":
		case "stock.currency_type":
		case "stock.place_type":
		case "stock.price_type":
		case "stock.rate_type":
		case "stock.report_type":
		case "stock.report_calc_type":
		case "stock.report_elements_type":
		case "stock.side_type":
		case "stock.task_type":
			return "varchar";
		default:
			throw new Exception("Неизвестный тип: " + pgType);
		}
	}

	private static List createH2Table(Connection pgConnect, Connection h2Connect, String schemaName, String tableName) throws Exception {
		List types = new ArrayList<>();
		List columns = new ArrayList<>();
		StringBuilder sql = new StringBuilder("create table ").append(schemaName).append(".").append(tableName).append( " (");
        DatabaseMetaData metaData = pgConnect.getMetaData();
        String pkey = getPKey(pgConnect, schemaName, tableName);
        try (ResultSet rs = metaData.getColumns(null, schemaName, tableName, null)) {
        	while (rs.next()) {
            	String colName = rs.getString("COLUMN_NAME");
            	if (!DEL.equals(colName) && !DATE_CREATE.equals(colName) 
						&& !DATE_UPDATE.equals(colName) && !LAST_TUSER_ID.equals(colName) 
						&& !TFACTORY_ID.equals(colName) && !SLAVE_ID.equals(colName)
						&& !TWORK_NUM_ID.equals(colName)) {
            		String typeName = rs.getString("TYPE_NAME");
                	columns.add(colName);
	            	types.add(typeName);
	            	boolean isNullable = "YES".equalsIgnoreCase(rs.getString("IS_NULLABLE")); 
	            	if ("SETTINGS".equalsIgnoreCase(schemaName) && "TCNC".equalsIgnoreCase(tableName) && "interval".equalsIgnoreCase(colName))
	            		sql.append("\"").append(colName.toUpperCase()).append("\"");
	            	else
	            		sql.append(colName);
	            	sql.append(" ").append(castType(typeName)).append((isNullable ? "" : " not null")).append(", ");
            	}
            }
        	if ("SETTINGS".equalsIgnoreCase(schemaName) && "TELEMENTS".equalsIgnoreCase(tableName)) {
        		String colName = "element_type_npp";
        		String typeName = "int8";
        		columns.add(colName);
            	types.add(typeName);
            	sql.append(colName).append(" ").append(castType(typeName)).append(", ");
        	}
            sql.append("PRIMARY KEY(").append(pkey).append("))");
        }

        try (Statement st = h2Connect.createStatement()) {
        	logger.debug("create table " + schemaName + "." + tableName);
			st.addBatch("CREATE SCHEMA IF NOT EXISTS " + schemaName);
			st.addBatch(sql.toString());
			if (!ID.equals(pkey))
				st.addBatch("CREATE UNIQUE INDEX ON " + schemaName + "." + tableName + " (" + ID + ")");
			for (int i = 0; i < types.size(); i++)
				if ("SETTINGS".equalsIgnoreCase(schemaName) && "TCNC".equalsIgnoreCase(tableName) && "interval".equalsIgnoreCase(columns.get(i)))
					st.addBatch("COMMENT ON COLUMN " + schemaName + "." + tableName + ".\"" + columns.get(i).toUpperCase() + "\" IS '" + types.get(i) + "'");
				else
					st.addBatch("COMMENT ON COLUMN " + schemaName + "." + tableName + "." + columns.get(i) + " IS '" + types.get(i) + "'");
			st.executeBatch();
			h2Connect.commit();
		} catch (Exception e) {
			h2Connect.rollback();
			throw e;
		}

		return columns;
	}

	private static String castRule(String actionType, int rule) {
		switch (rule) {
		case DatabaseMetaData.importedKeyCascade:
			return actionType + " CASCADE";
		case DatabaseMetaData.importedKeySetNull:
			return actionType + " SET NULL";
		default:
			return "";
		}
	}

	private static void createFKey(Statement st, String fkName, String pkTableName, String fkTableName, String pkColumnName, String fkColumnName, int updRule, int delRule) throws Exception {
		if (delRule == DatabaseMetaData.importedKeyCascade)
    		st.addBatch("delete from " + fkTableName + " fk where fk."
    				+ fkColumnName + " is not null and not exists(select 1 from " + pkTableName + " pk where pk." + pkColumnName + " = fk." + fkColumnName + ")");
    	if (delRule == DatabaseMetaData.importedKeySetNull)
    		st.addBatch("update " + fkTableName + " fk set " + fkColumnName + " = null where fk."
    				+ fkColumnName + " is not null and not exists(select 1 from " + pkTableName + " pk where pk." + pkColumnName + " = fk." + fkColumnName + ")");

    	String sql = "ALTER TABLE " + fkTableName + " ADD CONSTRAINT " + fkName
    		+ " FOREIGN KEY (" + fkColumnName + ") REFERENCES " + pkTableName + "(" + pkColumnName + ") "
    		+ castRule("ON DELETE", delRule) + " " + castRule("ON UPDATE", updRule);
    	st.addBatch(sql);
	}

	private static int createFKey(Connection pgConnect, Connection h2Connect, String schemaName, String tableName) throws Exception {
		int ret = 0;
		DatabaseMetaData metaData = pgConnect.getMetaData();
	    try (ResultSet rs = metaData.getImportedKeys(null, schemaName, tableName);
	    		Statement st = h2Connect.createStatement()) {
	    	logger.debug("create foreign keys for " + schemaName + "." + tableName);
			while (rs.next()) {
            	String pkTableName = rs.getString("pktable_schem") + "." + rs.getString("pktable_name");
            	if (!EXPORT_TABLES.contains(pkTableName)) {			// проверяем есть ли связанная таблица среди экспортируемых 
            		if (!"core.tfactories".equalsIgnoreCase(pkTableName)	// если нет и это не глобальные справочники, то ругаемся
            				&& !"catalog.telement_types".equalsIgnoreCase(pkTableName)
            				&& !"logistics.twork_nums".equalsIgnoreCase(pkTableName))
            			throw new Exception(pkTableName + " not in export set");
					continue;
            	}
            	String fkTableName = rs.getString("fktable_schem") + "." + rs.getString("fktable_name");

            	createFKey(st, rs.getString("fk_name"), pkTableName, fkTableName, rs.getString("pkcolumn_name"),
            			rs.getString("fkcolumn_name"), rs.getInt("update_rule"), rs.getInt("delete_rule"));
            	ret++;
            }
			if ("settings".equalsIgnoreCase(schemaName) && "tparents".equalsIgnoreCase(tableName))
				st.addBatch("delete from settings.tparents fk where fk.parent_id is null or not (exists(select 1 from settings.telements pk where pk.id = fk.parent_id) "
						+ "or exists(select 1 from settings.tcatalog_names pk where pk.id = fk.parent_id))");
			if ("stock".equalsIgnoreCase(schemaName) && "tproperties".equalsIgnoreCase(tableName)) {
            	createFKey(st, "tproperties_outgo_supplier_fk", "contractors.tsuppliers", "stock.tproperties", ID,
            			"outgo_tsupplier_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
            	createFKey(st, "tproperties_outgo_recipient_fk", "contractors.tsuppliers", "stock.tproperties", ID,
            			"outgo_trecipient_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
            	createFKey(st, "tproperties_outgo_recycler_fk", "contractors.tsuppliers", "stock.tproperties", ID,
            			"outgo_trecycler_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
            	createFKey(st, "tproperties_blank_supplier_fk", "contractors.tsuppliers", "stock.tproperties", ID,
            			"blank_tsupplier_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
            	createFKey(st, "tproperties_incoming_recipient_fk", "contractors.tsuppliers", "stock.tproperties", ID,
            			"incoming_trecipient_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
            	createFKey(st, "tproperties_write_off_supplier_fk", "contractors.tsuppliers", "stock.tproperties", ID,
            			"write_off_tsupplier_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
            	createFKey(st, "tproperties_write_off_recipient_fk", "contractors.tsuppliers", "stock.tproperties", ID,
            			"write_off_trecipient_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
            	ret += 7;
			}
			if ("contractors".equalsIgnoreCase(schemaName)) {
				if ("tsupplier_prices".equalsIgnoreCase(tableName)) {
	            	createFKey(st, "tsupplier_prices_supplier_fk", "contractors.tsuppliers", "contractors.tsupplier_prices", ID,
	            			"tsupplier_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeyCascade);
	            	ret++;
				}
				if ("taddresses".equalsIgnoreCase(tableName)) {
					st.addBatch("delete from contractors.taddresses a "
							+ "where not exists(select 1 from contractors.tcustomers c where c.id = a.tcounterparty_id) and "
							+ "not exists(select 1 from contractors.tsuppliers s where s.id = a.tcounterparty_id)");
	            	ret += 2;
				}
				if ("tdirections".equalsIgnoreCase(tableName)) {
					st.addBatch("delete from contractors.tdirections d "
							+ "where not exists(select 1 from contractors.tcustomers c where c.id = d.tcounterparty_id) and "
							+ "not exists(select 1 from contractors.tsuppliers s where s.id = d.tcounterparty_id)");
	            	ret += 2;
				}
			}
			if ("accessories".equalsIgnoreCase(schemaName)) {
				if ("talways".equalsIgnoreCase(tableName)) {
	            	createFKey(st, "talways_recipient_fk", "contractors.tsuppliers", "accessories.talways", ID,
	            			"trecipient_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
	            	ret++;
				}
				if ("tcalculated".equalsIgnoreCase(tableName)) {
	            	createFKey(st, "tcalculated_recipient_fk", "contractors.tsuppliers", "accessories.tcalculated", ID,
	            			"trecipient_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
	            	ret++;
				}
				if ("tcells_always".equalsIgnoreCase(tableName)) {
	            	createFKey(st, "tcells_always_recipient_fk", "contractors.tsuppliers", "accessories.tcells_always", ID,
	            			"trecipient_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
	            	ret++;
				}
				if ("tcells_calculated".equalsIgnoreCase(tableName)) {
	            	createFKey(st, "tcells_calculated_recipient_fk", "contractors.tsuppliers", "accessories.tcells_calculated", ID,
	            			"trecipient_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
	            	ret++;
				}
				if ("tgroups".equalsIgnoreCase(tableName)) {
	            	createFKey(st, "tgroups_recipient_fk", "contractors.tsuppliers", "accessories.tgroups", ID,
	            			"trecipient_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
	            	ret++;
				}
				if ("tranges_calculated".equalsIgnoreCase(tableName)) {
	            	createFKey(st, "tranges_calculated_recipient_fk", "contractors.tsuppliers", "accessories.tranges_calculated", ID,
	            			"trecipient_id", DatabaseMetaData.importedKeyCascade, DatabaseMetaData.importedKeySetNull);
	            	ret++;
				}
			}
			st.executeBatch();
			pgConnect.commit();
			h2Connect.commit();
		} catch (Exception e) {
			pgConnect.rollback();
			h2Connect.rollback();
			throw e;
	    } finally {
        	logger.debug(ret + " foreign keys created " + schemaName + "." + tableName);
		}
	    return ret;
	}

	// metaData.getIndexInfo не работает в драйвере 9.4.1208, эта процедура заменяется его
	private static String getPKey(Connection pgConnect, String schemaName, String tableName) throws Exception {
		String sql = "SELECT string_agg(replace(trim(both '\"' from pg_catalog.pg_get_indexdef(ci.oid, (i.keys).n, false)), ?, ?), ', ' order by (i.keys).n)\n"
					+ "FROM pg_catalog.pg_class ct\n"
						+ "JOIN pg_catalog.pg_namespace n ON ct.relnamespace = n.oid\n"
						+ "JOIN (SELECT i.indexrelid, i.indrelid, i.indisunique, information_schema._pg_expandarray(i.indkey) AS keys FROM pg_catalog.pg_index i) i ON (ct.oid = i.indrelid)\n"
						+ "JOIN pg_catalog.pg_class ci ON ci.oid = i.indexrelid\n"
					+ "WHERE n.nspname = ? AND ct.relname = ? AND i.indisunique and ci.relname ~* 'slave_uniq'\n"
						+ "and trim(both '\"' from pg_catalog.pg_get_indexdef(ci.oid, (i.keys).n, false)) <> ?";
		try (PreparedStatement st = pgConnect.prepareStatement(sql)) {
	    	st.setString(1, SLAVE_ID);
	    	st.setString(2, ID);
	    	st.setString(3, schemaName);
	    	st.setString(4, tableName);
	    	st.setString(5, TFACTORY_ID);
	    	try (ResultSet rs = st.executeQuery()) {
	    		if (rs.next() && (rs.getString(1) != null))
	    			return rs.getString(1);
				throw new Exception("Не найден primary key у таблицы " + schemaName + "." + tableName);
	    	}
	    }
	}

	private static void exportTable(Connection pgConnect, Connection h2Connect, String schemaName, String tableName, Long factoryId) throws Exception {
		StringBuilder sql;
		if ("SETTINGS".equalsIgnoreCase(schemaName) && "TELEMENTS".equalsIgnoreCase(tableName))
			sql = new StringBuilder().append("select e.*, et.npp as element_type_npp from SETTINGS.TELEMENTS e left join catalog.element_types et on et.id = e.telement_type_id where e.").append(TFACTORY_ID).append("=? and not e.").append(DEL);
		else
			sql = new StringBuilder().append("select * from only ").append(schemaName + "." + tableName).append(" where ").append(TFACTORY_ID).append("=? and not ").append(DEL);
		try (PreparedStatement stmt = pgConnect.prepareStatement(sql.toString())) {
			stmt.setObject(1, factoryId);
			stmt.setFetchDirection(ResultSet.FETCH_FORWARD);
			stmt.setFetchSize(128);
			stmt.execute();
			try (ResultSet rset = stmt.getResultSet()) {
				List cols = createH2Table(pgConnect, h2Connect, schemaName, tableName);
				try (PreparedStatement ps = h2Connect.prepareStatement("insert into " + schemaName + "." + tableName
						+ " values (" + cols.stream().map(c -> "?").collect(Collectors.joining(", ")) + ")")) {
					int count = 0;
					final int batchSize = 1000;
		        	logger.debug("insert into " + schemaName + "." + tableName);
					while (rset.next()) {
						for (int i = 1; i <= cols.size(); i++) {
							Object val = rset.getObject(cols.get(i - 1));
							if (val instanceof PGobject)
								val = val.toString();
							ps.setObject(i, val);
						}
						ps.addBatch();

						if (++count % batchSize == 0)
							ps.executeBatch();
					}
					ps.executeBatch();	// добиваем оставшиеся записи
					h2Connect.commit();
		        	logger.debug("insert complete " + schemaName + "." + tableName);
				} catch (Exception e) {
					h2Connect.rollback();
					throw e;
				}
			}
			pgConnect.commit();
		} catch (Exception e) {
			pgConnect.rollback();
			throw e;
		}
	}

	public static File export(Connection connection, Properties props, Long factoryId, ProgressCallback callback) throws Exception {
		Driver.load();
		Path tmpDb = Files.createTempDirectory("abyss_export_");
		try {
			try (Connection h2 = DriverManager.getConnection("jdbc:h2:" + tmpDb + "/abyss;TRACE_LEVEL_FILE=4;MAX_COMPACT_TIME=-1",
					props.getProperty("export.h2.login"), props.getProperty("export.h2.password"))) {
				h2.setAutoCommit(false);
				writeVersion(h2, factoryId);

				long done = 1;
				long total = EXPORT_TABLES.size() + 1;
				if (callback != null)
					callback.setProgress("Извлечение данных", done, total);
				// создаём и заполняем таблицы
				for (String table : EXPORT_TABLES) {
					String[] t = table.split("\\.");
					exportTable(connection, h2, t[0], t[1], factoryId);
					if (callback != null)
						callback.setProgress("Извлечение данных", ++done, total);
				}

				// навешиваем внешние ключи на таблицы
				int cnt = 0;
				done = 1;
				for (String table : EXPORT_TABLES) {
					String[] t = table.split("\\.");
					cnt += createFKey(connection, h2, t[0], t[1]);
					if (callback != null)
						callback.setProgress("Создание связей", ++done, total);
				}
				logger.debug(cnt + " foreign keys created");
			}

			logger.debug("compress results");
			try (IOutCreateArchive7z arch = SevenZip.openOutArchive7z()) {
				arch.setLevel(9);
				arch.setThreadCount(2);
				List files = new ArrayList<>();
				try (DirectoryStream fls = Files.newDirectoryStream(tmpDb)) {
					for (Path file : fls)
						files.add(file.toFile());
				}
				File exportArch = File.createTempFile("abyss_export_", ".tmp");
				try (RandomAccessFile out = new RandomAccessFile(exportArch, "rw")) {
					arch.createArchive(new RandomAccessFileOutStream(out), files.size(), new IOutCreateCallback() {
						private long total = 0;
						private long perc = -1;

						@Override
						public void setTotal(long total) throws SevenZipException {
							this.total = total;
						}

						@Override
						public void setCompleted(long complete) throws SevenZipException {
							long lastPerc = Math.round(100.0 * complete / total);
							if (lastPerc != perc) {
								perc = lastPerc;
								logger.debug("compression: " + perc + "%");
								if (callback != null)
									callback.setProgress("Сжатие базы данных", complete, total);
							}
						}

						@Override
						public void setOperationResult(boolean operationResultOk) throws SevenZipException {
							logger.debug(operationResultOk ? "export done" : "compression error");
						}

						@Override
						public IOutItem7z getItemInformation(int index, OutItemFactory outItemFactory)
								throws SevenZipException {
							IOutItem7z outItem = outItemFactory.createOutItem(); 

						    outItem.setDataSize(files.get(index).length());
						    outItem.setPropertyPath(files.get(index).getName());

						    return outItem;
						}

						@Override
						public ISequentialInStream getStream(int index) throws SevenZipException {
							try {
								return new RandomAccessFileInStream(new RandomAccessFile(files.get(index), "r"));
							} catch (Exception e) {
								logger.error("Произошла ошибка", e);
								return null;
							}
						}
					});
				} finally {
					exportArch.deleteOnExit();
				}
				return exportArch;
			}
		} finally {
			Driver.unload();
			Files.walkFileTree(tmpDb, new SimpleFileVisitor() {
				@Override
				public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
					file.toFile().deleteOnExit();
				    return FileVisitResult.CONTINUE;
				}

				@Override
				public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
					dir.toFile().deleteOnExit();
				    return FileVisitResult.CONTINUE;
				}
			});
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy