No matter what technology or framework you choose for your back-end services, bet that there will always be some database communication. Knowing and having the right backend technology to define your database schema, relationships and everything else is very important. JPA stands for Java Persistence API and it is a specification for managing data using different types of databases in Java applications. JPA lets us define our entities and mark them as database entities ready for mapping.
JPA lets you define objects that are going to be persisted into the database because not all objects are there to be persisted.
And what is Hibernate? Hibernate is one of the most popular ORM frameworks. Hibernate lets you map your application domain entities into database tables. Hibernate is the JPA implementation.
I’ll show you some basics of working with JPA and Hibernate.
We’ll provide the database connection information using the application.properties in our resources package. I’m using the in-memory H2 database for this demo.
I’ve included some additional properties such as a custom server port for our application, JPA properties for enabling logging for our queries.
Let’s create our first Employee entity.
Wow, plenty of new words and stuff, don’t worry, we’ll go through this structure now and explain how does JPA maps this to our relational DB table.
@Entity — the key to mapping our class to a table in the database, this will make the JPA aware of our class-to-table relation.
@Table — we can specify some table-specific things using this annotation such as name because in most cases, an entity name and the table name won’t be the same.
@Id — we’ve marked our “id” property with this annotation to tell JPA that this will be our primary key in the database.
@GeneratedValue — this is what we use to tell that Hibernate will generate a value for this field using the IDENTITY strategy, meaning it will use the AUTO_INCREMENT column in the database.
Default constructor — JPA needs this constructor when marking an entity as persistent. When Hibernate creates an instance of this entity, it will need the no-args constructor to create it.
We’re using an in-memory H2 database and we are going to seed some data to it on the application startup. The way we can achieve this is to define data.sql file in our resources directory and insert one row.
insert into employee(id, first_name,last_name,email,address)
values (1001, 'joe','doe','firstname.lastname@example.org','test');
As soon as your application starts, the script will be executed. Now let’s see if it actually executed and inserted our data.
There in the JDBC URL, you need to specify your connection URL, the same way we did in our application.properties.
We are going to create a repository class where we’ll do all our database operations. We’ll have our EntityManager autowired. But we already have our data source configured, so we can already autowire our EntityManager instance. How is this possible? Well, since we’re using an in-memory database which is H2, Spring will autoconfigure the data source for us as soon as it sees the dependency.
Our EntityManager is an interface and its main purpose is to talk to the PersistenceContext that manages our database operations and entities we’ve marked as persistent.
PersistenceContext manages operations on our entities, if some entity has been touched, it marks it as dirty, and when the transaction ends, it flushes the changes to the database. It is a bridge between our application entities and our storage. The persistence context is created at the beginning of a transaction, and “killed” at the end.
As you can see, I’ve injected our EntityManager instance that has been initialized as soon as the Hibernate saw H2 dependency on the classpath.
@Transactional — We need this annotation to specify that a certain method or an entire class, in this case, our repository, will be under a transaction. In case of updates or persisting new data, our methods need to be transactional. In case of a failure, the transaction rollback will happen.
Here we see a lot of methods being used to manipulate our data.
createQuery — we can use this to write JPQL queries
find — we use this to pull a single row from the DB by its primary key
persist — this is what makes our entity persistent and managed, but this does not fire our query to the database. The query will be triggered if we use the flush() method that actually flushes our changes to the database, or when the transaction ends which is the case with the insert method up there.
merge — this will actually update our persistent entity with new values being provided.
When do we need transactions?
Since our PersistenceContext is actually of type Transaction, we need to wrap our logic in a transaction when trying to persist, merge, remove something, or for the read operations when we have dependent entities.
It is also important to know that the @Transactional annotation works only on public methods.
Testing with JUnit
Let us first add dependencies so we can use testing libraries.
JUnit is probably the most popular Java testing framework. It offers us a lot of procedures and methods for testing our code.
It is a good practice to follow your project architecture when creating test architecture. For example, if you’re testing repository, it is a good practice to have a package called “repository” in a test folder.
Let’s take a look at how to write some tests for our Employee repository.
We’ve autowired our EntityManager, and annotated our class with SpringBootTest annotation.
@SpringBootTest — tells the Spring to look for the main configuration class and use it to run tests on it. This will run our JpademoApplication class, its context, and it will run our tests based on that.
@Test — this marks our methods as tests and it helps JUnit recognize them as tests. The important thing to notice is that they also need to be public.
Assertions — these are utility methods that help with verifying expected and given values for some test conditions.
assertEquals — so this is an Assertions public method that checks whether the give and expected values are matching.
Here we ran the findById test method and as you can see, the test passed, we’ve provided an expected id of the employee and a value that we got back from the database. On the right side, you can see our hibernate logs and the query being fired.
What if there is no @Transactional?
As you may notice here, I’ve removed the @Transactional annotation and I’ll now try to run this test where I’m trying to persist the new Employee. Will this fail?
javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread — cannot reliably process ‘persist’ call
Yes, and there is the error that we’ve got, since we’re trying to insert something, the logic must be wrapped in a transaction.
@DirtiesContext — This tells the Spring to clear the application context after the test has run, in other words, for this test, don’t save any of the data in an actual database.
Our tests should not change the state of our database after they’ve completed!
That would be some basics about Hibernate and JPA and how to work with databases in SpringBoot. You can easily replace the configuration in application.properties and use different databases.