Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

NetBeans in the Classroom: JavaFX and JDBC -- A Winning Combination

DZone's Guide to

NetBeans in the Classroom: JavaFX and JDBC -- A Winning Combination

· Java Zone
Free Resource

Learn how to troubleshoot and diagnose some of the most common performance issues in Java today. Brought to you in partnership with AppDynamics.

Ken Fogel is the Program Coordinator and Chairperson of the Computer Science Technology program at Dawson College in Montreal, Canada. He is also a Program Consultant to and part-time instructor in the Computer Institute of Concordia University's School of Extended Learning. He blogs at omniprogrammer.com and tweets @omniprof. His regular columns about NetBeans in education are listed here.

In this article, I will show you how I teach my students to code JDBC for JavaFX desktop applications. My approach works as follows:

  1. Use JavaFX beans
  2. Make a connection to get in to the database
  3. Issue the query through a prepared statement
  4. Copy the results to a suitable data structure
  5. Close all objects and then get out of the database

This type of approach is necessary when the desktop application is using a database server shared by other applications or users. An approach using connection pooling is the preferred way to deal with databases in a server side application and may also be suitable in some cases on the desktop.

Let’s begin with the data bean. When creating a JavaFX application you should write your beans so that the fields can bind to a control. At the same time you want the bean to continue to be usable in classes that are unaware of JavaFX.

Begin by creating all the fields as JavaFX properties. The properties shown are interfaces that will be instantiated by the proper class in the constructor.

public class FishData {

    private IntegerProperty id;
    private StringProperty commonName;
    private StringProperty latin;
    private StringProperty ph;
    private StringProperty kh;
    private StringProperty temp;
    private StringProperty fishSize;
    private StringProperty speciesOrigin;
    private StringProperty tankSize;
    private StringProperty stocking;
    private StringProperty diet;

The non-default constructor signature is that of a normal data bean. The arguments are all the primitives or actual object types that the bean will contain. The code of the constructor instantiates the property object and passes the matching argument in to it. The default constructor simply calls the non-default constructor with initial values that you have decided are appropriate.

    public FishData(int id, String commonName, String latin, String ph,
            String kh, String temp, String fishSize, String speciesOrigin,
            String tankSize, String stocking, String diet) {
        super();
        this.id = new SimpleIntegerProperty(id);
        this.commonName = new SimpleStringProperty(commonName);
        this.latin = new SimpleStringProperty(latin);
        this.ph = new SimpleStringProperty(ph);
        this.kh = new SimpleStringProperty(kh);
        this.temp = new SimpleStringProperty(temp);
        this.fishSize = new SimpleStringProperty(fishSize);
        this.speciesOrigin = new SimpleStringProperty(speciesOrigin);
        this.tankSize = new SimpleStringProperty(tankSize);
        this.stocking = new SimpleStringProperty(stocking);
        this.diet = new SimpleStringProperty(diet);
    }

    public FishData() {
        this(-1, "", "", "", "", "", "", "", "", "", "");
    }

If your fields contain an object that does not have a matching property such as a BigDecimal you can use the ObjectProperty.

private ObjectProperty unitCost;

In the constructor it will look like:

this.unitCost = new SimpleObjectProperty<>(unitCost);

The setters and getters in a JavaFX bean have the same interface as a normal bean. This is what ensures compatibility. Inside these methods the code either retrieves the value from the property object or sets the value in the property object. You never use set or get to touch the actual property object.

    public int getId() {
        return id.get(); // Use get on the propert
    }

    public void setId(int id) {
        this.id.set(id); // Use set on the property
    }

JavaFX beans require a third method for every field that you will bind to a control. This method returns the property object. This method should only be used in binding. I’m not sure if there is a naming convention but I tell my students to use the field name plus the word Property.

