Java Persistence (JPA/Hibernate)

This tutorial is more a theory with some demostration code that will be used in my Spring Boot tutorial, alot of Hibernate/JPA in Spring Boot is now masked from you but there are some areas I am going to cover in this tutorial that you need knowlegde of especially the mapping concepts, caching, query language, etc. You can come back to this tutorial from time to time understand the theory behind some of the Spring Boot JPA features.

Object/Relational Persistence

Object-Relational impedance mismatch is the set of technical difficulties that relate to converting database tables (relational model) into the programming language object structure (object model), basically mapping database tables to objects. There are various types of mismatches between the relational model and the object model:

Granularity The object model has various levels of granularity but a database table has only two, tables and columns, for example you could have two classes Person and Address but only one table that contains both this information.
Inheritance objects have the ability to inherit but database tables do not.
Identity Databases use a primary key to identify a row but Java uses both object identity (==) and equality (equals)
Associations In java you use references to associate objects and they can be bi-directional but in databases we use a foreign key which are not directional.
Data Navigation In Java you use the object graph to walk the associations, for example a Person object may contain references to an Address Object which in turn has references to a PostCode object, in order to get to the PostCode object you have walk both Person and Address objects. Databases use SQL joins, which joins tables together to retrieve data.

This is were Hibernate and JPA come in, Hibernate is a Object Relational Mapping framework and JPA is a framework that adds an extra layer of abstraction on top of a JPA provider like Hibernate. You can see in the below diagram that from the database access layer you use JPA/Hibernate/JDBC to retrieve data from the databse. There are other API's like Hibernate Native API that you can use instead of JPA, but JPA seems to be the standard at this point in time.


Before we jump into Hibernate/JPA I just wanted to show how object relational mapping worked before Hibernate/JPA, first you had to obtain a JDBC connection to the database, then prepare a SQL statement, add data, then execute the statement, this had to be repeated for all object/table mappings, you can see that below code is bulky and messy but it worked. A similar method was used to retrieve data from the database. Also if you notice SQL code is used and thus you need to know how to proram in SQL for a specific database (MySQL, Oracle, MSQL Server, etc)

Old way (pre hibernate/JPA)
public void persistData(Book book) {
  try {
	Class.forName("com.mysql.jdbc.Driver");
	connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bookstore", "root", "password"); 
			
	PreparedStatement stmt = connection.prepareStatement("INSERT INTO PUBLISHER (CODE, PUBLISHER_NAME) VALUES (?, ?)");
	stmt.setString(1, book.getPublisher().getCode());	
	stmt.setString(2, book.getPublisher().getName());			
	stmt.executeUpdate();

	stmt.close();
			
	stmt = connection.prepareStatement("INSERT INTO BOOK (ISBN, BOOK_NAME, PUBLISHER_CODE) VALUES (?, ?, ?)");
	stmt.setString(1, book.getIsbn());	
	stmt.setString(2, book.getName());
	stmt.setString(3, book.getPublisher().getCode());
	stmt.executeUpdate();
			
	stmt.close();
			
	so on and so on....................			
  } 
  catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } 
  finally { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } }
}

Hibernate/JPA Annotations

Hibernate is a Object Relational Mapping framework (ORM), basically it performs the CRUD operations (Create, Read, Update and Delete) on the database for you without haing to write any SQL code.

I am not really going to go into great depths of setting up Hibernate as it now not used that often with Spring Boot making so much easier to configure JPA and really being the default of choice in Java Microservices technology.

In the old days of Hibernate you used a configuration file and mapping files to configure the ORM as per below, Spring Boot makes this all very easy now and I will coer this in my Spring Boot tutorial.

Hibernate configuration (old way)
<hibernate-configuration>
    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/hello-world</property>
        <property name="connection.username">root</property>
        <property name="connection.password">password</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
         
        <!-- Use XML-based mapping metadata -->	
        <!-- <mapping resource="domain/Message.hbm.xml"/> -->
        
        <!-- Use Annotation-based mapping metadata -->
        <mapping class="entity.Message"/>            
        
    </session-factory>
</hibernate-configuration>

// Mapping file
<hibernate-mapping package="domain">
	<class name="Message" table="MESSAGE">
		<id name="id" column="ID">
			<generator class="native" />
		</id>
		<property name="text" type="string" column="TEXT"/>
	</class>
</hibernate-mapping>

The next evolution was JPA and annotations that allowed the ORM configuration to be performed inside the class instead of a mapping file, this made life very easy as you can see both the entity and mapping relationship in one file. There are many JPA annotation which I will cover in the next sections, below is an example of some of them

JPA Annotations example
@Entity        				// decalres the class as a Entity (persistence POJO)
@Table(name="message")			// maps this entity to a relational database table called message
public class Message {

	@Id 				// identifer property of this entity (normally primary key in database)
	@GeneratedValue(strategy=GenerationType.IDENTITY) 
	@Column(name="ID")	
	private Long id;

	@Column(name="TEXT") 		// specify a specific column to be used in the database
	private String text;
	
	public Message() {}
	public Message(String text) {
		this.text = text;
	}
	
	@Override
	public String toString() {
		return "Message [id=" + id + ", text=" + text + "]";
	}	
	
}

Transactions

Lastly I want to touch on transactions, I will be going into more details in other sections but wanted to give a basic overview on what transactions are in the hibernate world. A transaction is a group of operations that are run as a single unit of work, this means that the unit work must completey work or the whole unit is rolled back, its a all or nothing approach, Spring handles all transactions using annotations. As a note when using transactions you might hear the term automtic dirty checking, this simply means that the object has been modified and is now different from the database table, Hibernate will persist the object with the database using a SQL update statement.

Hibernate Transaction example
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction txn = session.getTransaction();
try {
  txn.begin(); 							// Start Transaction
        	
  Message msg = (Message) session.get(Message.class, 2L);

  Message msg = (Message) session.get(Message.class, 2L);
  msg.setText( “Hello Automatic Dirty Checking” );

  Message msg = (Message) session.get(Message.class, 2L);  
  session.delete(msg);
        			
  txn.commit(); 						// Commit Transaction
} catch(Exception e) {
  if(txn != null) { txn.rollback(); } 				// Roll back if any issues
  e.printStackTrace();
} finally {
  if(session != null) { session.close(); }
}