0

I am developing a system which uses a database access object (DAO) to link between the program and database. All connections must be made through this class, using getConnection(). However, I have a reset option which overrides the database file in the user's file system with one inside the application itself (i.e. in its jar when deployed). This reset() feature causes an exception saying that the database is already in use.

java.nio.file.FileSystemException: C:\iMProve\improveDB.accdb: The process cannot access the file because it is being used by another process.

With research, this means that the connections have not been closed properly. However, I will show you the whole code for this DAO class if you can spot possibly where lies the problem.

public class DAO {
    private static final String dbDir = "C://iMProve";
    private static final String dbName = "improveDB.accdb";
    private static final String dbUrl = "jdbc:ucanaccess://" + dbDir + "//"+ dbName;
    private ObservableList<Connection> allConnections = FXCollections.observableArrayList();

    public DAO() { //constructor - called when object is made
        try {
            Class.forName("net.ucanaccess.jdbc.UcanaccessDriver");
        } catch(ClassNotFoundException e) {
            System.out.println("Cannot load ucanaccess driver");
            e.printStackTrace();
        }
        File directory = new File(dbDir);
        if(!directory.exists()) //create directory if not already
            directory.mkdir();
        File database = new File(dbDir + "//" + dbName);
        if(!database.exists()) { //copy the database file into user's file system - if not already
            try {
                Files.copy(DAO.class.getResourceAsStream(dbName), database.toPath(), StandardCopyOption.REPLACE_EXISTING);
            } catch(IOException ex) {ex.printStackTrace();}
        }
    }
    public void reset() {
        File database = new File(dbDir + "//" + dbName);
        try {
            Files.copy(DAO.class.getResourceAsStream(dbName), database.toPath(), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Connection getConnection() { //create a connection to the database
        Connection conn = null; 
        try {
            conn = DriverManager.getConnection(dbUrl);
        } catch (SQLException e) {
            e.printStackTrace();
        }   
        allConnections.add(conn);
        return conn;
    }
    public void closeConnections() {
        for(Connection conn: allConnections) {
            if(conn!=null) {
                try {
                    conn.close();
                    System.out.println("closed");
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

The constructor simply copies the database from the application into the user's file system if not there already.

The getConnection() method connects to the database.

Calling reset() on its own produces that error, so by adding the connections to a static collection we can closing each and every connection, using closeConnections().

Note: These connections should already be closed anyways because we always a try-with-resources. For example:

try (
            Connection conn = dao.getConnection();
            PreparedStatement stmt = conn.prepareStatement("SELECT Title FROM Theory WHERE Grade <= ?");
    ) {

And although we have two levels for closing connections

  1. try-with-resources
  2. closeConnections() method

They still are ineffective. When calling closeConnections(), we still get outputs of

closed
closed

even when connections have been used inside try-with-resources blocks. And even with closeConnections(), we still get the same error:

java.nio.file.FileSystemException: C:\iMProve\improveDB.accdb: The process cannot access the file because it is being used by another process.
Kerage Chan
  • 116
  • 1
  • 9
  • Why to not use `try-with-resource` to automatically close the connections? It's possible that the driver is holding the database open, even after the connections are closed for performance reasons – MadProgrammer Nov 01 '18 at 21:32
  • 1
    So, not bad for a "guess". If you have a look at [this post from their help forums](https://sourceforge.net/p/ucanaccess/discussion/help/thread/4539a56c/), you should also include `((UcanaccessConnection) conn).unloadDB();` as part of your close operation (I looked at the connection properties, but I don't think they suit your needs) – MadProgrammer Nov 01 '18 at 21:39
  • thank you! that did the trick, calling `unloadDB()` from a random `Connection` in the `closeConnections()` method did the trick. However, why do you need to do this when all streams are closed? Also, why are the streams not already closed with the try-with-resource prior to `closeConnections()`? – Kerage Chan Nov 02 '18 at 10:10
  • If you read the linked post, ucanaccess will keep the file open (and some other resources) for a short period in order to improve the performance for situations where connections are opened and closed in quick succession – MadProgrammer Nov 02 '18 at 10:46

0 Answers0