Hibernate/JPA – Transparent Persistence

This is the first of a series of posts to provide an insight about Object Oriented Persistence, using an Object Relational Mapping tool like Hibernate. We will start with few concepts.

Object Relational Mapping

JPA (Java Persistence API) is the specification created by the Java Community for persisting objects in the Java Programming Language. And Hibernate ORM (Object-Relational Mapping) is an implementation of that specification.

The goal of an ORM is to let you design your application without taking care about a persistent mechanism. Design your domain model, implement it and test it, just persisting it on memory. The ORM, after configured, will take care of persist your model objects in a persistent storage (in this case a relational storage).

Let illustrate the previous sentence with an example. Suppose I have to model Departments and Employees, where each department knows their employees. I would write the following two classes:

public class Department {

	private String name;

	private Collection employees = new ArrayList<>();

	public Department(String name) {
		this.name = name;
	}

	public void addEmployee(Employee m) {
		employees.add(m);
	}

	public int totalEmployees() {
		return employees.size();
	}
}

public class Employee {

	private float salary;
	private String name;

	public Employee(String name, float salary) {
		this.name = name;
		this.salary = salary;
	}

	public String name() {
		return this.name;
	}

	public float salary() {
		return this.salary;
	}
}

And a test to cover that piece of functionality:

@Test
public void a_new_employee_is_added_then_it_increments_the_total() {

	Department rockAndRoll = new Department("Rock And Roll");
	Employee angus = new Employee("Angus Young", 1239f);
	rockAndRoll.addEmployee(angus);
	Assert.assertEquals(1, rockAndRoll.totalEmployees());
}

And ORM like Hibernate will let you write something like the test above and make the relation between the employee angus and the rockAndRoll department persistent, isn’t that great?

You may be asking, I don’t have to deal with the JDBC API ? I don’t have to write any SQL Statement ? No! you can just write code like this:

	department1.addEmployee(new Employee("José", 10503f));

then José will be persisted and be part of department1.

The real beauty of an ORM is the transparent persistence that provides, which will let you write plain Java objects and make them persistent with little work.

Transparent Persistence

To understand how transparent persistence works, we have to understand the following concepts. The first one is a concept called Persistence Context. The Persistence Context is the place used by the ORM to keep track of your Objects changes in order to persist them at a later point. In JPA this context is managed by the javax.persistence.EntityManager class. Each Java Object in a persistence context will have one of these states:

  • new, or transient: the object has just been instantiated and is not associated with a persistence context. It has no persistent representation in the persistent storage.
  • persistent: the object is associated with a persistence context and has representation in the persistent storage.
  • detached: the object is no longer associated with a persistence context (usually because the persistence context was closed).
  • removed: the object is associated with a persistence context, however it is scheduled for removal from the persistent storage.

The other important concept is called Persistence by Reachability. Which states that any transient object that gets related to a persistent object, will become persistent too.

These two concepts implemented make the so called Transparent Persistence possible.

Let’s put these concepts in code. The following code block defines the persistence context, and illustrate the persistence by reachability.

	EntityManager em = //obtain the EntityManger instance
	EntityTransaction tx = null;
	try {
		tx = em.getTransaction();
		tx.begin();

		Department dept1 = em.find(Department.class, "department1");
		dept1.addEmployee(new Employee("José", 10503f));

		tx.commit();
	} catch (Exception e) {
		if (tx != null) {
			tx.rollback();
		}
		throw new RuntimeException(e);
	} finally {
		em.close();
	}

On line 7, I’m getting the Department department1 from the persistent storage and on line 8 just append a new employee to it. Due to department1 is in the persistent state, by reachability the transient instance of Employee (José) will be persisted and attached to the department.

A similar scenario that also illustrate the two concepts described above is when having two transient objects, Department and Employee, and the Employee has a relation (or is part of) to the Department. If I persist the Department, then the Employee will be persisted too. Code shown below:

	//start persistence context
	...
	Department dept1 = new Department("department1");
	dept1.addEmployee(new Employee("José", 10503f));

	em.persist(dept1);
	...
	//close persistence context

Below are some more examples that continue demonstrating the beauty of the transparent persistence. Changing the name of a department:

	//start persistence context
	...
	Department dept1 = new Department("department1");
	dept1.setName("anotherDepartmentName");
	...
	//close end of persistence context

Removing an employee from a department:

	//start persistence context
	...
	Department dept1 = // retrieving from the storage
	dept1.removeEmployee(new Employee("Javier", 4569f));
	...
	//close end of persistence context

Lets write something a bit richer. Suppose each department gains 5 points each time a woman joins them and 3 points for men and subtract 5 points when an employee leaves them. I would change the Department class in the following way:

public class Department {

	...

	private int points;

	public void addEmployee(Employee e) {
		points += e.points();
		employees.add(e);
	}

	public void removeEmployee(Employee e) {
		points -=  e.points();
		employees.remove(e);
	}

	...
}

Now, if I add an employee to a department:

	//start persistence context
	...
	Department dept1 = // retrieving from the storage
	dept1.addEmployee(new Employee("Julio", 6205f));
	...
	//close end of persistence context

in addition to persist the new Employee and the relation with the department, it will also persist the new points obtained by adding employees to the department. It will be done executing two SQL statements wrapped in a transaction. Something like:

insert into employees values("Julio", 6205f, "department1");
update departments set points = ? where id = "department1";

That was transparent right? You can visualize all the work done by the ORM for you. This means that you can focus the effort on make a good Object Domain Model (and with good I mean, with business logic inside and not an anemic model), test it on memory, get a high test coverage, and let the ORM to take care of the persistence.

The support of Transparent Persistence is what makes Hibernate an invaluable tool.

Posted in Persistence Tagged with: , , , ,