Integrate JavaFX, Hibernate and PostgreSQL with the MVC Pattern

JavaFX provides a rich set of UI controls, which simplify the development of visually immersive front ends for database-driven applications. When combined with the PostgreSQL database and the Hibernate ORM tool, JavaFX can handle the presentation layer for large-scale, data-driven business applications (JavaFX PostgreSQL) as well as robust desktop applications (JavaFX Hibernate).

In this article, I demonstrate how to integrate JavaFX, Hibernate and PostgreSQL using the MVC pattern and present a sample CRUD application with a data-navigation feature.

To follow the demo in this article, simply:

  1. Install the Java SDK+JavaFX2 SDK
  2. Unpack the Eclipse IDE
  3. Install the PostgreSQL DB
  4. Unpack Hibernate
  5. Save the PostgreSQL JDBC driver

JavaFX / Hibernate / PostgreSQL Project Setup

From the File menu in Eclipse, choose Java Project. It is better to set up User Library of all JAR files required in the project. This is not a necessary procedure but it will come in quite handy for future reference. Let’s make an individual library for the Hibernate JavaFX PostgreSQL driver and include it in our project, as shown in Figure 1.

JavaFX Hibernate PostgreSQL
Figure 1. Project Library: Make a library for the Hibernate JavaFX PostgreSQL driver.

PostgreSQL Table Structure

You don’t need to worry about the structure of the sample table, Contact. It will be created automatically when the application runs. However, if you want to create the table separately, you can but keep in mind the following comment for the code snippets in the rest of the article

cfg.setProperty("hibernate.connection.url","jdbc:postgresql://127.0.0.1:5432/testdb");

In this case, testdb is the table space. If you opt for creating your table through SQL or through pgAdmin III, either give the name of tablespace as testdb or change the appropriate portion of code in the HiberateUtil.java file.

cfg.setProperty("hibernate.hbm2ddl.auto", "update");

Also keep in mind the update value. You may also put create in place of update. This would recreate the table every time the application runs, deleting all stored information. So use create or update appropriately.

cfg.setProperty("hibernate.show_sql", "true");

Alternatively, you may set the above property as false.

The table created in PostgreSQL is as follows:

CREATE TABLE contacts(  contactid integer NOT NULL,  email character varying(255),  firstname character varying(255),  lastname character varying(255),  phone character varying(255),  CONSTRAINT contacts_pkey PRIMARY KEY (contactid ))WITH ( OIDS=FALSE );ALTER TABLE contacts OWNER TO postgres;

JavaFX / Hibernate / PostgreSQL Project Structure

Our project will follow the MVC pattern and the structure will look like Figure 2.

JavaFX Hibernate PostgreSQL
Figure 2. Project Structure: Here is the structure of the JavaFX / Hibernate / PostgreSQL project.

Hibernate Configuration

The class HibernateUtil.java contains all the configuration requirements for our project to interact with the database. The Hibernate configuration can also be established through XML, but in this case I used annotation-based configuration.

package org.contact.entity;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.SessionFactoryObserver;import org.hibernate.cfg.Configuration;import org.hibernate.service.ServiceRegistry;import org.hibernate.service.ServiceRegistryBuilder;public class HibernateUtil {     private static final  SessionFactory sessionFactory;     private static final  ServiceRegistry serviceRegistry;     static {          try {               Configuration config = getConfiguration();               serviceRegistry = new ServiceRegistryBuilder().applySettings(                         config.getProperties()).buildServiceRegistry();               config.setSessionFactoryObserver(new SessionFactoryObserver() {                    private static final long  serialVersionUID = 1L;                    @Override                    public void sessionFactoryCreated(SessionFactory factory) {                    }                    @Override                    public void sessionFactoryClosed(SessionFactory factory) {                         ServiceRegistryBuilder.destroy(serviceRegistry);                    }               });               sessionFactory = config.buildSessionFactory(serviceRegistry);          } catch (Throwable ex) {               System.err.println("Initial SessionFactory creation failed." + ex);               throw new ExceptionInInitializerError(ex);          }     }     public static  Session openSession() {          return sessionFactory.openSession();     }     private static  Configuration getConfiguration() {          Configuration cfg = new Configuration();          cfg.addAnnotatedClass(Contact.class );          cfg.setProperty("hibernate.connection.driver_class",     "org.postgresql.Driver");          cfg.setProperty("hibernate.connection.url","jdbc:postgresql://127.0.0.1:5432/testdb");          cfg.setProperty("hibernate.connection.username", "postgres");          cfg.setProperty("hibernate.connection.password", "postgres");          cfg.setProperty("hibernate.show_sql", "true");          cfg.setProperty("hibernate.dialect","org.hibernate.dialect.PostgreSQLDialect");          cfg.setProperty("hibernate.hbm2ddl.auto", "update");          cfg.setProperty("hibernate.cache.provider_class","org.hibernate.cache.NoCacheProvider");          cfg.setProperty("hibernate.current_session_context_class", "thread");          return cfg;     }}

