查找并替换MS Access表行中的文本无效

时间:2017-02-15 18:33:54

标签: java javafx task jackcess

给定一个目录,我的应用程序遍历并使用Jackcess API加载.mdb MS Access dbs。在每个数据库的内部,有一个名为GCMT_CMT_PROPERTIES的表,其中包含一个名为cmt_data的列,其中包含一些文本。我还有一个Mapper对象(基本上类似于Map<String,String>但允许重复键),当我从字符串替换某个单词时,我将其用作字典。

例如,如果mapper包含fox -> dog,则句子"The fox jumps"变为"The dog jumps"

我参与此计划的设计如下:

1. Given a directory, traverse all subdirectories and load all .mdb files into a File[].
2. For each db file in File[], create a Task<Void> called "TaskMdbUpdater" and pass it the db file. 
3. Dispatch and run each task as it is created (see 2. above).

TaskMdbUpdater负责在给定的db文件中查找相应的表和列,并迭代运行&#34; find&amp;替换&#34;表的每一行上的例程,用于检测字典中的单词并替换它们(如上例所示),最后在关闭数据库之前更新该行。 TaskMdbUpdater的每个实例都是一个后台线程,其中分配了Jackcess API DatabaseBuilder,因此它可以操作数据库。

在当前状态下,代码运行时没有任何异常,但是当我&#34;手动&#34;通过Access打开数据库并检查给定的行,它似乎没有改变。我试图在没有任何运气的情况下确定问题的根源,并感谢任何支持。如果您需要查看更多代码,请告知我们,我会相应地更新我的问题。

public class TaskDatabaseTaskDispatcher extends Task<Void> {

    private String parentDir;
    private String dbFileFormat;
    private Mapper mapper;

    public TaskDatabaseTaskDispatcher(String parent, String dbFileFormat, Mapper mapper) {
        this.parentDir = parent;
        this.dbFileFormat = dbFileFormat;
        this.mapper = mapper;
    }


    @Override
    protected Void call() throws Exception {

        File[] childDirs = getOnlyDirectories(getDirectoryChildFiles(new File(this.parentDir)));
        DatabaseBuilder[] dbs = loadDatabasesInParent(childDirs);

        Controller.dprint("TaskDatabaseTaskDispatcher", dbs.length + " databases were found in parent directory");
        TaskMdbUpdater[] tasks = new TaskMdbUpdater[dbs.length];
        Thread[] workers = new Thread[dbs.length];
        for(int i=0; i<dbs.length; i++) {
            // for each db, dispatch Task so a worker can update that db.
            tasks[i] = new TaskMdbUpdater(dbs[i], mapper);
            workers[i] = new Thread(tasks[i]);
            workers[i].setDaemon(true);
            workers[i].start();
        }

        return null;
    }

    private DatabaseBuilder[] loadDatabasesInParent(File[] childDirs) throws IOException {
        DatabaseBuilder[] dbs = new DatabaseBuilder[childDirs.length];

        // Traverse children and load dbs[]
        for(int i=0; i<childDirs.length; i++) {

            File dbFile = FileUtils.getFileInDirectory(
                    childDirs[i].getCanonicalFile(),
                    childDirs[i].getName() + this.dbFileFormat);

            dbs[i] = new DatabaseBuilder(dbFile);
        }
        return dbs;
    }


} 


// StringUtils class, utility methods
public class StringUtils {

public static String findAndReplace(String str, Mapper mapper) {
        String updatedStr = str;
        for(int i=0; i<mapper.getMappings().size(); i++) {
            updatedStr = updatedStr.replaceAll(mapper.getMappings().get(i).getKey(), mapper.getMappings().get(i).getValue());
        }

        return updatedStr;
    }
}

