1

Class DbHelper contains all database methods. I created a single method for insert to save myself the repetition. When I run my app from the IDE (using the Launcher main class) it updates the database and does things normally. But when I run the .jar file of my app (an artifact) it doesn't save updates to database (but can read files normally).

I thought it had to do with autocommit set to false, so I set it to true with all update database functions (even though I never explicitly set it to false). I also made sure all ResultSet variables are closed when I'm done with them. The code that connects to the database:

public static Connection getConnection() {
    try {
        Class.forName("org.sqlite.JDBC"); // Load the SQLite JDBC driver
        String databaseUrl;
        // if running .jar file do this
        databaseUrl = "jdbc:sqlite::resource:ttlsDatabase.db";

        //else (running from ide) do this
        //databaseUrl = "jdbc:sqlite:C:\\Users\\medaw\\Desktop\\TTLS_APP\\src\\main\\Resource\\ttlsDatabase.db";

        Connection databaseLink = DriverManager.getConnection(databaseUrl);
        return databaseLink;
    } catch (ClassNotFoundException e) {
        System.err.println("SQLite JDBC driver not found. Make sure you have added the SQLite JDBC driver to your project's classpath.");
    } catch (SQLException e) {
        System.err.println("An error occurred with the database: " + e.getMessage());
    }
    return null;
}

Whenever I want to run the .jar file I comment out:

databaseUrl = "jdbc:sqlite:C:\\Users\\medaw\\Desktop\\TTLS_APP\\src\\main\\Resource\\ttlsDatabase.db";

Otherwise I comment out:

databaseUrl = "jdbc:sqlite:C:\\Users\\medaw\\Desktop\\TTLS_APP\\src\\main\\Resource\\ttlsDatabase.db";

It worked, I made sure of the con variable (it's never null) and the data is retrieved from database normally in both cases. I assume the database is in some kind of read only mode.

This is my function which I use to update the database. On advice to optimize this code I'm all ears! I use it for all updates that only require a single execution of a query:

public static boolean updateDatabase(ArrayList<Object> list, String query){
    try (Connection con = getConnection()) {
        con.setAutoCommit(true);

        PreparedStatement stmt = con.prepareStatement(query);
        stmt = setStatements(stmt, list);
        if (stmt == null) {
            return false;
        }

        int x = stmt.executeUpdate();
        if(x == 0){
            return false;
        }
        con.commit();

        return true;
    } catch (Exception e) {
        UtilityMethods.showMessageDialog("Unable to update item!", "Error!", "Update Unsuccessful", 1);
    }
    return false;
}

This is how my project directories look like.

user4157124
  • 2,809
  • 13
  • 27
  • 42

1 Answers1

4

Resources are read-only

Resources (any file type, not just your SQL lite db file) in jar files are read-only.

The jar protocol used to access stuff in the resources in a jar file doesn't allow writeback to change the resources.

JarURLConnection instances can only be used to read from JAR files. It is not possible to get an OutputStream to modify or write to the underlying JAR file using this class.

SQLite will need to update the database file if your write data to the database. So to allow updates to the database, you need to use a database file on your file system (not a resource embedded in a jar).

How to get a writeable database file

If you have a seed database that you want to distribute to your users, there are a few ways to do that.

Method 1: Extract an embedded database file resource from a jar to a file

If you embed your initial database in your jar file, you can extract the initial database image to a file on app startup and have SQLite connect to the extracted file. A mechanism for doing that is documented in James's answer to:

Method 2: Package the database file separately from your jar file

You can package the database file and distribute it separately from the jar.

  1. Use a packaging system like jpackage that allows the installation of apps with separate resources (complex) OR
  2. Create a zip distribution of your app with a separate database file, using a Maven assembly or something similar.

Method 3: Create a new database file if one does not exist

In the event you are using JPA/Spring/Hibernate, those tools have the ability to create a database if it does not exist, by introspecting on the entity classes defined for the application. So that may also be a viable alternative.

A JPA persistence manager with JPA entities defined is required to use this technique, it will not work for raw JDBC. Of course, the resultant database will be empty. You may need to also populate some data in the database if that is part of your requirement.

For Spring it is done like this, but consult the documentation for your DB access framework if you use another technology:

jewelsea
  • 150,031
  • 14
  • 366
  • 406