-
DBUnit으로 이기종 간 데이터 이관하기DB 2020. 3. 10. 09:28
이전 포스팅에 이어,
현 회사에서는 이기종간 mapper dir을 별도로 관리하고있는데,
mapper는 분류하여 작업한다 하더라도,
실제 데이터( mock data )에 대해서는 해결할 방안을 찾던 중
https://woowabros.github.io/experience/2019/11/06/db-unit.html
위 포스팅을 보고 데이터 이관을 DBUnit으로 할 수 있을 것으로 보여 시작하였다.
우선 DBUnit이란 xml 형태로 dataset을 가지고오는 형태이며,
tablename이라는 table이 있고,
column / column2 의 컬럼이 있을때 아래와 같이 조회되는 형태이다.
행은 DBUnit으로 조회하는 쿼리에 row 별로 뽑힌다.
<dataset> <tablename column="1" column2="ㅁ"> <tablename column="2" column2="ㄴ"> <tablename column="3" column2="ㅇ"> <tablename column="4" column2="ㄹ"> <tablename column="5" column2="ㅎ"> <tablename column="6" column2="ㅗ"> </dataset>
이를 이용하여,
Oracle DB의 data만 dbunit으로 빼오고, 이를 다시 dbunit insert로 이기종별 insert가 가능할 것으로 판단되 실행하였다.
하지만 DBunit에서 export -> import 형식으로 생각한 개발자가 별로 없는지 내역이 많이 나오지 않았고,
실제 export 시에도, FlatXmlDataSet 처리 간 별도로 진행해야하는 사항이 있어 디컴파일하여 처리하는것이 있었다.
우선 Oracle 기준으로 DATA 추출 코드이다.
public class ExportDBUnit { public static void main(String[] args) throws Exception { String outputFilePath = "C:\\solution\\gitWorkSpace\\standard-common\\src\\main\\test\\exportDBUnit"; Class driverClass = Class.forName("oracle.jdbc.driver.OracleDriver"); String user = "dbuser"; ResultSet rs = null; Connection jdbcConnection = DriverManager.getConnection("jdbc:oracle:thin:@url:1521:sid", user, "pwd"); Statement stmt = null; stmt = jdbcConnection.createStatement(); String sql = "SELECT UT.OBJECT_NAME AS TABLE_NAME\n" + " FROM ALL_OBJECTS UT,\n" + " ALL_TAB_COMMENTS UTC\n" + " WHERE UT.OBJECT_NAME = UTC.TABLE_NAME\n" + " AND UT.OWNER = UTC.OWNER\n" + " AND UT.OBJECT_TYPE IN ('TABLE')\n" + " AND UT.OWNER ='"+user.toUpperCase()+"'\n" + "\n" + " AND UT.OBJECT_NAME NOT LIKE 'BIN$%'\n" + " ORDER BY TABLE_NAME"; rs = stmt.executeQuery(sql); while(rs.next()){ IDatabaseConnection connection = new DatabaseConnection(jdbcConnection); DatabaseConfig config = connection.getConfig(); config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new Oracle10DataTypeFactory()); config.setFeature(DatabaseConfig.FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES,Boolean.TRUE); QueryDataSet partialDataSet = new QueryDataSet(connection); String tableName = rs.getString("TABLE_NAME"); partialDataSet.addTable(tableName); System.out.println("TABLE NAME :"+tableName); /*String originalStr = partialDataSet.getTable(tableName).getValue(0,partialDataSet.getTableMetaData(tableName).getColumns()[0].getColumnName()).toString(); // 테스트 String [] charSet = {"utf-8","euc-kr","ksc5601","iso-8859-1","x-windows-949"}; for (int i=0; i<charSet.length; i++) { for (int j=0; j<charSet.length; j++) { try { //FlatXmlDataSet.write(partialDataSet, new FileWriter(outputFilePath+ File.separator + tableName+".xml",false) ,"MS949" ); FileWriter t = new FileWriter(outputFilePath+ File.separator + tableName+".xml",true); t.write( new String(originalStr.getBytes(charSet[i]), charSet[j])); t.flush(); System.out.println("[" + charSet[i] +"," + charSet[j] +"] = " + new String(originalStr.getBytes(charSet[i]), charSet[j])); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } }*/ FlatXmlDataSet.write(partialDataSet, new FileWriter(outputFilePath+ File.separator + tableName+".xml",false) ,"UTF-8" ); } System.out.println("END"); }
이후 다른 이기종 Import 시
public class ImportDBUnit { @Test public void loadData() throws Exception { String outputFilePath = "C:\\solution\\gitWorkSpace\\standard-common\\src\\main\\test\\exportDBUnit"; Class driverClass = Class.forName("oracle.jdbc.driver.OracleDriver"); String user = "user"; Connection jdbcConnection = DriverManager.getConnection("jdbc:oracle:thin:@url:1521:sid", user, "pwd"); IDatabaseConnection connection = new DatabaseConnection(jdbcConnection,user.toUpperCase()); DatabaseConfig config = connection.getConfig(); config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new Oracle10DataTypeFactory()); config.setFeature(DatabaseConfig.FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES, Boolean.TRUE); //table 존재여부 검출용도 IDataSet databaseDataSet = connection.createDataSet(); String[] getTableNames =databaseDataSet.getTableNames(); List<String> importTableNames = new ArrayList<String>(); try { this.deleteDBData(); for (File info : FileUtils.listFilesAndDirs(new File(outputFilePath), TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)) { if(info.isDirectory()) { ArrayList<File> fileList = new ArrayList<File>(); this.arrayDirInFileList(fileList, info); for (File inDirIsFile : fileList) { String tableName = FilenameUtils.getBaseName(inDirIsFile.getName()); if(importTableNames.contains(tableName)) continue; //이미 import된 xml이라면 다음으로. System.out.println("START FILE READ TABLE NAME :"+tableName); for(String getTableName : getTableNames){ if(getTableName.equals(tableName)){ /* String[] depRootTableNames = TablesDependencyHelper.getAllDependentTables( connection, getTableName ); //상위 테이블 검색 if(depRootTableNames.length > 1){ *//*String[] depTableNames = new String[depRootTableNames.length]; this.getDependencyTableNames(connection,depRootTableNames);*//* for(int i=depRootTableNames.length-1; i>=0; i--){ String depTableName = depRootTableNames[i]; File depFile = new File(outputFilePath+File.separator+depTableName+".xml"); IDataSet depData = new FlatXmlDataSetBuilder().build(depFile); depData.getTable(depTableName); System.out.println("IMPORT START TABLE NAME :"+depTableName); try{ DeleteAllOperation.DELETE_ALL.execute(connection, depData); DatabaseOperation.CLEAN_INSERT.execute(connection, depData); }catch (Exception e){ e.printStackTrace(); } importTableNames.add(depTableName); } }*/ FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder(); builder.setColumnSensing(true); IDataSet data = builder.build(inDirIsFile); data.getTable(tableName); try{ System.out.println("IMPORT START TABLE NAME :"+tableName); /*DatabaseOperation op = new CompositeOperation(DatabaseOperation.DELETE_ALL,DatabaseOperation.INSERT); op.execute(connection, data);*/ DatabaseOperation.CLEAN_INSERT.execute(connection, data); importTableNames.add(tableName); }catch (Exception e){ DatabaseOperation.DELETE_ALL.execute(connection, data); } } } } }else{ String tableName = FilenameUtils.getBaseName(info.getName()) == null? "" : FilenameUtils.getBaseName(info.getName()); System.out.println("START FILE READ TABLE NAME :"+tableName); if(importTableNames.contains(tableName)) continue; //이미 import된 xml이라면 다음으로. outputFilePath = outputFilePath.replaceAll(info.getName(),""); for(String getTableName : getTableNames){ if(getTableName.equals(tableName)){ FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder(); builder.setColumnSensing(true); IDataSet data = builder.build(info); data.getTable(tableName); data.getTable(tableName); try{ System.out.println("IMPORT START TABLE NAME :"+tableName); DatabaseOperation.CLEAN_INSERT.execute(connection, data); importTableNames.add(tableName); }catch (Exception e){ DatabaseOperation.DELETE_ALL.execute(connection, data); } } } } } } catch (Throwable e) { e.printStackTrace(); } finally { if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } } public ArrayList<File> arrayDirInFileList(ArrayList<File> files ,File dir){ if(dir.isDirectory()) { File[] children = dir.listFiles(); for(File f : children) { // 재귀 호출 사용 // 하위 폴더 탐색 부분 arrayDirInFileList(files,f); } } else { files.add(dir); } return files; } public void deleteDBData(){ IDatabaseConnection srm9qaDBConnection = null; IDatabaseConnection srm9newDBConnection = null; Statement stmt = null; ResultSet rs = null; try{ Connection jdbcConnection = DriverManager.getConnection("jdbc:oracle:thin:@url:1521:sid", "user", "pwd"); Connection srm9new_jdbcConnection = DriverManager.getConnection("jdbc:oracle:thin:@url:1521:sid", "user", "pwd"); srm9newDBConnection = new DatabaseConnection(srm9new_jdbcConnection,"user".toUpperCase()); srm9qaDBConnection = new DatabaseConnection(jdbcConnection,"user".toUpperCase()); DatabaseConfig srm9qaConfig = srm9qaDBConnection.getConfig(); srm9qaConfig.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new Oracle10DataTypeFactory()); srm9qaConfig.setFeature(DatabaseConfig.FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES, Boolean.TRUE); //table 존재여부 검출용도 IDataSet databaseDataSet = srm9newDBConnection.createDataSet(); String[] getTableNames =databaseDataSet.getTableNames(); stmt = jdbcConnection.createStatement(); String sql = "db 자식-> 부모 순으로 지울수있는 쿼리"; rs = stmt.executeQuery(sql); while(rs.next()){ QueryDataSet partialDataSet = new QueryDataSet(srm9newDBConnection); String tableName = rs.getString("TABLE_NAME"); for(String getTableName : getTableNames) { if (getTableName.equals(tableName)) { System.out.println("DELETE TABLE NAME :"+tableName); partialDataSet.addTable(tableName); DatabaseOperation.DELETE_ALL.execute(srm9newDBConnection, partialDataSet); } } } }catch (Exception e){ e.printStackTrace(); }finally { if(null != srm9qaDBConnection) { try{ srm9qaDBConnection.close(); }catch (Exception e){ e.printStackTrace(); } } } }
우선 import 시 초기화 -> insert 작업을 진행하려 하였는데,
현 회사에서는 oracle base로 data가 들어가야하는 순서 (부모-자식테이블) 구조가 있기 때문에,
초기화가 가능하도록 순차적으로 지울수있었으나, 활용하는 곳에서 지울수있는것은 미지수이기에,
이에 대한 방안은 고려해봐야할것으로 보인다.
또한 DBUnit을 디컴파일하여 처리하다보니, dataset xml 처리간 number / timestamp 에 대해서는 공백이 아닌 null 처리이면, column name 조차 표기하지 않아 data가 정확히 들어가지 않는 부분 있었다.
이는 datasetproduceradapter를 override하는것으로 해결하였다. ( 해당 부분은 xml로 데이터를 명확히 보려고하는 목적임을 서술합니다. )
public class DataSetProducerAdapter implements IDataSetProducer{ /** * Logger for this class */ private static final Logger logger = LoggerFactory.getLogger(DataSetProducerAdapter.class); private static final IDataSetConsumer EMPTY_CONSUMER = new DefaultConsumer(); private final ITableIterator _iterator; private IDataSetConsumer _consumer = EMPTY_CONSUMER; public DataSetProducerAdapter(ITableIterator iterator) { _iterator = iterator; } public DataSetProducerAdapter(IDataSet dataSet) throws DataSetException { _iterator = dataSet.iterator(); } @Override public void setConsumer(IDataSetConsumer consumer) throws DataSetException { logger.debug("setConsumer(consumer) - start"); _consumer = consumer; } @Override public void produce() throws DataSetException { logger.debug("produce() - start"); _consumer.startDataSet(); while(_iterator.next()) { ITable table = _iterator.getTable(); ITableMetaData metaData = table.getTableMetaData(); _consumer.startTable(metaData); try { Column[] columns = metaData.getColumns(); if (columns.length == 0) { _consumer.endTable(); continue; } for (int i = 0; ; i++) { Object[] values = new Object[columns.length]; for (int j = 0; j < columns.length; j++) { Column column = columns[j]; if(column.getDataType().isNumber() || column.getDataType().isDateTime()){ values[j] = table.getValue(i, column.getColumnName()) == null? null : table.getValue(i, column.getColumnName()); }else{ values[j] = table.getValue(i, column.getColumnName()) == null? "" : table.getValue(i, column.getColumnName()); } } _consumer.row(values); } } catch (RowOutOfBoundsException e) { // This exception occurs when records are exhausted // and we reach the end of the table. Ignore this error // and close table. // end of table _consumer.endTable(); } } _consumer.endDataSet(); } }
사용하는 방향에 따라 많은것을 진행할 수 있는 코드로 보여, 공유합니다.
잘쓰셨으면 좋겟습니다.
'DB' 카테고리의 다른 글
Mysql The user specified as a definer ('계정명'@'호스트') does not exist (0) 2017.05.30 mysql dump 뜨기 (0) 2017.05.30 ORACLE SID 확인하기 (0) 2017.05.28 오라클 계정 LOCK(락) 걸렸을 때 (0) 2017.05.28 Mysql 호스트 접근 오류 날 경우 (0) 2017.05.28