// FileUtils class, utility methods:
public class FileUtils {
/**
     * Returns only directories in given File[].
     * @param list
     * @return
     */
    public static File[] getOnlyDirectories(File[] list) throws IOException, NullPointerException {
        List<File> filteredList = new ArrayList<>(); 
        for(int i=0; i<list.length; i++) {
            if(list[i].isDirectory()) {
                filteredList.add(list[i]);
            }
        }

        File[] correctSizeFilteredList = new File[filteredList.size()];
        for(int i=0; i<filteredList.size(); i++) {
            correctSizeFilteredList[i] = filteredList.get(i);
        }

        return correctSizeFilteredList;
    }

/**
     * Returns a File[] containing all children under specified parent file.
     * @param parent
     * @return
     */
    public static File[] getDirectoryChildFiles(File parent) {
        return parent.listFiles();
    }
}

public class Mapper {

    private List<aMap> mappings;

    public Mapper(List<aMap> mappings) {
        this.mappings = mappings;
    }

    /**
     * Returns mapping dictionary, typically used for extracting individual mappings.
     * @return List of type aMap 
     */
    public List<aMap> getMappings() {
        return mappings;
    }

    public void setMappings(List<aMap> mappings) {
        this.mappings = mappings;
    }
} 

/**
 * Represents a single String based K -> V mapping.
 */
public class aMap {

    private String[] mapping; // [0] - key, [1] - value

    public aMap(String[] mapping) {
        this.mapping = mapping;
    }

    public String getKey() {
        return mapping[0];
    }

    public String getValue() {
        return mapping[1];
    }

    public String[] getMapping() {
        return mapping;
    }

    public void setMapping(String[] mapping) {
        this.mapping = mapping;
    }
} 

更新1: 为了验证我的自定义StringUtils.findAndReplace逻辑,我已经执行了以下单元测试(在JUnit中),该测试正在通过:

@Test
    public void simpleReplacementTest() {
        // Construct a test mapper/dictionary
        List<aMap> aMaps = new ArrayList<aMap>();
        aMaps.add(new aMap(new String[] {"fox", "dog"})); // {K, V} = K -> V
        Mapper mapper = new Mapper(aMaps);

        // Perform replacement
        String corpus = "The fox jumps";
        String updatedCorpus = StringUtils.findAndReplace(corpus, mapper);
        assertEquals("The dog jumps", updatedCorpus);
    }

我在这里分别包含我的TaskMdbUpdater课程,其中包含了一些日志记录代码,因为我怀疑失败点位于call

/**
 * Updates a given .mdb database according to specifications defined internally.
 * @since 2.2
 */
public class TaskMdbUpdater extends Task<Void> {

    private final String TABLE_NAME = "GCMT_CMT_PROPERTIES";
    private final String COLUMN_NAME = "cmt_data";

    private DatabaseBuilder dbPackage;
    private Mapper mapper;

    public TaskMdbUpdater(DatabaseBuilder dbPack, Mapper mapper) {
        super();
        this.dbPackage = dbPack;
        this.mapper = mapper;
    }