Model Layer: Entity Class

The model layer implements the domain logic of the application with strong separation from the way that the user requests and sees results. Contact.java is our annotation-based entity class for creating the Contacts database table.

@[email protected](name="CONTACTS")public class Contact {     @Id     @GeneratedValue     @Column(name="CONTACTID")     private Integer contactId;     @Column(name="FIRSTNAME")     private String firstName;     @Column(name="LASTNAME")     private String lastName;     @Column(name="EMAIL")     private String email;     @Column(name="PHONE")     private String phone;          public Contact() {          super();               }          public Contact(Integer contactId, String firstName, String lastName, String email, String phone) {          super();          this.contactId = contactId;          this.firstName = firstName;          this.lastName = lastName;          this.email = email;          this.phone = phone;     }          public Integer getContactId() {          return contactId;     }     public void setContactId(Integer contactId) {          this.contactId = contactId;     }     public String getFirstName() {          return firstName;     }     public void setFirstName(String firstName) {          this.firstName = firstName;     }     public String getLastName() {          return lastName;     }     public void setLastName(String lastName) {          this.lastName = lastName;     }     public String getEmail() {          return email;     }     public void setEmail(String email) {          this.email = email;     }     public String getPhone() {          return phone;     }     public void setPhone(String phone) {          this.phone = phone;     }     }

Model Layer: Data Access Objects

ContactDAO.java is an abstract interface of our persistence mechanism. It supports operation without exposing any details of the database. This interface provides a mapping from application calls to the persistence layer.

ContactDAO.java
package org.contact.dao;import java.util.List;import org.contact.entity.Contact;public interface ContactDAO {public void addContact(Contact contact);public List listContact(); public void removeContact(Integer id); public void updateContact(Contact contact);}

A concrete implementation of this interface is provided by the ContactDAOImpl.java class. This approach isolates domain-specific objects and data types, abstracting the application needs and how they are satisfied.

ContactDAOImpl.java
package org.contact.dao;import java.util.ArrayList;import java.util.List;import org.contact.entity.Contact;import org.contact.entity.HibernateUtil;import org.hibernate.Session;public class ContactDAOImpl implements ContactDAO{     @Override     public void addContact(Contact contact) {          Session s=HibernateUtil.openSession();          s.beginTransaction();          s.save(contact);          s.getTransaction().commit();          s.close();     }     @Override     public List listContact() {          List list=new ArrayList<>();          Session s=HibernateUtil.openSession();          s.beginTransaction();          list=s.createQuery("from Contact").list();          s.getTransaction().commit();          s.close();          return list;     }     @Override     public void removeContact(Integer id) {          Session s=HibernateUtil.openSession();          s.beginTransaction();          Contact c=(Contact)s.load(Contact.class , id);          s.delete(c);          s.getTransaction().commit();          s.close();               }     @Override     public void updateContact(Contact contact) {                    Session s=HibernateUtil.openSession();          s.beginTransaction();          s.update(contact);          s.getTransaction().commit();          s.close();               }}

Model Layer: Services

The ContactService.java interface provides cohesive, high-level logic for related parts of the application. This layer is invoked directly by the Controller and View layers.

