ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • DBUnit으로 이기종 간 데이터 이관하기
    DB 2020. 3. 10. 09:28

    이전 포스팅에 이어, 

     

    현 회사에서는 이기종간 mapper dir을 별도로 관리하고있는데,

     

    mapper는 분류하여 작업한다 하더라도,

     

    실제 데이터( mock data )에 대해서는 해결할 방안을 찾던 중 

     

    https://woowabros.github.io/experience/2019/11/06/db-unit.html

     

    스프링부트에서 DbUnit 을 이용하여 DB 테스트 해보기 - 우아한형제들 기술 블로그

    안녕하세요. 상품시스템팀 권순규입니다. 저희 팀에서 DB 테스트를 위해 사용하고 있는 DbUnit 의 설정 및 사용에 대해 알려드리고자 합니다.

    woowabros.github.io

     

    위 포스팅을 보고 데이터 이관을 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();
        }
    }
    

     

    사용하는 방향에 따라 많은것을 진행할 수 있는 코드로 보여, 공유합니다.

     

    잘쓰셨으면 좋겟습니다.

     

     

    댓글

Designed by black7375.