    @Override
    protected Void call() {
        try {
//      Controller.dprint("TaskMdbUpdater", "Worker: " + Thread.currentThread().getName() + " running");

        // Open db and extract Table
        Database db = this.dbPackage
                .open();
        Logger.debug("Opened database: {}", db.getFile().getName());

        Table table = db.getTable(TABLE_NAME);
        Logger.debug("Opening table: {}", table.getName());

        Iterator<Row> tableRows = table.iterator();

//      Controller.dprint("TaskMdbUpdater", "Updating database: " + db.getFile().getName());

        int i=0;
        try {
            while( tableRows.hasNext() ) {

                // Row is basically a<code> Map<Column_Name, Value> </code>
                Row cRow = tableRows.next();
                Logger.trace("Current row: {}", cRow);

//              Controller.dprint(Thread.currentThread().getName(), "Database name: " + db.getFile().getName());
//              Controller.dprint("TaskMdbUpdater", "existing row: " + cRow.toString());
                String str = cRow.getString(COLUMN_NAME);
                Logger.trace("Row {} column field contents (before find/replace): {}", i, str);

                String newStr = performFindAndReplaceOnString(str);
                Logger.trace("Row {} column field contents (after find/replace): {}", i, newStr);

                cRow.put(COLUMN_NAME, newStr);
                Logger.debug("Updating field in row {}", i);

                Row newRow = table.updateRow(cRow); // <code>updateRow</code> returns the new, updated row. Ignoring this.
                Logger.debug("Calling updateRow on table with modified row");

//              Controller.dprint("TaskMdbUpdater", "new row: " + newRow.toString());
                i++;
                Logger.trace("i = {}", i);
            }
        } catch(NoSuchElementException e) {
//          e.printStackTrace();
            Logger.error("Thread has iterated past number of rows in table", e);
        }
        Logger.info("Iterated through {} rows in table {}", i, table.getName());

        db.close();
        Logger.debug("Closing database: {}", db.getFile().getName());

        } catch (Exception e) {
//          e.printStackTrace();
            Logger.error("An error occurred while attempting to update row value", e);
        }
        return null;
    }

    /**
     * @see javafx.concurrent.Task#failed()
     */
    @Override
    protected void failed() {
        super.failed();
        Logger.error("Task failed");
    }

    @Override
    protected void succeeded() {
        Logger.debug("Task succeeded");
    }

    private String performFindAndReplaceOnString(String str) {
//      Logger.trace("OLD: [" + str + "]");

        String updatedStr = null;
        for(int i=0; i<mapper.getMappings().size(); i++) {
            // loop through all parameter names in mapper to search for in str.
            updatedStr = findAndReplace(str, this.mapper);
        }
//      Logger.trace("NEW: [" + updatedStr + "]");
        return updatedStr;
    }
}

这是我日志中的一个小问题。正如你所看到的,打开桌子之后似乎没有做任何事情让我感到有些困惑:

INFO (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Located the following directories under specified MOIS parent which contains an .mdb file:
[01_Parent_All_Safe_Test[ RV_DMS_0041RV_DMS_0001RV_DMS_0003RV_DMS_0005RV_DMS_0007RV_DMS_0012RV_DMS_0013RV_DMS_0014RV_DMS_0016RV_DMS_0017RV_DMS_0018RV_DMS_0020RV_DMS_0023RV_DMS_0025RV_DMS_0028RV_DMS_0029RV_DMS_0031RV_DMS_0033RV_DMS_0034RV_DMS_0035RV_DMS_0036RV_DMS_0038RV_DMS_0039RV_DMS_0040 ]]
...
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Created new task: NAMEMAP.logic.TaskMdbUpdater@4cfe46fe
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Created new worker: Thread[Thread-22,5,main]
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Set worker Thread[Thread-22,5,main] as daemon
DEBUG (16-02-2017 17:27:59) [Thread-9] NAMEMAP.logic.TaskDatabaseTaskDispatcher.call(): Dispatching worker: Thread[Thread-22,5,main]
...
DEBUG (16-02-2017 17:28:00) [Thread-22] NAMEMAP.logic.TaskMdbUpdater.call(): Opened database: RV_DMS_0023.mdb
DEBUG (16-02-2017 17:28:00) [Thread-22] NAMEMAP.logic.TaskMdbUpdater.call(): Opening table: GCMT_CMT_PROPERTIES

在此之后,日志中没有任何条目条目,并且处理器在100%负载时出现峰值,保持这种状态直到我强制终止应用程序。这可能意味着程序陷入无限循环 - 但如果情况确实如此,那么文件中是否应该有日志条目?

更新2

好的,我已将日志TRACE打印到stdio,从而进一步缩小了问题范围。似乎我的performFindAndReplaceOnString效率极低,而且它永远不会超过这些dbs的第一行,因为它只是在长弦上磨掉。关于如何有效地为此用例执行字符串替换的任何建议?

0 个答案:

没有答案