2

I've got a Spring Batch job that is reading a specified Access database file. I'm using uncanaccess JDBC library to do so. Once the job exits, whether the job completes successfully or not, I need to have my application move the access file to another folder. Currently I'm getting a java.nio.file.FileSystemException that states it can't be moved because it's used by another process. I'm assuming the other process is due to the fact that the JDBC connection was opened.

Exception in thread "main" com.mycompany.weeklyimport.FileException: Failed to move file [C:\temp\hold\temp.mdb] to [C:\temp\error\temp.mdb]
    at com.mycompany.weeklyimport.WeeklyImportApplication.moveFile(WeeklyImportApplication.java:365)
    at com.mycompany.weeklyimport.WeeklyImportApplication.main(WeeklyImportApplication.java:91)
Caused by: java.nio.file.FileSystemException: C:\temp\hold\temp.mdb -> C:\temp\error\temp.mdb: The process cannot access the file because it is being used by another process.

    at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86)
    at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
    at sun.nio.fs.WindowsFileCopy.move(WindowsFileCopy.java:387)
    at sun.nio.fs.WindowsFileSystemProvider.move(WindowsFileSystemProvider.java:287)
    at java.nio.file.Files.move(Files.java:1395)
    at com.mycompany.weeklyimport.WeeklyImportApplication.moveFile(WeeklyImportApplication.java:363)
    ... 1 more

Here's my main program (that runs the Spring job via Spring Boot):

@SpringBootApplication
@EnableBatchProcessing
@Slf4j
public class WeeklyImportApplication extends DefaultBatchConfigurer {
    ...
    private static String inputFile;
    private static boolean exceptionEncountered = false;

    public static void main(String[] args) throws Throwable {
        handleArguments(args);

        ConfigurableApplicationContext context = new SpringApplicationBuilder(WeeklyImportApplication.class).listeners(new CustomLoggingConfigurationApplicationListener(logConfigurer)).run(args);

        if (exceptionEncountered) {
            moveFile("error");
        } else {
            moveFile("complete");
        }

        finished();
    }

    private static void moveFile(String folderName) {
        File file = new File(inputFile);
        File newPath = new File(file.getParentFile().getParentFile().getPath() + File.separator + folderName);

        if (!newPath.exists()) {
            if (!newPath.mkdirs()) {
                throw new FileException("Failed to create folder [" + newPath.getPath() + "]");
            }
        }

        File newFile = new File(newPath.getPath() + File.separator + file.getName());

        try {
            Files.move(file.toPath(), newFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException ex) {
            throw new FileException("Failed to move file [" + file.getPath() + "] to [" + newFile.getPath() + "]", ex);
        }
    }

    ...

My datasource configuration. I'm assigning to a static variable as well to attempt to close the connection once Spring exits.

@Configuration
public class DataSourceConfiguration {

    public static SingleConnectionDataSource legacyDataSource;

    @Bean(name = "importDataSource")
    public DataSource importDataSource() {
        SingleConnectionDataSource dataSource = new SingleConnectionDataSource();
        dataSource.setDriverClassName(this.importDriver.trim());
        dataSource.setSuppressClose(true);
        dataSource.setUrl("jdbc:ucanaccess://" + WeeklyImportApplication.getInputFile());

        return dataSource;
    }

    ...

I've tried the following:

    DataSourceConfiguration.legacyDataSource.getConnection().close();
    DataSourceConfiguration.legacyDataSource.destroy();
    DataSourceConfiguration.legacyDataSource = null;

Somehow, something still has a lock on that file. Has anyone encountered anything like this or have any ideas on how to force the true close of the file read?

SOLVED

jamadei's answer below helped me get to this solution. Relevant solution code:

@Bean(name = "importDataSource")
public DataSource importDataSource() {
    SingleConnectionDataSource dataSource = new SingleConnectionDataSource();
    dataSource.setDriverClassName(this.importDriver.trim());
    dataSource.setSuppressClose(true);
    dataSource.setUrl("jdbc:ucanaccess://" + WeeklyImportApplication.getInputFile() + ";SingleConnection=true");

    importDataSource = dataSource;

    return dataSource;
}

public static void main(String[] args) throws Throwable {
    handleArguments(args);

    new SpringApplicationBuilder(WeeklyImportApplication.class).listeners(new CustomLoggingConfigurationApplicationListener(logConfigurer)).run(args);

    DataSourceConfiguration.importDataSource.setSuppressClose(false);
    DataSourceConfiguration.importDataSource.destroy();

    if (exceptionEncountered) {
        moveFile("error");
        System.exit(1);
    } else {
        moveFile("complete");
    }

    finished();
}
4

1 回答 1

3

这不仅仅是 Spring 问题,而是 UCanAccess 优化/缓存副作用。你所做的似乎很好,但还不够。在 jdbc url 中使用 SingleConnection=true 参数应该可以解决问题。

于 2015-02-19T17:21:32.037 回答