Spring: A Head Start 🔥 — Spring Data Access (Part 6)
Working with databases using JDBC and Hibernate.
Setting Up The Environment 💪
📣 Before start, make sure you’ve the following:
- Database Server (XAMPP, WAMP, MySQL, etc)
- Hibernate JAR files (and it’s dependencies) and JDBC Driver.
If you’re using maven, then add the hibernate and JDBC driver (In our example it’s MySQL) dependencies in your “pom.xml” file.
<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.1.7.Final</version>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
Once you’re done, create users table, and generate some fake data for testing.
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(60) NOT NULL,
`email` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
)
JDBC
Spring JDBC module takes care of all the low-level details starting from opening the connection, prepare and execute the SQL statement, process exceptions, handle transactions and finally close the connection.
All what we need to do is just define the connection parameters and specify the SQL statement to be executed.
But, before using Spring data access modules, let’s see how can we make a plain old JDBC.
Basic JDBC Connection
There are basic 5 steps in order to connect and fetch data from a database.
So, first, you need to register a JDBC driver so that the driver’s class file is loaded and can be used as an implementation of the JDBC interfaces.
Then, we establish a connection by providing the connection URL, username and password.
💡 A database URL is an address that points to your database. In our example,
localhost
is the hostname, anddemoDB
is the database name. Different JDBC drivers have different corresponding URLs.
After that, we create an object of type Statement for executing an SQL statement, and returning the result set.
JDBC Connection In Spring MVC
Having a basic idea about how to make a JDBC Connection in Java is essential for building a Web Application in Spring that connects to database.
But, first, let’s see how can we structure our application.
— The Structure
- Data Access Layer (DAO): It’s commonly used for database interaction. They should expose this functionality through an interface by which the rest of the application will access them. The DAO support in Spring makes it easy to work with data access technologies like JDBC, Hibernate, etc.
- JDBC Template: The JDBC Template executes SQL queries, updates statements, stores procedure, etc. It also catches JDBC exceptions.
- Mappers: They map between the rows data in a table and java objects. In case of ORMs, like Hibernate, the mapping done using
@Column
on object’s field (will be discussed later). - Entities: They’re representation for the database tables. Each record in the table represents an entity object. So, entity objects’ fields represent table columns.
— JDBC Template & DAO
The JDBC in turn implements the DAO interface to provide the actual implementation code. Though it is not required, you can directly write the implementation code in DAO class, but not a good practice.
— JDBC Template & DataSource (i.e Connection)
A common practice when using the JDBC Template is to:
- Configure a DataSource bean in your Spring configuration.
- Dependency-inject that DataSource bean in the class implements the DAO.
- Create a new instance of JdbcTemplate in the setter for the DataSource.
JDBC — The Process
[1] Create Entities & Their Mappers
[2] Create The DAOs Interfaces
List down all the methods for database interaction.
[3] Create JDBC Implementation Class
Next is the implementation class for the DAO interfaces.
💡The JDBC Implementation Class has an instance of JdbcTemplate which is instantiated by using DataSource object (as a dependency) to do the work.
💡You can either inject the DataSource object using XML configurations (Setter Injection), or using Annotation (Field Injection).
💡 In case of using Annotation for injecting the DataSource. Use
@Autowired
annotation onjdbcTemplate
field, and remove thesetDataSource()
method (also from UserDAO).The DataSource bean will be injected to JDBC template bean in XML (see next).
[4] Define Configurations
Define configurations for the DataSoruce and JDBC implementing class.
💡 In case of using Annotation for injecting the DataSource.
[5] Working With JDBC Implementation Class In Controllers
In order to use the UserDAOJDBCImpl class in our controller, we need to inject it in our controller as a dependency, which can be autowired.
⚠ The UserDAOJDBCImpl class has to be defined as a bean so it can be injected.
Then, we can use it to invoke methods which will interact with the database and return the result.
Finally, you can construct the model object based on the data returned so that it can be displayed in the view pages.
[6] The View Pages
One page to display all the users, and the other one to create a new user using an HTML form.
Hibernate
Hibernate is an ORM framework that maps between java objects and database records. It acts as a middle layer between our application code and the database.
It handles the low level SQL code, minimize the amount of JDBC code, and provides object-To-Relational-Mapping (ORM). Actually, Hibernate uses JDBC in the background for database communications. So, It’s just a layer of abstraction on top of JDBC.
All what we to do is to tell Hibernate how our java object maps to a database table. This is done inside the object class (or in XML configuration). Hibernate will then get, store, update, or delete this object in the table based on the mappings we’ve defined.
A quick examples on how Hibernate makes it a lot easier …🏃
// Saving an object
User newUser = new User(“John”, “Doe”, “myemail.com”);// “session” is a special Hibernate object
int thePrimaryId = (Integer) session.save(newUser);// Retrieve an object
User user = session.get(User.class, thePrimaryId);// Quering object
// We pass what’s called “Hibernate Query Language” (will be discussed later)
Query query = session.createQuery(“from user”);
List<User> usersList = query.list();
Hibernate — The Process
[1] The Hibernate Configuration file
It tells hibernate how to connect to a database, which is the JDBC configuration, since it uses JDBC to connect to database. So, create a “hibernate.cfg.xml” file in “src” folder.
This file has the following:
[1] Session Factory
It reads the hibernate configuration file, gets a connection to database,
and creates the session objects, and it’s created once during the application.
💡A session is a wrapper around the JDBC connection. It’s the main object to save and retrieve objects to the database.
It’s short-lived object, so, for a given method, you use a session, and then trow it away, and so on. It’s created, and retrieved from the Session Factory.
[2] JDBC Database connection settings: These are the settings for JDBC connections such as JDBC driver, connection URL, username and password.
[3] Connection Pool: For simplicity, keep it 1 for now. It’s the max number of connections in the connection pool.
💡Hibernate make use of connection pool for connecting to the database.
Hibernate provides an internal connection manager, which is rudimentary. For the best performance and stability, use a third-party tool (‘ll do that later).
[4] SQL Dialect: It’s the SQL dialect your database uses. So, it tells Hibernate to generate the appropriate SQL statements for the chosen database.
[5] Show SQL: To print out the SQL used to communicate to the database.
[6] Session Context: The default context is thread
which means the session factory will bind the session to the thread from which openSession()
is called.
This is useful because you can later call sessionFactory.getCurrentSession()
which will return the session that is bound to the currently running thread.
[2] Annoate Java Classes (Entities)
An entity is a plain java class that mapped to database table. We use annotation to do the mappings. This is what’s called Object Relational Mapping (ORM).
💡There’s another way to define the mappings usign XML configuration file, which is the old way.
So, first, map a class to database table. Then, map the fields to database table columns.
⚠ Don’t forget to define constructors, getters and setters.
[3] Setup Session Factory And Create A Session Object
The session factory to create a session object which in turn will be used to interact with the database.
First, we load the configuration file, the default name configuration file is “hibernate.cfg.xml”. Then, the class(es) to be scanned for mapping, and we create the session object.
Finally, close the factory session. It destroys the SessionFactory and release all resources (caches, connection pools, etc).
[4] Start A Transaction
In Hibernate, you always use a database transaction when interacting with the database. Regardless if it’s a create, read, update, or delete operation. Database transactions are never optional in Hibernate.
// start a transaction
session.beginTransaction();// database operations
// ... We can wrap a set of queries inside a transaction// commit transaction
session.getTransaction().commit();
The commit()
statement indicates the end of the transaction, and commits the changes to the database. No changes will be applied to the database unless the transaction is committed.
Once you committed the transaction, you can’t use the session object anymore. You need to re-assign the session object to get a new session, and begin a transaction again.
session = factory.getCurrentSession();
[4] Save (Create) Object To Database
For every operation, don’t forget to get a new session object using factory.getCurrentSession()
, start the transaction, and commit.
// create a user object
User newUser = new User(125, "Alex", "alex@example.com");// save the user object
session.save(newUser);
[5] Reading Objects
// retrieve student based on the id: primary key
User user = session.get(User.class, newUser.getId());
There is a query language in Hibernate called HQL used to query the database. In HQL, we use the class name and property names, instead of table and column names.
// "User" is the name of the class
List<User> users = session.createQuery("from User").list();// "u" is an alias that maps to the actual java object (user),
// where u.name is a property of user object
List<User> users = session.createQuery("from User AS u where u.name='Alex'").list();
⚠ In Hibernate 5.2 or higher, the Query
list()
method has been deprecated, usegetResultList()
instead.
[6] Updating Objects
We can update user object using setters.
💡Since a persistent instance of the user object is returned by
get()
method (after being fetched from the table), updating this user object’s field value will also reflect on the table values.
// retrieve user based on the id: primary key
int userId = 125;
User user = session.get(User.class, userId);// update name to "Max"
user.setName("Max");
Or, we can write the query to update specific user(s).
session.createQuery("update User set name='Max' WHERE id=125").executeUpdate();
[7] Deleting Objects
The same idea in update applies here; We can either delete a persistent instance of the user object returned by get()
method, or, write and execute a query to do so.
// retrieve user based on the id: primary key
int userId = 125;
User user = session.get(User.class, userId);// delete the student
session.delete(user);
// delete user id=125 (using query)
session.createQuery("delete from User WHERE id=125").executeUpdate();
Thank you for reading! If you enjoyed it, please clap 👏 for it.