Spring Data JPA, Hibernate, ORM & Repository (Part 4)
This tutorial explains more about Spring Data JPA, ORM tools used to interact with Databases in Spring applications and how inversion of control helps in Spring.
Recap about my previous post
This post is a continuation of my previous post.
As part of this tutorial, we will learn about
- What is Java Persistence API (JPA)
- How hibernate ORM is being used to configure Entities in Java
- Get to know about CrudRepository and its default operations(methods) and how to take advantage of it.
- Span MySql database in local machine using Docker
What is JPA?
JPA is a specification which means a set of guidelines to be followed to represent Java objects in databases. The JPA provides a set of concepts in the form of interfaces & annotations to configure Java objects. As a result, Java object mapped as relational database tables. Relationship between Java objects would be established by adding relational annotations (one-to-many, many-to-many, one-to-one, many-to-one). If JPA is a specification, who is providing implementation? ORM tools are providing implementation for JPA. Hibernate, EclipseLink or other JAVA ORM tools are providing an ORM implementation.
In Reality, we can use ORM tools without the JPA specification. Then what is need of JPA? — For Example, EclipseLink is being used as an ORM tool for one of the project. EclipseLink has its own set of methods, configuration, annotations, etc. to map objects with relational database. In the middle of project execution, If there is a need to switch to a different ORM tool, say Hibernate, then we have to learn Hibernate way of doing configurations which in turn end up with code refactoring. This is one of the reasons for JPA existence. It abstracts common patterns and define specifications so that ORM vendors such as Hibernate, EclipseLink or others will make use of it to provide their own concrete solutions. From the developer's point of view, JPA is somewhere between ORM and the application layer and the abstract ORM responsibilities.
Configure Spring for JPA
Add required dependencies
Let’s check out the source code from my post in github(Checkout ‘data-jpa-begin’ branch)
Add the following dependencies into gradle.build
Refresh dependencies and run the app and check.
Errors. If we watch closely, url is not defined and the datasource is not configured. These errors are all because of the newly added dependencies. Data JPA dependency is expecting us to configure datasource and URL. We need a datasource info to use in the Spring. Let’s span a MySql database in local machine using Docker container.
Setup MySql database on local machine
Here, we can find how to span MySql container locally (Local machine) using docker commands. Docker-compose is being used to configure mysql container. If would like to know more about Docker, please check out the official website.
Install Docker & docker compose and execute in terminal.
docker-compose --version
Create a docker-compose.yml file in root directory and add the following configurations
Run ‘docker-compose up’ and it will span mysql database on localhost(127.0.0.1).
docker-compose up
- URL: jdbc:mysql://127.0.01:3306/em-dev
- Host: 127.0.01
- User: dev
- Password: password
- Database: em-dev
- Port: 3306
Open new tab in terminal and issue the following command to know about my-sql container
docker ps
Next time we can start the docker container using container Id
docker start 0ee6ddeba0ca
Connect to DB
You can use any database tool to connect/check the database schema. I have used the IntelliJ Database tool to connect.
Datasource configuration
We have a database and configure the url, username & password in application.properties
Let’s run and check the results.
Why? Because we haven’t added MySql Jdbc driver as a dependency. Let’s add it in build.gradle
Let’s run and check
See the log. Hibernate core started using MySql connecter to establish a connection with MySql server.
Let’s map Model to Database Table: JPA Specification
Model class is a very important entity to apply configurations, such as, table name, property type and constraints, relationships & etc., which in turn will be mapped to Databases. For Example, Account model will be mapped to the Accounts table in Database just by adding a few annotations on it.
- Entity: Adding it on Model it will be treated as Entity.
- Table: Model will be mapped to database table
- Id: Specifies the primary key of an entity
- GenerateValue: It has four strategies to generate Id Value. We have to choose any one of the given value or we can add custom strategy.
Constraints: Javax.validation package has constraints to be applied on attribute/field level.
Let’s add JPA specification to Account model.
Here, we have configured Account model to be mapped to the accounts table with a primary key constraint along with two more attributes.
Add SQL Configure logging into the application.properties for proper logging
We are using ddl-auto as an update — Changes which we do in Java code would reflect in the database. For Ex, We have annotated Account model as table and added three attributes. During the execution of the application, the Java annotations will ensure that the Account table will be created with the appropriate columns as well as its constraints.
Let’s run and see the results.
A new table (accounts) got created with the constraints which have configured in Account model class. Now our Account Entity changes propagated to the accounts table in MySql database.
Repository
- Indicates that an annotated class is a Repository. It is a special version of Component annotation.
If a class is being annotated with Component, then, it will be scanned by Spring and it will be available on Spring Container.
AccountRespository extends JPARepository extends PagingAndSortingRepository extends CrudRepository. What does this mean.
- CRUD operations of Account model have been configured by Spring
- Retrieval operations are Pageable
- Retrieval operations are Sortable
We extend JPARepository interfaces for Database operations, but actual implementations can be taken care by Spring.
Let’s use Repositories on Controllers (IOC & DI)
- AccountRestController declared accountRepository attribute as AccountRepository (‘private AccountRepository accountRepository;’) type.
- Configured the constructor as AutoWired: Autowire is being used for automatic configuration in Spring. This annotation search for AccountRepository interface’s implementation of Spring Container.
- Repository annotation: Spring provides an implementation using ‘SimpleJpaRepository’ class for AccountRepository interface and it is available in the Spring Container (Because of Component Annotation).
Let’s evaluate it by setting breakpoint in AccountController constructor.
- Injection: Instance of SimpleJpaRepository (AccountRepository implementation) will be assigned to AccountRestController’s accountRepository (Of type AccountRepository) attribute while constructing AccountRestController class.
The above process is one of core functionality of Spring and It is called as Inversion Of Control(IOC) by Dependency Injection(DI)
As per Spring Doc the IOC definition is
IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies, that is, the other objects they work with, only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse, hence the name Inversion of Control (IoC), of the bean itself controlling the instantiation or location of its dependencies by using direct construction of classes, or a mechanism such as the Service Locator pattern
Now, we have an interface and an implementation for CRUD operations, Let’s code for controller which in turn will invoke repository to make necessary changes in the database.
Controller Endpoints
Insert operation — Post (/accounts)
Let’s post an account using POST. We have invoked save method of repository to do so.
Let’s build and run in PostMan
And Check Database for newly created accounts.
New Account is being created and Id is being returned along with account info.
Let us see what happens when the postman invokes /accounts POST endpoint
- Postman calls /accounts POST endpoint
- AccountController’s createAccount method got invoked
- AccountController instantiates AccountRepository through constructor injection
- addAccount method invokes AccountRepository’s save method
- AccountRepository persists the into DB and returns updated information
- AccountController returns what it gets from AccountRepository
- Postman shows the response
Let us add few more accounts either by running the following SQL, or by manually calling accounts POST endpoint
Retrieve operation — GET (/accounts & /accounts/{account-id})
Let us refactor accounts() & accountById() methods. Pageable is an interface and it provides attributes to get responses based on paging. The return value is encapsulated with Page. Using the Pageable attributes passed as a request parameter on each service would return the values appropriately.
For ex, page size is 3, sort account id in descending order & need a second page then the request would be ‘/accounts?page=0&size=3&sort=id,DESC’
- Pageable paginate request parameters
- return this method returns paginated accounts
Let us run and hit the service without any pagination parameters, then it would take default values and return the responses accordingly.
Update operation — PUT (/accounts/{account-id})
Let us create a method updateAccount and do the following
- PutMapping (“/accounts/{accountId}”) Make this method get invoked while updating accounts
- PathVariable extracts accountId from url
- Valid validates request body
- RequestBody passes the request as input parameter
- return this method returns updated account information
findById returns Optional which means it returns either an Account or No Value.
- If No Value then we are returning a 404 HTTP status
- If the Account info is available, then we are merging the account retrieved from DB (accountFound) with a request body (account). The mergeAccount method of the Account model will merge it
Let us run!!!
Delete operation — DELETE (/accounts/{account-id})
Let us create a method deleteAccount and do the following
- DeleteMapping(“/accounts/{accountId}”) Make this method get invoked while deleting accounts
- PathVariable extracts accountId from url
- return: this method returns success after deleting
We have achieved our goal of creating restful services for accounts to be managed in a database using SpringBoot.
Final code can be found on github at ‘data-jpa-end’ branch.
Ragunath Rajasekaran has experience in developing full-stack applications using SpringBoot, Node, React and GraphQl technologies. He is also the AWS & Azure cloud specialist who uses the infrastructure as code (IAC) via Terraform. He worked with Spark & Hive Big Data technologies. He debuted his career as an iOS developer.
Let’s connect to Ragunath Rajasekaran through LinkedIn, GitHub, Dev.to, Medium Email Subscription