    public IntegerProperty idProperty() {
        return id;
    }

Here is the method that will retrieve all the records from the table containing the fish records. The variables url, user and password have been declared and I teach my students that this information should be loaded from a properties file.

public ObservableList findAll() throws SQLException {

   ObservableList fishData = FXCollections.observableArrayList();

   String selectQuery = "SELECT ID, COMMONNAME, LATIN, PH, KH, TEMP, FISHSIZE, SPECIESORIGIN, TANKSIZE, STOCKING, DIET FROM FISH";

   // Using try with resources, available since Java 1.7
   // Classes that implement the Closable interface created in the
   // parenthesis () will be closed when the block ends.
   try (Connection connection = DriverManager.getConnection(url, user, password);
        PreparedStatement pStatement = connection.prepareStatement(selectQuery);
        ResultSet resultSet = pStatement.executeQuery()) {

      while (resultSet.next()) {
         fishData.add(createFishData(resultSet));
      }
   } catch (SQLException sqlex) {
      log.error("SQL Error", sqlex);
      throw sqlex;
   }
   log.info("# of records found : " + fishData.size());
   return fishData;
}

public ObservableList findAll() throws SQLException {

The method will return an ObservableList of FishData objects. ObservableList implements the Collection and List interfaces and this makes it compatible with, for example, an ArrayList. Existing code that expects an ArrayList will continue to work with this method.

   try (Connection connection = DriverManager.getConnection(url, user, password);
        PreparedStatement pStatement = connection.prepareStatement(selectQuery);
        ResultSet resultSet = pStatement.executeQuery()) {

This try uses the try-with-resources that was introduced in Java 7. Each of the objects inside the parenthesis implement the Closable interface. This means that when this try block ends, either successfully or due to an exception, all the objects will be closed in the reverse order from their instantiation.

      while (resultSet.next()) {
         fishData.add(createFishData(resultSet));
      }

Here the code goes thru the ResultSet, calls upon a private method to create FishData objects and then adds the objects to the ObservbleList. Here is the private method.

    private FishData createFishData(ResultSet resultSet) throws SQLException {
        FishData fishData = new FishData();
        fishData.setCommonName(resultSet.getString("COMMONNAME"));
        fishData.setDiet(resultSet.getString("DIET"));
        fishData.setKh(resultSet.getString("KH"));
        fishData.setLatin(resultSet.getString("LATIN"));
        fishData.setPh(resultSet.getString("PH"));
        fishData.setFishSize(resultSet.getString("FISHSIZE"));
        fishData.setSpeciesOrigin(resultSet.getString("SPECIESORIGIN"));
        fishData.setStocking(resultSet.getString("STOCKING"));
        fishData.setTankSize(resultSet.getString("TANKSIZE"));
        fishData.setTemp(resultSet.getString("TEMP"));
        fishData.setId(resultSet.getInt("ID"));
        return fishData;
    }

Back in findAll is the catch block.

   } catch (SQLException sqlex) {
      log.error("SQL Error", sqlex);
      throw sqlex;
   }

I used to teach my students to just use a throws clause and not bother to catch the exception here. Recent reading on the subject plus some classroom experiences has modified my approach. The exception is now caught and logged before it is re-thrown. I don’t believe that the database manager is where the decision should be made about what to do about an exception so I re-throw it.

One last example is a method that uses a ‘where’ clause in the SQL. Here is when the PreparedStatement is mandatory. It also requires an inner try-with-resources because only the instantiation of objects may be placed inside the try’s parenthesis.

public FishData findID(int id) throws SQLException {

   // If there is no record with the desired id then this will be returned
   // as a null pointer
   FishData fishData = null;

   String selectQuery = "SELECT ID, COMMONNAME, LATIN, PH, KH, TEMP, FISHSIZE, SPECIESORIGIN, TANKSIZE, STOCKING, DIET FROM FISH WHERE ID = ?";

   // Using try with resources, available since Java 1.7
   // Classes that implement the Closable interface created in the
   // parenthesis () will be closed when the block ends.
   try (Connection connection = DriverManager.getConnection(url, user, password);

        // You must use PreparedStatements to guard against SQL Injection
        PreparedStatement pStatement = connection.prepareStatement(selectQuery);) {

      // Only object creation statements can be in the parenthesis so
      // first try-with-resources block ends
 
     pStatement.setInt(1, id);

      // A new try-with-resources block for creating the ResultSet object begins
      try (ResultSet resultSet = pStatement.executeQuery()) {

         if (resultSet.next()) {
            fishData = createFishData(resultSet);
         }
      }
   } catch (SQLException sqlex) {
      log.error("SQL Error", sqlex);
      throw sqlex;
   }
   log.info("Found " + id + "?: " + (fishData != null));
   return fishData;
}

I hope that this will make your JDBC coding more robust and that you will move to JavaFX for your GUI interfaces.

Understand the needs and benefits around implementing the right monitoring solution for a growing containerized market. Brought to you in partnership with AppDynamics.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}