Introduction
I have been developing Java applications for a long time and the major difficulty was the coding part related to database manipulations. Recently I shifted to use Object-Relational Mapping through JPA. I have used TopLink, EclipseLink, and Hibernate. Although I could achieve the functionally I want with ease, the output was slower when compared to normal method of database connection with JDBC. Then I used some Object databases like neoDatis. They were quite faster, yet I was reluctant to proceed as most of them had their own standards and did not comply to JPA. Finally I found the ObjectDB, an Object database for Java which completely supports JPA.
also read:
This article demonstrates how to create a simple JAVA Standard application in NetBeans using ObjectDB and JPA. The demonstrated application uses JPA to store and retrieve objects of two entity classes from an Embedded ObjectDB. It will demonstrate the capabilities of ObjectDB and JPA, which include persisting objects, removing saved objects and retrieving persisted objects. For this tutorial, besides the Java JDK and the NetBeans IDE,you only need to download
and extract the ObjectDB distribution zip file.The distribution zip contains everything necessary without any external dependency.
This tutorial consists of the following steps
- Step 1: Create an ObjectDB Enabled Java SE Project
- Step 2: Define a JPA Entity Classes
- Step 3: Add a Main Class
- Step 4: Design a Graphical User Interface
- Step 5: Code user-defined Methods
- Step 6: Code Events of Controls
- Step 7: Run the program
Step 1: Create an ObjectDB Enabled Project
We start by creating a new NetBeans Project.
From the menu, select File > New to get the [New Project] dialog box
Select Java from Categories and
Java Application from Projects and click Next
Choose a Project Name (e.g City
Set the name of the Main class to main.Main – This will create a package named main and a Java Class named Main with the public static void main(String[] args){}
Click Finish to create the project.
Then ObjectDB/JPA support has to be added to the project
Download the ObjectDB installation files from the download page. The downloaded file, objectdb-(version).zip file has to be extracted to a folder like D:\Program Files\ObjectDB.
Find the project in Project window.If project window is not visible, press ctrl + 1 to get the project window. Right click on the project to get the popup menu and select the Properties to get the [Project Properties] window.
Click on Libraries under categories of the Project Properties Window. No libraries are listed at the moment under Compile tab.
Click on Add Jar/Folderand locate the objectdb.jar file inside the bin folder of ObjectDB extracted directory.
This will add the objectdb.jar file to the libraries and now the project is JPA/ObjectDB enabled.
Step 2: Define JPA Entity Classes
First we will create a new package named ‘bean’ to include entity classes.
Right click on City node or Source Packages node to get the popup menu and select New. The recent selections are listed and find the Java Package.
If it is not listed in popup menu, select Other to get the [New File] window. Select Java from Categorise and Java Package from File Types.
In the New Java Package window, enter bean as the package name and make sure Source Packages is selected as the location. The click Finish to create the package.
We can create Entity classes by creating a java class and adding @Entity annotation just above the class definition line. NetBeans also allow us to directly add an Entity Class.
To create a new entity class right click on bean and select New > Entity Class. If Entity Class is not listed under recent types, select Other to get the New File Window. Select Persistence under categories and Entity Class as the file type
In the New Entity Class window, type City as the Class Name. Make sure bean is selected as the package. By default, the Create Persistence Unit is selected and un-check it as it is not essential to have a persistence unit to work with ObjectDB. In this tutorial we do not use a persistent unit. Then click Finish to generate State Entity Class.
You will see that NetBeans has created most of the coding for us and we can edit the code to suit our needs.
package bean; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Country implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public int hashCode() { int hash = 0; hash += (id != null ? id.hashCode() : 0); return hash; } @Override public boolean equals(Object object) { // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof Country)) { return false; } Country other = (Country) object; if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { return false; } return true; } @Override public String toString() { return "bean.Country[id=" + id + "]"; } }The class generated by NetBeans implements Serializable interface and it is not essential when working with ObjectDB. Therefore we can safely delete
- import java.io.Serializable (full line – no 3) ; and
- implements Serializable (in line no 10)
from the generated code.
The generated code has created an id field from Line 12 to 14. As we need a unique sequentially increasing id for the each newly created country object, we have to change
@GeneratedValue(strategy = GenerationType.AUTO) (to) @GeneratedValue(strategy = GenerationType.IDENTITY).This will make sure when a new Country object is persisted, a unique sequential id specific to Country type will be automatically added.
We need to add a String field named ‘name’ as a property to the Country class. Add
String name;
to line no 15 of the above coding. It is needed to add getter and setter to the new property. Press Alt + Ins to get code insert code menu and select getter and setterThen check [name : String] and click Generate to insert getters and setters.
We also have to modify the toString method to return the name of the country.
Finally the new coding of Country Entity will look like this.
package bean; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Country { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public int hashCode() { int hash = 0; hash += (id != null ? id.hashCode() : 0); return hash; } @Override public boolean equals(Object object) { // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof Country)) { return false; } Country other = (Country) object; if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { return false; } return true; } @Override public String toString() { return name; } }The next step is to create a New Entity class named City. The very same above steps have to be followed and then add the following modifications.
Just below where the String name; is inserted, type Country country; This means every object made from City class has a reference to another persisting object from the Entity Country. Any single city has only one country. But a single country can have any number of cities. So there is one-to-many relationship. Just above the place we define Country country; in City Entity Class, we have to add the @ManyToOne annotation. As soon as we finish typing the Country country; NetBeans give us warning to add the annotation and all we have to select the correct one, [Create Unilateral ManyToOne Relationship]. Generate the getter and setter for the Country country; as described above.Finally the new coding of City entity will look like this.
package bean; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToOne; @Entity public class City{ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; String name; @ManyToOne Country country; public Country getCountry() { return country; } public void setCountry(Country country) { this.country = country; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public int hashCode() { int hash = 0; hash += (id != null ? id.hashCode() : 0); return hash; } @Override public boolean equals(Object object) { // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof City)) { return false; } City other = (City) object; if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { return false; } return true; } @Override public String toString() { return name; } }Step 3: Design GUI
We have to create a graphical user interface as the front end to allow user to store and retrieve data. In this example, there is a list of cities on the left side and when a city is clicked, the details are displayed on the right side. For the sake of simplicity, the details of the city are limited to the name of the city and the country to which the city belongs. After selecting a city we can click the Edit button so that we can start changing the details of the selected city. We can select a country and then click the Delete button to remove the it from the list. When we click the Add button, we can start entering data to add a new city. Unless we click the Edit or Add button, the details of the city cannot be changed.
While details of a city is being add or changed after clicking the Add or Edit buttons, we cannot select the cities from the lstCity as it disabled(Edit Mode). After editing the details or entering new details of a city, we can click the Save button to save them or the Cancel button to discard the changes. style=’mso-spacerun:yes’> This will again allow us to select any City from the lstCity. (Select Mode)
There is a textbox to enter a new Country and if we click the Add New Country button, the country will be added to the existing list of countries.
The Close button on the lower down corner of the form will terminate the application.
Right click on City node or Source Packages node to get the popup menu and select New. The recent selections are listed and find the Java Package. If not listed in the list, select Other to get the New File window. Select Java from the Categories list and then Java Package from the File Types List. We will then get the New Java Package window. Enter a name like “gui” to the package we are going to store Graphical User Interfaces inside this package. Make sure Source Packages is selected as the location and then click on the Finish Button.
Right Click over the gui package to get the popup menu. Select New and then JFrame. If JFrame is not listed, select Other to get the New File window and select Swing GUI Forms from the categories and then JFrame from the File Types to get New JFrame Form window. Enter a name like frmCity and after making sure gui is selected as the Package, click the Finish button.
The projects window will show the frmCity under gui package. Double click on frmCity or right click on frmCity and then select Open to design the form. Drag and drop the following components from the component palette to the form to design the following look. If the component palette is not visible on the right side choose Window menu and then Reset Windows. To change the text displayed on the controls, right click on the component and then select Edit Text. Right click on the control and select Change Variable Name to change the name of the control which is used in coding to identify the controls.
Component Name Text JLabel lblCities Cities JList lstCity JButton btnAdd Add JButton btnEdit Edit JButton btnDelete Delete JTextField txtCountry JButton btnAddNewCountry Add New Country JLabel lblCity City JTextField txtCity JComboBox cmbCountry JButton btnSave Save JButton btnCancel Cancel JButton btnClose CloseStep 4: Code the main Method
After adding the controls we can run the program to see whether we have arranged the swing components properly.
Open the Main.java in the main package. There is a public static void main method and inside that we have to call the JFrame frmCity to be displayed. The following code has to be added.
package main; import gui.frmCity; public class Main { public static void main(String[] args) { frmCity city = new frmCity(); city.setVisible(true); } }Line 3 imports the frmCity file as it is inside a different package then the Main.java file.
Line 8 create a new object from the frmCity class and in the next line the form is displayed.
Then press F6, or select Run and Run Project (City) from the menu. It can also be done by clicking the Play button in the Run toolbar.
If the form we designed is not displayed, check whether the Project is the main project. You can right click on the City Project and select Set as the Main Project.
If still the form is not displayed, open the Project Properties and look under Run and check Main.main is selected as the Main Class.
Step 5: Code user-defined methods
Now we have to add functionality to this project. Although ideally the functionality has to be added as separate manager classes, for the sake of simplicity, all the coding necessary are done within the frmCity itself.
The important functionalities displayed are connecting to a JPA database and then saving, retrieving, updating and removing objects of entity classes.
The existing cities are listed in the lstCity. When it is clicked, the details of the selected city are displayed on the right side. If we want to edit details, we have to click btnEdit. If we want to add a new city, have to click the btnAdd. After clicking btnAdd or btnEdit, can enter name of the city to txtCity and select the country from cmbCountry.
Until we click btnSave or btnCancel, we cannot access lstCity, btnAdd or btnEdit. If btnSave is clicked, the details are saved, and program is again set to the select mode.If we enter a name of a country to txtCountry and click the btnAddNewCountry, the new country will be saved and appear in the cmbCountry.
btnClose
will close the application when clicked. There functionalities are coded in to several reusable methods.Before proceeding, we have to add the following imports. They can be added later while typing the code which needs the relevant import, but in that case we have to be very careful to select the appropriate one out of the list.
The Imports are as follows.
import bean.City; import bean.Country; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.TypedQuery; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListModel; import javax.swing.JOptionPane;The JPA database connection is represented by the EntityManager interface. style=’mso-spacerun:yes’> There has to be a database to get a connection. Like vice, we need an instance of EntityManagerFactory to get an instance of EntityManager. EntityManagerFactory represents the database. When we need any modification to the database, we need an EntityTransaction instance.
One EntityManagerFactory can have any number of EntityManagers, that is, there is one-to-many relationship. One EntityManager can have only one EntityTransaction and that is a one to one relationship.
As the program works with a single database, one EntityManagerFactory instance is all that necessary for the whole program. It will efficiently manage all the connections represented by several EntityManager Instances.
Inside the frmCity we need to retrieve saved objects, save new ones, update old ones and delete existing ones. As all these actions have to be inter-related, we have to manage them all through a single EntityManager. If we retrieve objects from one instance of EntityManager and modify them and then save them using another instance of EntityManager, the
objects will not get updated and instead new set of fresh objects will be saved with the modified attributes without modifying the previously retrieved object.As there is only one EntityManagerFactory for the whole program, it should have an application vide scope. EntityManager should have access by all the methods in the class. In this tutorial, we define both EntityManager and EntityManagerFactory as class level variables.
To start with, we can define an EntityManagerFactory and assign a null value. Then define an EntityManager and assign it also with a null value.
Immediately below the first line of the class, we can define these as below.
EntityManager em = null; EntityManagerFactory emf = null;If you have not already done the importing, the EntityManager and the EntityManagerFactory will be underlined in red. At the start of the line, a light bulb (“Fixable Hint”) appears and if we take the mouse over that, it says as “Cannot find symbol”. If we click on the hint, select “Add Import for Javax.persistence.EntityManager”.
When the program starts we have to check whether there is an existing EntityManagerFactory instance, and if not, we can create one instance. Then we check whether the instance of an EntityManager, and if not available, we create new one
from the EntityManagerFactory.Java persistence requires a name of a Persistence unit as a parameter when an EntityManagerFactory is created. But if we are developing an Embedded ObjectDB application, we can simple use a relative or absolute path of a database with *.odb extension instead of a name of a Persistence Unit. This is coded under a method called “connectDatabase”.
private void connectDatabase(){ if (emf == null) { emf = Persistence.createEntityManagerFactory("city.odb"); } if (em == null) { em = emf.createEntityManager(); } }If there is an already existing valid ObjectDB file in the location, the program will use it. style=’mso-spacerun:yes’> If no such database is available, one will be created at the location. When simply the name of the database is given, the database is created in the NetBeans project folder. style=’mso-tab-count:1′> As we are using an Embedded database, one connection at a time is allowed. If we want to use multiple simultaneous connections over TCP/IP connection, we can easily define a Persistence Unit and add that functionality as described in another tutorial.
All the countries have to be listed in the cmbCountry. When the form is first displayed and when a new country is added, this JCombo has to be refilled. Filling this JCombo with the countries is done in a method called fillCountries. This code will fill the JCombo with objects retrieved from JPA. Make sure you add the correct imports at the beginning, namely javax.persistence.Query and javax.swing.DefaultComboBoxModel.
The items already added to the JCombo can be cleared with cmbCountry.removeAllItems() method.
We first create an instance of TypedQuery from the EntityManager. style=’mso-spacerun:yes’> When creating an instance of a query, we can give one of several parameters including a String type JPAQuery which is used in this example. JPA query is very similar to SQL, but is more flexible.The query, “SELECT cu FROM Country cu ORDER BY cu.name”, can retrieve all the Country type objects from the database ordered by the name attribute. The results can be assigned to a List( java.util.List), which in turn converted to an array of Country type objects. A new instance of a DefaultComboBoxModel is created with these objects and it is assigned as the model of the JComboBox.This will fill the cmbCountry with all the saved country objects.
private void fillCountries() { cmbCountry.removeAllItems(); try { TypedQuery query = em.createQuery("SELECT cu FROM Country cu ORDER BY cu.name", Country.class); List countryList = query.getResultList(); Object[] countries = countryList.toArray(); DefaultComboBoxModel dcm = new DefaultComboBoxModel(countries); cmbCountry.setModel(dcm); } catch (Exception e) { JOptionPane.showMessageDialog(rootPane, e); } }All the cities are listed in lstCity and it is done inside a method called fillCities. As described previously, JPA Query will retrieve all the city type objects. We can directly pass these objects to the JList using lstCity.setListData(cities)
method.private void fillCities() { DefaultListModel dlm = new DefaultListModel(); dlm.removeAllElements(); lstCity.setModel(dlm); try { TypedQuery query = em.createQuery("SELECT ci FROM City ci ORDER BY ci.name" , City.class); List cityList = query.getResultList(); Object[] cities = cityList.toArray(); lstCity.setListData(cities); } catch (Exception e) { JOptionPane.showMessageDialog(rootPane, e); } }When a city is selected from the lstCity, the details of the selected city are displayed on the right side and the method to call is listed below. Cities are stored in the JList and we select the user selection and display the details. We could have retrieved the object directly from the database, but it is not necessary in this example.
private void displayCityDetails() { City city = (City) lstCity.getSelectedValue(); if (city != null) { txtCity.setText(city.getName()); cmbCountry.setSelectedItem(city.getCountry()); } }When a city is deselected, the details of the previously selected city have to be cleared from the right side and the method to call is listed below.
private void clearCityDetails() { txtCity.setText(null); cmbCountry.setSelectedItem(null); }When the window is first displayed, we can select any city, but we must not be able to edit it or add a new one until we purposely click the “Edit” button or “Add” button respectively. The controls are prepared to such an editable stated from the following method.
private void setToEdit() { btnAdd.setEnabled(false); btnEdit.setEnabled(false); btnDelete.setEnabled(false); lstCity.setEnabled(true); btnSave.setEnabled(true); btnCancel.setEnabled(true); txtCity.setEditable(true); cmbCountry.setEnabled(true); }After editing or adding a new city, we can save the changes by clicking “Save” button or ignore the changes by clicking “Cancel” button. After that the window goes to a state where we can select a city, but cannot edit it. That is achieved by the following set of codes.
private void setToSelect() { btnAdd.setEnabled(true); btnEdit.setEnabled(true); btnDelete.setEnabled(true); lstCity.setEnabled(true); btnSave.setEnabled(false); btnCancel.setEnabled(false); txtCity.setEditable(false); cmbCountry.setEnabled(false); }Step 6: Code Events of the Controls
After coding our own methods, we can code the events for each control.
Get the design view of the frmCity JFrame and right click on it avoiding any controls. style=’mso-spacerun:yes’> Select Events, Window and windowOpened[formWindowOpened] to get the code block where we can write for all what we should do when the window is opened. We call the methods needed to connect to database, to fill cmbCountry with list of countries, to fill the lstCity with cities and to prepare the form to a selectable state like follows.
private void formWindowOpened(java.awt.event.WindowEvent evt) { connectDatabase(); fillCountries(); fillCities(); setToSelect(); }As describe previously, get the btnAddNewCountryActionPerformed event and include this code needed to add a new country to the database.
private void btnAddNewCountryActionPerformed(java.awt.event.ActionEvent evt) { Country country = new Country(); country.setName(txtCountry.getText()); em.getTransaction().begin(); em.persist(country); em.getTransaction().commit(); fillCountries(); txtCountry.setText(null); }Write click on lstCity, select Events, ListSelection and valueChanged to get the lstCityValueChanged.
private void lstCityValueChanged(javax.swing.event.ListSelectionEvent evt) { displayCityDetails(); }btnAddwill prepare the form to add a new City. It will clear the existing entries, selecting a city is disabled and can select to save or cancel after entering the data. The following code will achieve that functionality.
private void lstCityValueChanged(javax.swing.event.ListSelectionEvent evt) { displayCityDetails(); } private void btnAddActionPerformed(java.awt.event.ActionEvent evt) { clearCityDetails(); lstCity.clearSelection(); setToEdit(); txtCity.requestFocus(); }btnEdit will prepare the form to edit a selected country. When the user is editing a city, selecting a city is disabled, but can change the details as necessary. The following code inside btnEditActionPerformed is achieving that.
private void btnEditActionPerformed(java.awt.event.ActionEvent evt) { if (lstCity.getSelectedValue() == null) { JOptionPane.showMessageDialog(null, "Select one to edit"); lstCity.requestFocus(); } else { setToEdit(); txtCity.requestFocus(); txtCity.selectAll(); } }Code under the ActionPerformed of btnDelete will remove the selected item from the database. Removing an object makes a change to the database, hence we need to get the EntityTransaction instance to begin with. The object is marked to be removed when
em.remove
is called and as the commit method of the EntityTransaction instance is called, the object is physically erased from the database.private void btnDeleteActionPerformed(java.awt.event.ActionEvent evt) { City city = (City) lstCity.getSelectedValue(); if (city == null) { JOptionPane.showMessageDialog(null, "Select one to delete"); lstCity.requestFocus(); return; } else { em.getTransaction().begin(); em.remove(city); em.getTransaction().commit(); } fillCities(); clearCityDetails(); setToSelect lstCity.requestFocus(); }btnSave will save a new Object if the user had previously clicked the “Add” button. If the “Edit” button was clicked previously, it will update the changes done to the selected object. When the Add button is clicked, the selection in the lstCity is cleared. In that case, lstCity.getSelectedValue() returns a null value. In that case it creates a new instance of city and assign the user entered values. Now we are going to save the newly created object. As it changes the database, we must get EntityTransaction instance of the em EntityManager and call the begin method. When we call the persist method of the EntityManager instance, it marks the object to be saved to the database. No physical change occurs until we call the commit method of the EntityTransaction, which save the object in the database. Clicking the Edit button do not clear the selection of the lstCity and lstCity.getSelectedValue() will return a city object. In that case, new attributes entered by the user is done to that object. We get the EntityTransaction and call the begin method. As em is the very same EntityManager which retrieved the city object we are going to update, we do not have to order to save it explicitly. Just calling the commit method of the EntityTransaction will update the object in the database with new values. The id value can NOT be updated like this and it will throw an exception. If we call the persist method of another EntityManager, this very same object would have consider as a new object, and the update will not occur as expected. We must use the commit method related to the very same EntityManager which we used to retrieve it.
private void btnSaveActionPerformed(java.awt.event.ActionEvent evt) { City city = (City) lstCity.getSelectedValue(); if (city == null) { city = new City(); city.setCountry((Country) cmbCountry.getSelectedItem()); city.setName(txtCity.getText()); em.getTransaction().begin(); em.persist(city); em.getTransaction().commit(); } else { city.setCountry((Country) cmbCountry.getSelectedItem()); city.setName(txtCity.getText()); em.getTransaction().begin(); em.getTransaction().commit(); } fillCities(); setToSelect(); lstCity.requestFocus(); }btnCancel just clears the entered values and make the application able to select cities.
private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) { clearCityDetails(); setToSelect(); }When the window is closing, we have to close the EntityManager. As there is only one window, the same event can be considered as closing the whole application. We can therefore close the EntityManagerFactory in the window closing event.
private void formWindowClosing(java.awt.event.WindowEvent evt) { em.close(); emf.close(); }Step 7: Code Run the Program
We have completed all the coding. NetBeans also has created some codes for us when the JFrame and controls are added to application. If you run the application, you will get all the functionality. If there is any error, first check whether you have add the support for ObjectDB as described above.
You can download the source code either with or without the libraries from this article.