ContactService.java
package org.contact.service;import java.util.List;import org.contact.entity.Contact;public interface ContactService {     public void addContact(Contact contact); public List listContact(); public void removeContact(Integer id); public void updateContact(Contact contact);}

ContactServiceImpl.java is the concrete implementation of the ContactService.java interface. It provides a public interface of underlying model objects.

ContactServiceImpl.java
package org.contact.service;import java.util.List;import org.contact.dao.ContactDAO;import org.contact.dao.ContactDAOImpl;import org.contact.entity.Contact;public class ContactServiceImpl implements ContactService{          private ContactDAO contactDAO=new ContactDAOImpl();          @Override     public void addContact(Contact contact) {          contactDAO.addContact(contact);               }     @Override     public List listContact() {          return contactDAO.listContact();               }     @Override     public void removeContact(Integer id) {          contactDAO.removeContact(id);               }     @Override     public void updateContact(Contact contact) {          contactDAO.updateContact(contact);               }     }

Controller Layer: Controller

The Controller translates requests coming from the View layer into outgoing responses. In order to do this, ContactController.java takes request data and passes it to the service layer. The service layer then returns data that the Controller injects into a View for rendering. This view may be HTML for a standard Web request or JSON (JavaScript Object Notation) for a RESTful API request. In our case, it is a JavaFX UI.

ContactController.java
package org.contact.app;import java.util.List;import javafx.collections.FXCollections;import javafx.collections.ObservableList;import org.contact.entity.Contact;import org.contact.service.ContactService;import org.contact.service.ContactServiceImpl;public class ContactController {     private ContactService contactService=new ContactServiceImpl();     private ObservableList contactList=FXCollections.observableArrayList();          public void addContact(Contact contact){          contactService.addContact(contact);     }          public ObservableList getContactList(){          if(!contactList.isEmpty())               contactList.clear();                              contactList = FXCollections.observableList((List) contactService.listContact());          return contactList;     }          public void removeContact(Integer id)     {          contactService.removeContact(id);     }          public void updateContact(Contact contact){          contactService.updateContact(contact);     }     }

View Layer of MVC

Our front-end object is implemented in JavaFX. Main.java acts as a View layer for our application. The View layer translates data for visual rendering in response to the client. The data is supplied primarily by the Controller, ContactController.java in this case.

