1

I am working on a JavaFX project that connects to a MySQL database (and as such I use a JDBC [java.sql] to perform various tasks from java to SQL). I've recently come into a lot of confusion regarding the standard procedure of connecting to a database.

From what I understand, I should create one instance of a Connection object, then I should pass this instance around the project until the end of the entire program.

Connection connection = DriverManager.getConnection(url, username, password);

If the statement above is true then I only need to call the previous line once in the entire program and pass the connection variable around the entire program whenever I need to connect to the database. If this is true, how can I pass around this instance of connection from one controller class to another?


Furthermore, I am seeing that it is a standard procedure (in order to maximize GUI responsiveness) to push all "costly" procedures onto another thread (not the main one that is handling the GUI) using the javafx.concurrencies library. If this statement is true, is it also true that I should push all methods that interact with the database into say a Task<V> class?


Up until now, I created multiple object classes that each communicate with the database. In addition, I created a Database class that allows me to initialize a "new" connection to the database. For example,

public class Database {

    public static Connection createConnectionToDB() throws SQLException{       
        Connection connection = DriverManager.getConnection(url, username, password); 
        return connection;
    }
}

Now in the various object classes, when I need to add, update, or delete some data from the database, I created an add, update, and delete method in each object to handle that object's data in the database. However, for each method, I thought (from my natural naivete) that it would be standard to first create a connection to the database via the previous Connection connection = createConnectionToDB() method in the Database class then to use this connection instance to do what needs to be done, then close the connection with the connection.close() method.

For example,

public class Object{

     add(){
          Connection connection = Database.createConnectionToDB();
          PreparedStatement statement = ...
          // some other lines 
          connection.close();
     }
     
     update(){
          Connection connection = Database.createConnectionToDB();
          PreparedStatement statement = ...
          // some other lines 
          connection.close();
     }
     
     delete(){
          Connection connection = Database.createConnectionToDB();
          PreparedStatement statement = ...
          // some other lines 
          connection.close();
     }

}

That is, for each object, and each method for each object, a new connection is created and closed. Is this bad practice?

