В проекте для ХД МосОблГаз'а пришлось делать парсер для загрузки CSV файлов, размещенных в определенной папке на диске сервера (10.2.0.4, Win32). Папка заполняется извне инструментами Cognos'а в виде набора *.csv файлов. Данные из файлов парсятся и грузятся в определенную буферную таблицу, предварительно очищаемую. После загрузки файлы удаляются. Папка определена в виде объекта DIRECTORY, поэтому считывается из DBA_DIRECTORIES. В качестве разделителя используется ";", в первой строке каждого файла находятся названия столбцов. Они тоже парсятся, чтобы потом использовать их порядок при INSERT'е в целевую таблицу
Сначала сделал на PL/SQL'е, потом решил переделать под встроенную Java. В приведенном коде много несуразностей, но он был написан за час-полтора работы, поэтому прошу строго не судить. Вот что получилось:
Сначала сделал на PL/SQL'е, потом решил переделать под встроенную Java. В приведенном коде много несуразностей, но он был написан за час-полтора работы, поэтому прошу строго не судить. Вот что получилось:
create or replace and compile java source named pump_csv asimport java.sql.*;import java.text.*;import java.lang.*;import java.util.*;import java.io.*;import oracle.jdbc.*; // Подгружаем драйвер ORACLEimport c_pump_tools.*;public class c_pump_csv{static boolean DEBUG = true; // Включает режим пассивной отладки (вывод диагностических сообщений)static String CSV_DIR_NAME = "CSV_DIR";static String ORA_URL = "jdbc:default:connection"; // URL соединения с MSSQLpublic static void run() throws Exception{// Грузим драйвер ORACLEDriverManager.registerDriver(new oracle.jdbc.OracleDriver());OracleConnection conORA = null;try{// Соединяемся с базой данных ORACLE по внутреннему соединениюtry{conORA = (OracleConnection)DriverManager.getConnection(ORA_URL);}catch(Exception e){e.printStackTrace();throw new Exception("Невозможно соединиться с сервером-приемником данных по URL=" + ORA_URL);}conORA.setAutoCommit(false);// Устанавливаем отказ от autocommitlog("Connected ORACLE on " + ORA_URL);String csvDirectoryName = null;OraclePreparedStatement stmtORA_InsertData = null;OracleStatement stmtORA_CheckStageClearance = null;try{// Получаем имя директории с CSV файламиtry{OracleStatement stmtORA_GetDirectory = null;OracleResultSet ors=null;try{stmtORA_GetDirectory = (OracleStatement)conORA.createStatement();ors = (OracleResultSet)stmtORA_GetDirectory.executeQuery("select directory_path from dba_directories where directory_name = '"+CSV_DIR_NAME+"'");if(ors.next()) csvDirectoryName = ors.getString(1);if (csvDirectoryName == null){log("Unable to resolve directory name " + CSV_DIR_NAME + ". Pumping cannot be initiated");throw new Exception ("Невозможно разрешить имя директории '" + CSV_DIR_NAME + "'");}log("Path to \"" + csvDirectoryName + "\" has been found to pump from");}finally{if (ors instanceof OracleResultSet)ors.close();if (stmtORA_GetDirectory instanceof OracleStatement) stmtORA_GetDirectory.close();log("Resources for resolving the CSV directory name has been released");}}catch(Exception e){e.printStackTrace();throw new Exception("Невозможно получить путь к папке по имени директории " + CSV_DIR_NAME);}// Проверяем готовность целевой таблицы к приему новых данныхtry{stmtORA_CheckStageClearance = (OracleStatement)conORA.createStatement();OracleResultSet rstCheck = null;try{rstCheck = (OracleResultSet)stmtORA_CheckStageClearance.executeQuery("select count(*) cnt from target_table");int iCountRows = 0;if (rstCheck.next()) iCountRows = rstCheck.getInt(1);if (iCountRows>0){log("WARNING! Source table TARGET_TABLE is not empty. Data cannot be loaded into non-empty table. Terminating data import");return;}log("Source table TARGET_TABLE is empty and ready to receive data");}finally{if (rstCheck != null) rstCheck.close();}}catch(Exception e){e.printStackTrace();throw new Exception("Невозможно выполнить команду для проверки готовности целевой таблицы TARGET_TABLE сервера-приемника");}// Просматриваем содержимое папки на предмет наличия в ней CSV файловFile sourceFolder = new File(csvDirectoryName);FileFilter sourceFolderFilter = new FileFilter(){public boolean accept(File file){return !file.isDirectory()&& file.getName().endsWith(".csv");}};File[]csvFiles = sourceFolder.listFiles(sourceFolderFilter);if (csvFiles == null || csvFiles.length == 0){log("Source CSV files not found in directory " + CSV_DIR_NAME + ". Exiting process");return;}else{for (int iterFile=0; iterFile{// Открываем и построчно читаем текстовый файлFile csvFile = csvFiles[iterFile];log("File " + csvFile.getCanonicalPath() + " has been found in source SSV folder");BufferedReader input = new BufferedReader(new FileReader(csvFile));try{// Инициализируем переменные для разбора строкStringBuffer rowColumnNames = new StringBuffer();StringBuffer rowColumnBinds = new StringBuffer();int columnCount = 0;String line = null;long rowIndex = 0;try{while (( line = input.readLine()) != null){rowIndex++;if(rowIndex == 1){// В первой строке находятся столбцы, для вставки данныхStringTokenizer st = new StringTokenizer(line, ";");while(st.hasMoreTokens()){String fieldName = st.nextToken().trim().toUpperCase();if (rowColumnNames.length() > 0) rowColumnNames.append(",");rowColumnNames.append(fieldName);if (rowColumnBinds.length() > 0) rowColumnBinds.append(",");rowColumnBinds.append("?");columnCount++;}if (columnCount==0) throw new Exception("В первой строке CSV файла не обнаружены названия столбцов. Импорт данных невозможен.");log("Found " + new Integer(columnCount).toString() +" columns: " + rowColumnNames.toString() );// Готовим команду для вставки данныхtry{stmtORA_InsertData = (OraclePreparedStatement)conORA.prepareStatement("insert into target_table("+rowColumnNames.toString()+") values("+rowColumnBinds.toString()+")");}catch(Exception e){e.printStackTrace();throw new Exception("Невозможно подготовить команду для вставки данных из CSV источника");}}else{// Разбор данныхStringTokenizer st = new StringTokenizer(line, ";");ArrayList columnValues = new ArrayList(columnCount);while(st.hasMoreTokens()){columnValues.add(st.nextToken());}if (columnValues.size() != columnCount) throw new Exception("Количество значений (" + columnValues.size()+ ") не соответствует количеству столбцов ("+new Integer(columnCount).toString()+")");// Вставка данных в БДif (columnCount>=1){try{stmtORA_InsertData.setString(1, (String)columnValues.get(0));}catch(Exception e) {log("Error parsing data cell #1");throw e;}}if (columnCount>=2){try{stmtORA_InsertData.setString(2, (String)columnValues.get(1));}catch(Exception e) {log("Error parsing data cell #2");throw e;}}if (columnCount>=3){try{stmtORA_InsertData.setString(3, (String)columnValues.get(2));}catch(Exception e) {log("Error parsing data cell #3");throw e;}}if (columnCount>=4){try{stmtORA_InsertData.setString(4, (String)columnValues.get(3));}catch(Exception e) {log("Error parsing data cell #4");throw e;}}if (columnCount>=5){try{stmtORA_InsertData.setString(5, (String)columnValues.get(4));}catch(Exception e) {log("Error parsing data cell #5");throw e;}}if (columnCount>=6){try{stmtORA_InsertData.setString(6, (String)columnValues.get(5));}catch(Exception e) {log("Error parsing data cell #6");throw e;}}if (columnCount>=7){try{stmtORA_InsertData.setString(7, (String)columnValues.get(6));}catch(Exception e) {log("Error parsing data cell #7");throw e;}}if (columnCount>=8){try{stmtORA_InsertData.setString(8, (String)columnValues.get(7));}catch(Exception e) {log("Error parsing data cell #8");throw e;}}stmtORA_InsertData.execute();}} //while (read file)stmtORA_InsertData.execute("commit");log("Processed " + new Long(rowIndex).toString() + " rows. Pumping finished");}catch(Exception e){e.printStackTrace();throw new Exception("\n\nФайл "+csvFile.getName()+"\nСбой закачки в строке " + rowIndex + ":\n"+e.getMessage()+"\n\n");}}finally{if (stmtORA_InsertData instanceof OraclePreparedStatement) stmtORA_InsertData.close();input.close();log("Input CSV stream closed");csvFile.delete();log("Source CSV file deleted");}} //iterate csvFiles} // csvFiles is not null}finally{// Освобождаем все используемые ресурсыif (stmtORA_InsertData != null) stmtORA_InsertData.close();if (stmtORA_CheckStageClearance != null) stmtORA_CheckStageClearance.close();log("Resources released");}}catch(Exception e){log(e.getMessage());throw e;}finally{// Закрываем соединения с базами данныхif (conORA != null && !conORA.isClosed()) conORA.close();log(Connections closed");}}// Пишем в LOG сервераpublic static void log(String msg){if (DEBUG) c_pump_tools.log(msg);}}
для корректной работы после компиляции необходимо будет с помощью DBMS_JAVA.GrantPermission(..) дать права на удаление файлов из папки-источника CSV файлов (java.io.FilePermission). Точный текст PL/SQL команды ORACLE напишет прямо в тексте сообщения об ошибке, если при попытке удалении файла такого гранта не обнаружит. Текст примерно такой:
call dbms_java.grant_permission('<пользователь,которому дать права>','SYS:java.io.FilePermission', '<объект для раздачи прав>', 'read,write,execute,delete')
Для обеспечению удаления любых файлов в папке вместо имени файла нужно указать "*", например C:\\CSVFOLDER\\*
Комментариев нет:
Отправить комментарий