Course Content
Java Data Manipulation with Hibernate
Java Data Manipulation with Hibernate
Implementing DAO Pattern for our Program
Any theory needs to be reinforced with practice. In the previous chapter, we learned about Hibernate's workflow, life cycles, and looked at the basic code that is used almost always. In this chapter, I suggest starting to implement the interaction between the database and the code, at least with the two entity classes we currently have. We will use the DAO pattern, meaning the logic of interacting with the database will be separated from the business logic of the application.
HibernateUtil
As you may recall from the previous chapter, creating a SessionFactory
object consumes a lot of resources. Therefore, we need to optimize the code so that this object is created only once during the program execution. We'll do this in the HibernateUtil.java
file.
We need to ensure that an instance of the SessionFactory
object is created and stored in a static constant, and not modified by other files. To do this, we will create a private method, initSessionFactory()
, in this class, which will configure and build the SessionFactory
object. Then, assign this object to the instance. Additionally, there should be a public static getter so that we only access this object through it.
Ultimately, this class will look like this:
This way, the SessionFactory
object will be created only once, and we will use it to create different sessions for manipulating objects. Later, we'll see how to use this utility correctly.
Note
Such utilities are often used in programming to ease code writing and optimize resources. We, as future programmers, will also use them. It's a very good practice.
DAO layer
Let's start with implementing the DAO layer. Just a reminder, this is the layer where the code interacts with the database, fetching and storing data. Here, we should split the code into an interface and an implementation class. The interface will serve as a template, defining all the methods we need to implement. The implementation class will implement them.
Let's start with the Employee
entity:
As you can see, we plan to implement just 2 methods for now: getById()
and add()
. Later, when we need other methods for the program, we can easily expand this code by adding new methods to this interface.
Next, we need to implement the implementation. It will be located in the EmployeeDaoImpl
class.
Now, we need to apply the theory from the previous chapter and use it to implement these 2 methods.
First, we need to create and use an instance of the SessionFactory
object. We'll do this using the HibernateUtil
utility we implemented before:
Great, now, with this sessionFactory
, we'll be able to open sessions in this class.
Note
Note that the variable is
private
, meaning it can only be used within this class. Also, this variable isn't a newSessionFactory
; it's an instance ofSessionFactory
created in theHibernateUtil
class.
Let's start implementing the add()
method.
We'll follow the template from the previous chapter:
Initially, in the method, we create variables session
and transaction
, initializing them as null. This is done to better track the state of these variables, in case of any issues, to close the session
or rollback the transaction
.
Then we follow the familiar algorithm:
In the catch
block, we catch potential errors, and if the transaction
was modified, we roll it back. Also, we throw a new exception, notifying that we cannot add a new employee.
In the finally
block, we also check if the session
object was modified, and then we close the session
within which we are working.
Now let's move on to the implementation of the getById
method, which takes an id as a parameter and returns an element from the database:
Here, the algorithm is also similar and straightforward. The difference is that we do not create a transaction
object.
Note
A transaction is used when our changes affect the state of the table in the database. Most queries from the READ group do not affect the database, so we do not use a
transaction
object when implementing this method.
Next is the familiar algorithm. In case we catch an exception, we throw a new exception notifying the user that we failed to retrieve an employee by ID. In the finally
block, we close the current session.
Note
Note that we use the same
sessionFactory
instance created in this file. We do not create a new instance for each method.
Service Layer
We have separated the code's interaction with the database into a separate layer. It's time to write the service layer, which will contain all the business logic of the application. At the moment, our application is very simple because we are just learning. Therefore, the service layer should mimic the methods of the DAO layer. Later, as the application develops and more complex logic is added, we will enhance the service layer.
The service layer should also have the structure of Interface - Implementation.
Let's implement the EmployeeService
interface:
I don't think there's much to comment on here; we simply replicated the methods from the DAO layer in this code. Right now, these are the methods we need.
Now let's write the implementation:
We use the instance of the EmployeeDao
object through composition.
- In the
add()
method, we simply use a method from the DAO layer; - In the
getById()
method, we check if we managed to retrieve an employee by ID, and if so, we return that object. If not, we throw aNoSuchElementException
, indicating that we couldn't retrieve an employee by ID.
At the moment, the implementation of the service layer looks simple, but let's add some additional logic to the application. For example, a method that retrieves the name of an employee by their ID. We should implement this method in the service layer because it's business logic and shouldn't directly affect the interaction with the database.
To start, let's add the method to the interface:
Now let's implement this method:
- First, we retrieve the
Employee
as an object using the previously implementedgetById
method; - Then, we create a variable to store the employee's name, fetching the name using a getter;
- Standard null check and return the employee's name from the method.
It's very important to add additional logic, specifically at the service layer. Yes, we could involve the DAO layer, but why add extra interaction with the database if we can avoid it?
Testing the Correctness of the Methods.
Now that everything is set up, we can finally use these methods.
We'll do this in the following algorithm:
- Instantiate the
EmployeeService
; - Create a new
employee
object and initialize its fields using setters; - Add the
employee
to the database; - Retrieve the
employee
by ID and print it to the console; - Retrieve the employee's name by ID and print it to the console.
Let's take a look at the implementation in the code:
As you can see, it's all quite straightforward. Next, all we need to do is run this code and thereby verify its functionality.
Great, we have fully implemented the database connection for one of our entities. In the next chapter, you will independently implement the same connection for the Department
entity. It will be excellent practice!
Thanks for your feedback!