Shadow
  • 33,525
  • 10
  • 51
  • 64
  • 1
    Does this answer your question? [Passing Parameters JavaFX FXML](https://stackoverflow.com/questions/14187963/passing-parameters-javafx-fxml) – SedJ601 Jan 17 '23 at 04:54
  • @SedJ601 No it does not –  Jan 17 '23 at 05:17
  • 3
    It should at least answer part of your question. If you understand how to pass objects between FXML controllers, then you understand how to pass the object that manages your database connection between FXML controllers. Though database access should be in a relatively "low" layer of your application. So, more likely you'll be passing some higher-level model object around that internally has a reference to your database (or another object between the higher-level model and the database layer). And passing objects between controllers is conceptually the same as between any kind of objects. – Slaw Jan 17 '23 at 07:30
  • 1
    Despite the answer below, I recommend you avoid static singletons. They are considered an anti-pattern. If you don't want to use a connection pool library (which I've seen info of such linked on other questions of yours), then you would still do something similar to the answer below, but "cache" the connection in an _instance_ field. Then it's up to how you program your application to ensure only one instance of the connection-caching-object is ever created. This encourages things like dependency injection, which also makes things like testing easier. – Slaw Jan 17 '23 at 07:36
  • @Slaw And how about if I did use a pool library, could I use a static reference to a connection pool object and then pass that connection pool around the project? –  Jan 17 '23 at 08:17
  • 1
    You could, same as with the `Connection` directly. But you shouldn't. Your default response to the question, "Should I use a static singleton?", should be, "No", in virtually all cases. A major reason for this is testing. It makes it very difficult to run isolated unit/integration tests if you can't control which references an object has (see [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection)). But I'm not saying don't use a singleton. What I'm saying is make use of an "instance singleton" (where it's your program's logic and not the language that enforces one instance) – Slaw Jan 17 '23 at 08:31
  • @Slaw I'm not really following, could you provide an example or explain the "instance singleton" please? –  Jan 17 '23 at 08:54
  • Do you have to do any sort of insertion/deletion/updating of collections of data? if so, I would have an outer method that creates/closes the connection, iterates the collection and passes the connection in the parameters to a method that does the PreparedStatement and executes it. I found that when I did not do this, the database complained that I was making too many connections at a time, even when the database operations were done linearly in a separate thread. – experiment unit 1998X Jan 17 '23 at 09:27
  • 1
    I don't really understand why you would use a connection pool in a desktop/mobile application. Surely that's only necessary in server-side code (with perhaps some possible exceptions for highly complex code). – James_D Jan 17 '23 at 14:14
  • looks unrelated to javafx to me – kleopatra Jan 17 '23 at 16:52
  • 1
    *"Up until now, I created multiple object classes that each communicate with the database."* If I understand this correctly, this looks like the real problem. Are you saying your entity classes (the classes that represent the data) are *also* performing database functionality? That violates the principle of single responsibility. Your entity classes should have no database code in them; you should have a separate class (or even classes) that is responsible for data access. – James_D Jan 17 '23 at 17:56
  • This tutorial on [connecting JavaFX with SQLite using JDBC](https://edencoding.com/connect-javafx-with-sqlite/) from Eden coding may help. It does use the static access pattern for getting connections, but it is still worth reviewing IMO. Also, a [simpler example](https://gist.github.com/jewelsea/4957967), using [Tasks](https://stackoverflow.com/questions/14878788/javafx-background-thread-for-sql-query). – jewelsea Jan 17 '23 at 23:53
  • @Tom What I mean by "instance singleton" is that you only have `new Database(...)` execute once during the normal flow of your application. You then pass that instance around, via constructors and/or via methods (i.e., "dependency injection"). And like I said, you cache the `Connection` (or connection pool object) in an _instance_ field (not a _static_ field). You might also want to research the MVC pattern, data-access objects (DAO), and, if you allow multiple threads to interact with the database code, thread synchronization. Libraries such as JDBI may be of interest as well. – Slaw Jan 18 '23 at 04:06

1 Answers1

-1

It could be a bad practice if your application creates new database Connection objects with longer scopes every time it needs to perform a query operation on the database. I would suggest that you should put the Connection initiation code into a try-with-resources code block so that for each database operation, a connection is established, query operation is performed and the connection is closed within the block. The class to fetch new Connection could look like this:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseConnection {

    private static final String DB_URL = "jdbc:mysql://localhost/dbName";
    private static final String USER = "dbUser";
    private static final String PASS = "dbPassword";

    public static Connection getDatabaseConnection()
    {
        Connection databaseConnection;
        try {
            databaseConnection = DriverManager.getConnection(DB_URL, USER, PASS);
            
        } catch (SQLException e)
        {
            e.printStackTrace();
        }
        return databaseConnection;
    }
}

So, you can make a call to the static method DatabaseConnection.getDatabaseConnection() from any point (controller or other class) of your application to obtain the new Connection instance and use it.

And yes, it would be better to make use of the javafx.concurrency library in case of a JavaFX application. You can utilize the Task<V> class like:

Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {

                String query = "SELECT id, first, last, age FROM Employees";
                try(Connection conn = DatabaseConnection.getDatabaseConnection();
                    Statement stmt = conn.createStatement();
                    ResultSet rs = stmt.executeQuery(query);) {
                    // Extract data from result set
                    while (rs.next()) {
                        // Retrieve by column name
                        System.out.print("ID: " + rs.getInt("id"));
                        System.out.print(", Age: " + rs.getInt("age"));
                        System.out.print(", First: " + rs.getString("first"));
                        System.out.println(", Last: " + rs.getString("last"));
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }

                return null;
            }
        };

And invoking the Task by:

new Thread(task).start();

Note that you should populate all the ResultSet data into your own custom non-jdbc classes so that the ResultSet class can also be disposed within the block. Furthermore, you should also consider applying Transaction Management to your code containing Database Operations (in case your application is concurrently making many database calls from different points/classes) for data consistency.

Fuzail
  • 372
  • 5
  • 12
  • this could work but I read on other posts that it is not proper to make a Connection static. If this statement is true then your solution would be invalid. –  Jan 17 '23 at 07:29
  • Doing some more research I came to the point that yes, your connection should not be static instance due to thread unsafety and other reasons. In that case, it is suggested that you establish new connection using the try-with-resources block each time you perform a Db operation. I should edit my answer as well. – Fuzail Jan 17 '23 at 08:48