Main.javapackage org.contact.app;import javafx.application.Application;import javafx.event.ActionEvent;import javafx.event.EventHandler;import javafx.geometry.Insets;import javafx.geometry.Pos;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.control.Label;import javafx.scene.control.TableColumn;import javafx.scene.control.TableView;import javafx.scene.control.TextField;import javafx.scene.control.Tooltip;import javafx.scene.control.cell.PropertyValueFactory;import javafx.scene.layout.BorderPane;import javafx.scene.layout.GridPane;import javafx.scene.layout.HBox;import javafx.scene.layout.Pane;import javafx.stage.Stage;import org.contact.entity.Contact;public class Main extends Application {     String bgcolor = "-fx-background-color: #f0f0f0";     String style = "-fx-font-weight:normal; -fx-color: #f0f0f0; -fx-font-size:11; -fx-font-family: Verdana;";     private TableView table = new TableView<>();     private String buttonCaption[] = { "Add New", "Update", "Delete", "|<",               "<<", ">>", ">|" };     private String label[] = { "Contact ID", "First Name", "Lat Name", "Email",               "Phone" };     private String fields[] = { "contactId", "firstName", "lastName", "email",               "phone" };     private Button button[] = new Button[7];     private TextField textField[] = new TextField[5];     private ContactController controller = new ContactController();     private static int  index;     public static void  main(String[] args) throws Exception {          Application.launch(args);     }     @Override     public void start(Stage stage) throws Exception {          stage.setTitle("Contact Manager");          BorderPane border = new BorderPane();          border.setTop(createButtonBox());          border.setCenter(createForm());          border.setBottom(table);          border.setStyle(bgcolor);          border.setPadding(new Insets(10, 10, 10, 10));          populateForm(0);          populateTable();          stage.setScene(new Scene(border, 800, 650));          stage.show();     }     private Pane createForm() {          GridPane grid = new GridPane();          grid.setAlignment(Pos. CENTER);          grid.setPadding(new Insets(10, 10, 10, 10));          grid.setHgap(20);          grid.setStyle(style);          grid.setVgap(2);          for (int i = 0; i < label.length; i++) {               grid.add(new Label(label[i] + " :"), 1, i);               textField[i] = new TextField();               grid.add(textField[i], 2, i);          }          textField[0].setEditable(false);          textField[0].setTooltip(new Tooltip(                    "This field is automatically generated hence not editable"));          return grid;     }     private Pane createButtonBox() {          int width = 100;          HBox box = new HBox();          box.setAlignment(Pos. CENTER);          box.setSpacing(5);          for (int i = 0; i < buttonCaption.length; i++) {               button[i] = new Button(buttonCaption[i]);               button[i].setStyle(style);               button[i].setMinWidth(width);               button[i].setOnAction(new ButtonHandler());               box.getChildren().add(button[i]);          }          return box;     }     private class ButtonHandler implements EventHandler {          @Override          public void handle(ActionEvent event) {               if (event.getSource().equals(button[0])) {                    Contact c = new Contact(111, textField[1].getText(),                              textField[2].getText(), textField[3].getText(),                              textField[4].getText());                    controller.addContact(c);               } else if (event.getSource().equals(button[1])) {                    Contact c = new Contact(                              Integer.parseInt(textField[0].getText()),                              textField[1].getText(), textField[2].getText(),                              textField[3].getText(), textField[4].getText());                    controller.updateContact(c);                    System.out.println("update button clicked");               } else if (event.getSource().equals(button[2])) {                    Contact c = (Contact) controller.getContactList().get(index);                    controller.removeContact(c.getContactId());               } else if (event.getSource().equals(button[4])) {                    if (index > 0) {                         index--;                    } else                         event.consume();               } else if (event.getSource().equals(button[3])) {                    index = 0;               } else if (event.getSource().equals(button[5])) {                    if (index < controller.getContactList().size() - 1) {                         index++;                    } else                         event.consume();               } else if (event.getSource().equals(button[6])) {                    index = controller.getContactList().size() - 1;               }               populateForm(index);               populateTable();          }     }     private void populateForm(int i) {          if (controller.getContactList().isEmpty())               return;          Contact c = (Contact) controller.getContactList().get(i);          textField[0].setText(c.getContactId().toString());          textField[1].setText(c.getFirstName());          textField[2].setText(c.getLastName());          textField[3].setText(c.getEmail());          textField[4].setText(c.getPhone());     }     private void populateTable() {          table.getItems().clear();          table.setStyle(style);               table.setItems(controller.getContactList());          TableColumn contactIdCol = new TableColumn(     "Contact ID");          contactIdCol.setCellValueFactory(new PropertyValueFactory("contactId"));          TableColumn firstNameCol = new TableColumn("First Name");          firstNameCol.setCellValueFactory(new PropertyValueFactory("firstName"));          TableColumn lastNameCol = new TableColumn("Last Name");          lastNameCol.setCellValueFactory(new PropertyValueFactory("lastName"));          TableColumn emailCol = new TableColumn("Email");          emailCol.setCellValueFactory(new PropertyValueFactory("email"));          TableColumn phoneCol = new TableColumn("Phone");          phoneCol.setCellValueFactory(new PropertyValueFactory("phone"));          table.getColumns().setAll(contactIdCol, firstNameCol, lastNameCol,emailCol, phoneCol);     }}

Compile and Run the Application

We are now ready to compile and run our JavaFX / Hibernate / PostgreSQL app. To do so, just follow these two steps in Eclipse:

  1. Right click on Main.java from Project Explorer
  2. Select Run As and choose Java Application

The application will then compile and run. Figure 3 shows the application’s output.

JavaFX Hibernate PostgreSQL
Figure 3. Application Output: Here is the output after running our application.
Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: