Using Spring Data JPA for data access layer
Introduction
We started with a simple hello world application and we covered up to how to set up our database schema flyway. We are now ready to write some code which will interact with database.
Before we jump on to code let’s look at a bit of history.
Java has a nice JDBC api which helps us query database. Making it a base many ORM tools come into existence, Hibernate, Mybatis, Toplink to name a few.
ORMs bridged the gap between JDBC and objet orientedness with how we perform database operations and mapped them to some objects. Though we have many different opinions about penalty of using ORMs but in practical we see a huge adotion of this tools and specially Hibernate.
JPA is a specification and is an attempt to uniform the APIs used by many ORMs .
If we look at Java based applications today JPA+Hibernate has become the defacto choice for relational databases.
Spring brings more utility that makes life a lot easier for developer.
Now this post is not a Hibernate or JPA tutorial rather it is a simple Spring tutorial on how to use Spring’s support for JPA and Hibernate.
The dependencies
Like always we have a starter named spring-boot-starter-jpa
. Below is the dependency
What does it bring to the application ?
We see it brings hibernate core dependencies and JPA dependencies.
A small note here JPA which was previously used to be known as Java Persistence API has now renamed to Jakarta Persistence API due to naming rights issues.
What Spring data jpa provides is
- A Repository interface to auto generate most boiler plate query pattern.
- Support for annotation driven transaction mechanism .
- Easy auditing for entities .
- Support for paginations, filter and so on.
So now that we have our dependencies in place, let’s start with code.
Our entity class is simple and looks like below
(imports are ommitted for brevity)
It’s a simple JPA entity with id field as identifier. Now comes the best part, we ofcourse need some code to store and retrieve products from database. Good news is Spring has got us covered. All you need to do is define a repository as below -
Repository
That’s it spring will generate all boiler plate basic query like persists, findAll to name a few .
JpaRepository also supports generating query to find by some column of the entity for example id, name, stock, manufacturer,created on.
All we need is a method named findBy<propertyName>
. For a list of supported methods read https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
Let’s try to fetch a product by id .
Below is ProductService
which takes the Product
DTO as input and stores into database.
All we need to do after creating our entity is to call productRepository.save(productEnity)
.
I have not used any transactions, because JpaRepository itself works in a transaction. Also in this simple example I am not lazily loading any properties from the entity so it is fine to ommit the transaction .
Logging
We may want to check what SQL hibernate is generating for that we can use below properties
In output we will see below logs
What if we want to see the actual inputs passed in the insert statement. Well there is no direct property but we can enable logs like below
Our application will churn out logs -
Let’s try to get all products -
We again going to take help from Spring JPA repositories and our code will look something like -
Pagination
Well the above works but there’s a basic issue, there may be a huge number of products, in that case we need pagination support. Spring repositories are here to rescue us again.
We need to modify our ProductRepository
class a little -
Our method signatures change this time and most of the method takes an Pageable
type .
The modified method looks like-
Notice how the return type changes from List<Product>
to Page<Product>
. A Page type contains information like total page count and total items.
We can also verify in our application log that select queries are not using limit
and offset
instead of doing a select all .
Note
Response from our controller also needs to be changed to accomodate the new pagination information.
Auditing
If we look into our save
method in ProductService, we are setting the value of createdOn
field to be current date time. Although in this demo context there is nothing wrong in doing that , there is a better way to populate this fields.
Spring data jpa provides auditing fetaure via AuditingEntityListener
. This provide a bunch of annotations that populate a field before or after an event.
Let’s try to populate our createdOn
field.
- We first need to add
@EntityListeners(AuditingEntityListener.class)
to ourProductEntity
class. - We need to provide a bean of type
DateTimeProvider
which will be responsible for providing a current time. Because we are usingOffsetDatetime
we create a bean like below which gives anOffsetDatetime
.
Just like timestamp we can also add an auditorAwareRef
which returns an AuditorAware<T>
. Let's add a new column to our ProductEntity
and we add a bean like below
if we now create a new product we will see test-user
has been set as createdBy in database.
Note
Adding a constant
test-user
is only for example purpose. Getting the real user name may involve getting it from ThreadLocal, SecurityContext, Auth Header or from anything else suitable for your context.
We also have other annotation LastModifiedBy
and LastModifiedOn
to capture modification auditing.
Some More features
@Query
- Sometime even the provided repository methods will not be enough for our usecase. May be we need a more complex query, in that case we can add a method and use@Query
annotation to specify our sql query. If we setnative=true
we can provide a native SQL query not a JPQL one.Specification
- Our repository can also inherit from JpaSpecificationExecutor which provides method that takes anSpecification
type . We can make use of JPACriteria
query to build more nuanced and complex queries.
Spring data jpa is a big module and not everything can be covered in a single post. However we now can create an entity and know how to persist it and query it. In future posts we will see more features from spring-data-jpa.
That’s it from this post .