Because JavaFX is gaining ground as Java’s de-facto GUI framework, it is going to replace Swing sooner or later. JavaFX UI and JDBC can be an effective combination when creating a database-driven application, especially in an offline or embedded system. This article essentially shows how this may be done with an example scenario.
A JDBC Application Overview
The evolution of the Java GUI framework now rests on the JavaFX library. It provides a powerful yet flexible alternative to GUI development, in contrast to its existing Swing and AWT framework. JavaFX supplies a large array or controls and components that help in building a GUI interface quickly and effectively. It is very easy to develop a desktop application that interacts with the back-end database. A JDBC (Java Database Connectivity) application primarily has a back-end database system such as MySQL, Derby, Oracle, or any other database. Java code is written to fetch records from one or more tables in the database. The SQL (Structured Query Language) queries are fired from Java code and sent to the database engine for processing. The JDBC driver acts as an intermediary between the Java program and the database and interprets the volley of information to and fro, so that both the unmatched party, such as the database, and the Java program can reconcile to a workable solution. The database has absolutely no idea about Java code, its syntaxes, or anything about it. It simply understands SQL and can communicate with it only. Java, on the other hand, is an OOP (Object Oriented Programming) language and has no idea about SQL or its syntaxes, either. To make communication possible, the database vendor supplies native drivers along with the database. This is called the JDBC driver. Note that there are four types of drivers available. They are colloquially called Type-1, Type-2, Type-3, and Type-4 drivers. The native drivers are Type-4 drivers and are most commonly used. They are also more efficient than other the types. A Java program can include these JDBC drivers as an external library into the Java program, as they commonly come in JAR archive files.
JavaFX into the Scene
Every database application requires an interface so that the user can interact with the database information. Better, if it is a GUI interface where we do not have to stoop down to a low-level, intimidating command interface but get what we want with a click of a button. In this aspect, JavaFX with JDBC can be a killer combination because it has quite a number of visually exciting GUI components that can be used to represent database records in a more meaningful manner. For example, records can be displayed in a tabular form with the TableView control. Or, we can create a form to add new records into the database table. The data input by the user can be verified through Java code before sending to the database. The back-end database engine gets a respite from validating data and stalled processing due to an input error. Moreover, the end user may be a layman with little or no idea on the constraints of input data. This is ideally done when an input form is created with TextField, Label, ComboBox, and ListView controls in JavaFX. The events generated by Button and other controls are handled in such a manner that the user is at ease while interacting with the GUI interface.
Into an Example Scenario
In the following illustrated example, we’ll implement a ListView search operation by input text in a TextField. The selected item in the ListView is fetched accordingly from the back-end database and displayed in the TableView control. So, it is primarily a fetch and display kind of application. Other database operations—such as the insertion, deletion, and updating of records—are not implemented due to size constraints. It would be a nice exercise to implement them yourself.
So, before we begin, we must create a database table and a Java project. We’ll use MySQL as the back-end database; you may choose any other but make sure to include appropriate drivers in your pom.xml file. Here is some of the SQL code to create the table, insert some dummy data, and some other operations.
CREATE DATABASE addressbook; USE DATABASE addressbook; DROP TABLE IF EXISTS contact; CREATE TABLE contact( id INT UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, nick_name VARCHAR(20), address VARCHAR(128), home_phone VARCHAR(10), work_phone VARCHAR(10), cell_phone VARCHAR(10), email VARCHAR(100), birthday date, web_site VARCHAR(100), profession VARCHAR(100), PRIMARY KEY (id) ); INSERT INTO contact (name, nick_name, address, home_phone, work_phone, cell_phone, email, birthday, web_site,profession) VALUES ('Bruce Wayne', 'batman', 'XYZ Batcave', '9876543210', '6278287326', '9182872363', 'batman@gmail.com', '1976/02/03', 'batblog.com', 'Super Hero'); ... INSERT INTO contact (...) VALUES (...); Maven Project: pom.xml <project xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.mano.jdbc.examples</groupId> <artifactId>JavaFXJDBCApp</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>JavaFXJDBCApp</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding> UTF-8 </project.build.sourceEncoding> </properties> <build> <plugins> <plugin> <groupId> org.apache.maven.plugins </groupId> <artifactId> maven-compiler-plugin </artifactId> <version>2.5.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/mysql/ mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> </dependencies> </project>
Now, let’s create a domain object which we’ll use in both ListView and TableView because they are both related, as stated in our case. The TableView will contain an observable list of people (ContactPerson) based on selected perso’s name from the ListView control. We also have a TextField to make a quick search of items (ContactPerson name) contained in the ListView. On selection of a specific item from the ListView, an SQL query is fired and relevant records are fetched to populate the TableView control accordingly.
Domain Object: ContactPerson
The ContactPerson class is nothing but the POJO representation of the contact table attributes. It contains the constructor and simple getter-setter methods.
package org.mano.jdbc.examples; import java.util.Date; public class ContactPerson { private int id; private String name; private String nickName; private String address; private String homePhone; private String workPhone; private String cellPhone; private String email; private Date birthDate; private String webSite; private String profession; public ContactPerson() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getHomePhone() { return homePhone; }< public void setHomePhone(String homePhone) { this.homePhone = homePhone; } public String getWorkPhone() { return workPhone; } public void setWorkPhone(String workPhone) { this.workPhone = workPhone; } public String getCellPhone() { return cellPhone; } public void setCellPhone(String cellPhone) { this.cellPhone = cellPhone; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public Date getBirthDate() { return birthDate; } public void setBirthDate(Date birthDate) { this.birthDate = birthDate; } public String getWebSite() { return webSite; } public void setWebSite(String webSite) { this.webSite = webSite; } public String getProfession() { return profession; } public void setProfession(String profession) { this.profession = profession; } }
Data Access Object: ContactDAO
The ContactDAO is a data access object class that primarily includes database access operation. It implements the DAO interface. This interface may not be important in our example but may put to good use if the application is extended with more data access object classes. Here, the DAO interface includes a connection string, driver, and user name and password to access the MySQL database.
DAO.java
package org.mano.jdbc.examples; public interface DAO { public static final String DB_URL = "jdbc:mysql://localhost:3306/"+ "addressbook?zeroDateTimeBehavior=convertToNull"; public static final String DRIVER = "com.mysql.jdbc.Driver"; public static final String USER = "root"; public static final String PASS = "secret"; }
ContactDAO.java
package org.mano.jdbc.examples; import java.sql.*; import java.util.ArrayList; import java.util.List; public class ContactDAO implements DAO { private ontactPerson createContactPerson(ResultSet rs) { ContactPerson p = new ContactPerson(); try { p.setId(rs.getInt("id")); p.setName(rs.getString("name")); p.setNickName(rs.getString("nick_name")); p.setAddress(rs.getString("address")); p.setHomePhone(rs.getString("home_phone")); p.setWorkPhone(rs.getString("work_phone")); p.setCellPhone(rs.getString("cell_phone")); p.setEmail(rs.getString("email")); p.setBirthDate(rs.getDate("birthday")); p.setWebSite(rs.getString("web_site")); p.setProfession(rs.getString("profession")); } catch (SQLException ex) { } return p; } public List<ContactPerson> getContacts() { String sql = "Select * from contact order by name"; List<ContactPerson> list = new ArrayList<>(); try { Class.forName(DRIVER); Connection con = DriverManager.getConnection (DB_URL, USER, PASS); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { ContactPerson p = createContactPerson(rs); list.add(p); } rs.close(); con.close(); } catch (ClassNotFoundException | SQLException ex) { } return list; } public List<ContactPerson> getContactsForName(String name) { String sql = "Select * from contact where name like '%" + name + "%'"; List<ContactPerson> list = new ArrayList<>(); try { Class.forName(DRIVER); Connection con = DriverManager.getConnection (DB_URL, USER, PASS); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { ContactPerson p = createContactPerson(rs); list.add(p); } rs.close(); con.close(); } catch (ClassNotFoundException | SQLException ex) { } return list; } }
JavaFX GUI Interface: ContactBrowser
In the JavaFX application named ContactBrowser, we set up all the controls programmatically. This also can be set using FXML or builder utility tools such as Scene Builder. But, in the scribe’s opinion, they may be used once one has gained enough experience on what goes behind the scenes in JavaFX. The GUI is primarily an interplay of three controls, such as a TextField (searchField), a ListView (listView), and TableView (contactTableView). The code is self-explanatory, with comments given at appropriate places. Lambda expression is used wherever applicable to keep the code terse. Refer to the JavaFX API documentation wherever needed.
package org.mano.jdbc.examples; import javafx.application.Application; import javafx.beans.value.*; import javafx.collections.*; import javafx.collections.transformation.*; import javafx.geometry.Insets; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.control.cell.PropertyValueFactory; import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.stage.Stage; public class ContactBrowser extends Application { // List of contact table properties private String[] propertyName = {"id", "name", "nickName", "address", "homePhone", "workPhone", "cellPhone", "email", "birthDate", "webSite", "profession"}; private String[] propertyLabel = {"ID", "Name", "Nick Name", "Address", "Home Phone", "Work Phone", "Cell Phone", "Email", "Birth Date", "Website", "Profession"}; private ContactDAO contact = new ContactDAO(); private final GridPane gridPane = new GridPane(); private final Label lblName = new Label("Search by Name"); private final TextField searchField = new TextField(); private ObservableList<ContactPerson> observableNames; private FilteredList<ContactPerson> filteredData; private SortedList<ContactPerson> sortedData; private final ListView<ContactPerson> listView; TableView<ContactPerson> contactTableView = new TableView<>(); public ContactBrowser2() { lblName.setTextFill(Color.web("#0076a3")); observableNames = FXCollections.observableArrayList (contact.getContacts()); filteredData = new FilteredList<> (observableNames, p -> true); sortedData = new SortedList<>(filteredData); listView = new ListView<>(sortedData); } @Override public void start(Stage primaryStage) { primaryStage.setTitle("Address Book"); primaryStage.setMaximized(true); BorderPane borderPane = new BorderPane(); Scene scene = new Scene(borderPane,650,400,true); gridPane.setPadding(new Insets(10)); gridPane.setHgap(5); gridPane.setVgap(5); gridPane.add(lblName, 0, 0); gridPane.add(searchField, 0, 1); // Search TextField event handling searchField.textProperty() .addListener((observable, oldValue, newValue) -> filteredData.setPredicate(str -> { if (newValue == null || newValue.isEmpty()) return true; if (str.getName().toLowerCase().contains (newValue.toLowerCase())) return true; return false; })); listView.getSelectionModel().setSelectionMode (SelectionMode.SINGLE); listView.setPrefHeight(Integer.MAX_VALUE); // Sets a new cell factory to use in the ListView. // This throws away all old list cells and new ListCells // created with the new cell factory. listView.setCellFactory(listView-> { Tooltip tooltip = new Tooltip(); ListCell<ContactPerson> cell = new ListCell<ContactPerson>() { @Override public voidupdateItem(ContactPerson contactPerson, Boolean empty) { super.updateItem(contactPerson, empty); if (contactPerson != null) { setText(contactPerson.getName()); tooltip.setText(contactPerson.getNickName()); setTooltip(tooltip); } else setText(null); } }; return cell; }); gridPane.add(listView, 0, 2); // Create and initializing TableView ObservableList<ContactPerson> contactPeopleList = FXCollections.observableArrayList(); contactTableView.setItems(contactPeopleList); contactTableView.setColumnResizePolicy( TableView.CONSTRAINED_RESIZE_POLICY); for (int i = 0; i < propertyLabel.length; i++) { TableColumn<ContactPerson, Object> col = new TableColumn<>(propertyLabel[i]); col.setCellValueFactory(new PropertyValueFactory<>(propertyName[i])); contactTableView.getColumns().add(col); } borderPane.setCenter(contactTableView) borderPane.setLeft(gridPane); // TableView will populate from the contactPeopleList // contactPeopleList will have value according to the // item selected in the ListView listView.getSelectionModel() .selectedItemProperty() .addListener(new ChangeListener<ContactPerson>() { @Override public void changed( ObservableValue<? extends ContactPerson> observable, ContactPerson oldValue, ContactPerson newValue) { if (observable != null && observable.getValue() != null) { contactPeopleList.clear(); contactPeopleList.addAll( contact.getContactsForName (newValue.getName())); } } }); primaryStage.setScene(scene); primaryStage.show(); } public static void main(String[] args) { launch (args); } }
Output
Figure 1: Code output
Conclusion
A JDBC application with JavaFX essentially means that the JavaFX GUI framework was used as the front-end development engine and JDBC was used for the back-end database interaction. They can be of varieties of types with N number of functionality defined in them. The basic is the CRUD application. We have implemented a part of the search and display operation. Here is what you can do to extend it: implement Create, Delete, and Update operations; also, you can add names with images in the ListView. Happy coding 😉