JPA (Java Persistence Annotation) is Java‘s standard solution to bridge the gap between object-oriented domain models and relational database systems. The idea is to map Java classes to relational tables and properties of those classes to the rows in the table. This changes the semantics of overall experience of Java coding by seamlessly collaborating two different technology within same programming paradigm. This article provides an overview and its supporting implementation in Java.
An Overview
Relational databases are perhaps the most stable of all the persistence technologies available in computing instead of all the complexities involved with it. That’s because today, even in the age of so-called “big data,” “NoSQL” relational databases are consistently in demand and thriving. Relational databases are stable technology not by mere words but by its existence through the years. NoSQL may be good for dealing with large quantities of structured data in the enterprise, but the numerous transactional workloads are better handled through relational databases. Also, there are some great analytical tools associated with relational databases.
To communicate with relational database, ANSI has standardised a language called SQL (Structured Query Language). A statement written in this language can be used both for defining and manipulating data. But, the problem of SQL in dealing with Java is that they have a mismatched syntactical structure and very different at the core, meaning, SQL is procedural whereas Java is object oriented. So, a working solution is sought such that Java can speak in object oriented-way and the relational data base would still be able to understand each other. JPA is the answer to that call and provides the mechanism to establish a working solution between the two.
Relational to Object Mapping
Java programs interact with relational databases by using the JDBC (Java Database Connectivity) API. A JDBC driver is the key to the connectivity and allows a Java program to manipulate that database by using the JDBC API. Once the connection is established, Java program fires SQL queries in the form of Strings to communicate create, insert, update, and delete operations. This is sufficient for all practical purposes, but inconvenient from a Java programmer’s point of view. What if the structure of relational tables can be remodeled into pure Java classes and then you could deal with them in the usual object-oriented way? The structure of a relational table is a logical representation of data in a tabular form. Tables are composed of columns describing entity attributes and rows are the collection of entities. For example, an EMPLOYEE table may contain following entities with their attributes.
Emp_number | Name | dept_no | Salary | Place |
112233 | Peter | 123 | 1200 | LA |
112244 | Ray | 234 | 1300 | NY |
112255 | Sandip | 123 | 1400 | NJ |
112266 | Kalpana | 234 | 1100 | LA |
Rows are unique by primary key (emp_number) within a table; this enables a quick search. A table may be related to one or more tables by some key, such as a foreign key (dept_no), that relates to the equivalent row in another table.
According to the Java Persistence 2.1 specification, JPA adds support for schema generation, type conversion methods, use of entity graph in queries and find operation, unsynchronized persistence context, stored procedure invocation, and injection into entity listener classes. It also includes enhancements to the Java Persistence query language, the Criteria API, and to the mapping of native queries.
In short, it does everything to provide the illusion that there is no procedural part in dealing with relational databases and everything is object oriented.
JPA Implementation
JPA describes relational data management in Java application. It is a specification and there are a number of implementations of it. Some popular implementations are Hibernate, EclipseLink, and Apache OpenJPA. JPA defines the metadata via annotations in Java classes or via XML configuration files. However, we can use both XML and annotation to describe the metadata. In such a case, the XML configuration overrides the annotations. This is reasonable because annotations are written with the Java code, whereas XML configuration files are external to Java code. Therefore, later, if any, changes need to be made in the metadata; in the case of annotation-based configuration, it requires direct Java code access. This may always not be possible. In such a case, we can write new or changed metadata configuration in an XML file without any hint of change in the original code and still have the desired effect. This is the advantage of using XML configuration. However, annotation-based configuration is more convenient to use and is the popular choice among programmers.
- Hibernate is the popular and most advanced among all JPA implementations due to Red Hat. It uses its own tweaks and added features that may be used in addition to its JPA implementation. It has a bigger community of users and is well documented. Some of the additional proprietary features are support for multi-tenancy, joining unassociated entities in queries, timestamp management, and so forth.
- EclipseLink is based upon TopLink and is a reference implementation of JPA versions. It provides standard JPA functionalities apart from some interesting proprietary features, such as support from multi-tenancy, database change events handling, and so on.
Using JPA in a Java SE Program
To use JPA in a Java program, you need a JPA provider such as Hibernate or EclipseLink, or any other library. Also, you need a JDBC driver that connects to the specific relational database. For example, in the following code, we have used the following libraries:
- Provider: EclipseLink
- JDBC Driver: JDBC Driver for MySQL (Connector/J)
We’ll establish a relationship between two tables—Employee and Department—as one-to-one and one-to-many, as depicted in the following EER diagram (see Figure 1).
Figure 1: Table relationships
The employee table is mapped to an entity class using annotation as follows:
package org.mano.jpademoapp;
import javax.persistence.*;
@Entity
@Table(name = "employee")
public class Employee {
@Id
private int id;
private String name;
private String phone;
private String email;
@OneToOne
private Department department;
public Employee() {
super();
}
public Employee(int id, String name, String phone,
String email) {
super();
this.id = id;
this.name = name;
this.phone = phone;
this.email = email;
}
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 getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
And, the department table is mapped to an entity class as follows:
package org.mano.jpademoapp; import java.util.*; import javax.persistence.*; @Entity @Table(name = "department") public class Department { @Id private int id; private String location; @OneToMany private List<Employee> employees = new ArrayList<>(); public Department() { super(); } public Department(int id, String location) { super(); this.id = id; this.location = location; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public List<Employee> getEmployees() { return employees; } public void setEmployees(List<Employee> employees) { this.employees = employees; } }
The configuration file, persistence.xml, is created in the META-INF directory. This file contains the connection configuration, such as JDBC driver used, user name and password for database access, and other relevant information required by the JPA provider to establish the database connection.
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance" xsi_schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="JPADemoProject" transaction-type="RESOURCE_LOCAL"> <class>org.mano.jpademoapp.Employee</class> <class>org.mano.jpademoapp.Department</class> <properties> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" /> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/testdb" /> <property name="javax.persistence.jdbc.user" value="root" /> <property name="javax.persistence.jdbc.password" value="secret" /> <property name="javax.persistence.schema-generation .database.action" value="drop-and-create"/> <property name="javax.persistence.schema-generation .scripts.action" value="drop-and-create"/> <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/> </properties> </persistence-unit> </persistence>
Entities do not persist themselves. Logic must be applied to manipulate entities to manage their persistent life cycle. The EntityManager interface provided by the JPA lets the application manage and search for entities in the relational database. We create a query object with the help of EntityManager to communicate with the database. To obtain EntityManager for a given database, we’ll use an object that implements an EntityManagerFactory interface. There is a static method, called createEntityManagerFactory, in the Persistence class that returns EntityManagerFactory for the persistence unit specified as a String argument. In the following rudimentary implementation, we have implemented the logic.
package org.mano.jpademoapp; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; public enum PersistenceManager { INSTANCE; private EntityManagerFactory emf; private PersistenceManager() { emf=Persistence.createEntityManagerFactory("JPADemoProject"); } public EntityManager getEntityManager() { return emf.createEntityManager(); } public void close() { emf.close(); } }
Now, we are set to go and create the application’s main interface. Here, we have implemented only the insertion operation for the sake of simplicity and space constraints.
package org.mano.jpademoapp; import javax.persistence.EntityManager; public class Main { public static void main(String[] args) { Department d1=new Department(11, "NY"); Department d2=new Department(22, "LA"); Employee e1=new Employee(111, "Peter", "9876543210", "peter@gmail.com"); Employee e2=new Employee(222, "Ronin", "993875630", "ronin@gmail.com"); Employee e3=new Employee(333, "Kalpana", "9876927410", "kalpana@gmail.com"); Employee e4=new Employee(444, "Marc", "989374510", "marc@gmail.com"); Employee e5=new Employee(555, "Anik", "987738750", "anik@gmail.com"); d1.getEmployees().add(e1); d1.getEmployees().add(e3); d1.getEmployees().add(e4); d2.getEmployees().add(e2); d2.getEmployees().add(e5); EntityManager em=PersistenceManager .INSTANCE.getEntityManager(); em.getTransaction().begin(); em.persist(e1); em.persist(e2); em.persist(e3); em.persist(e4); em.persist(e5); em.persist(d1); em.persist(d2); em.getTransaction().commit(); em.close(); PersistenceManager.INSTANCE.close(); } }
Note: Please consult the appropriate Java API documentation for detailed information on APIs used in the preceding code. |
Conclusion
As should be obvious, the core terminology of JPA and Persistence context is vaster than the glimpse given here, but beginning with a quick overview is better than long intricate dirty code and their conceptual details. If you have a little programming experience in core JDBC, you’ll undoubtedly appreciate how JPA can make your life simpler. We’ll dive deeper into JPA gradually as we go along in forthcoming articles.