Defining the DAO interfaces
An interface in the Java programming language defines a set of method signatures and constant declarations. Interfaces expose behaviors (or what can be done) and define a contract that implementing classes promise to provide (how it is done). Our DAO layer will contain one interface and one implementing class per domain object.
Note
The use of interfaces is an often misunderstood pattern in enterprise programming. The argument goes along the line, "Why add another set of Java objects to your codebase when they are not required". Interfaces do add to the number of lines of code that you write, but their beauty will be appreciated as soon as you are asked to refactor an aging project that was written with interfaces from the start. I have migrated an SQL-based persistence layer to a JPA persistence layer. The new DAO implementation replaced the old without any significant change in the service layer, thanks to the use of interfaces. Development was done in parallel to supporting the existing (old) implementation until we were ready to swap in the new implementation. This was a relatively painless process that would not have been as easily achieved without the use of interfaces.
Let's start with the company interface.
Adding the CompanyDao interface
- Navigate to File | New File from the menu and select Java Interface as shown in the following screenshot:
- Click on the Next button and fill in the details as shown in the following screenshot:
The name of the interface is CompanyDao
. We could have named this interface using the uppercase acronym CompanyDAO
. In keeping with the newer Java EE naming styles, we have decided to use the camel case form of the acronym. Recent examples of this style include the Html
*, Json
*, and Xml
* classes and interfaces, an example of which is javax.json.JsonObject
. We also believe that this form is easier to read. However, this does not prohibit you from using the uppercase acronym; there are many of these examples in Java EE as well (EJB
*, JAXB
*, and JMS
* interfaces and classes to name a few). Whatever you choose, be consistent. Do not mix forms and create CompanyDAO
and ProjectDao
interfaces!
Note that the package com.gieman.tttracker.dao
does not exist yet and will be created for you. Click on Finish to create your first interface, after which NetBeans will open the file in the editor.
The Company interface will define the persistence methods that we will use in our application. The core methods must include the ability to perform each CRUD operation in addition to any other operations appropriate to our business needs. We will add the following methods to this interface:
persist
: This method inserts a new company recordmerge
: This method updates an existing company recordremove
: This method deletes a company recordfind
: This method selects a company record using a primary keyfindAll
: This method returns all the company records
Note that the JPA terminologies persist
, merge
, remove
, and find
are equivalent to the SQL operations insert
, update
, delete
, and select
. Add the methods to CompanyDao
as shown in the following code:
package com.gieman.tttracker.dao; import com.gieman.tttracker.domain.Company; import java.util.List; public interface CompanyDao { public Company find(Integer idCompany); public List<Company> findAll(); public void persist(Company company); public Company merge(Company company); public void remove(Company company); }
We have defined a contract that the implementing class must promise to deliver. We will now add the ProjectDao
interface.
Adding the ProjectDao interface
The ProjectDao
interface will define a similar set of methods to the CompanyDao
interface:
package com.gieman.tttracker.dao; import com.gieman.tttracker.domain.Company; import com.gieman.tttracker.domain.Project; import java.util.List; public interface ProjectDao { public Project find(Integer idProject); public List<Project> findAll(); public void persist(Project project); public Project merge(Project project); public void remove(Project project); }
You will note that all method signatures in the ProjectDao
interface are identical to the CompanyDao
interface. The only difference is in class types where Company
is replaced by project
. The same situation will occur in all the other interfaces that we are going to add (TaskDao
, UserDao
, and TaskLogDao
). Each of the interfaces will require a definition for the find
method that will look like the following code:
public Company find(Integer idCompany); // in CompanyDao public Project find(Integer idProject); // in ProjectDao public Task find(Integer idTask); // in TaskDao public User find(Integer idUser); // in UserDao public TaskLog find(Integer idTaskLog); // in TaskLogDao
As you can see, the only functional difference in each of these methods is the returned type. The same can be said for the persist
, merge
, and remove
methods. This situation lends itself perfectly to the use of Java generics.
Defining a generic DAO interface
This interface will be extended by each of our DAO interfaces. The GenericDao
interface uses generics to define each method in a way that can be used by each descendent interface. These methods will then be available free of cost to the extending interfaces. Rather than defining a find(Integer id)
method in each of the CompanyDao
, ProjectDao
, TaskDao
, UserDao
, and TaskLogDao
interfaces, the GenericDao
interface defines the generic method that is then available for all descendants.
The generic interface definition looks like this:
package com.gieman.tttracker.dao; public interface GenericDao<T, ID> { public T find(ID id); public void persist(T obj); public T merge(T obj); public void remove(T obj); }
We can now refactor the CompanyDao
interface as follows:
package com.gieman.tttracker.dao; import com.gieman.tttracker.domain.Company; import java.util.List; public interface CompanyDao extends GenericDao<Company, Integer>{ public List<Company> findAll(); }
Note the way in which we have extended the GenericDao
interface using the <Company, Integer>
types. The type parameters <T, ID>
in the GenericDao
interface become placeholders for the types specified in the CompanyDao
definition. A T
or ID
that is found in the GenericDao
interface will be replaced with Company
and Integer
in the CompanyDao
interface. This automatically adds the find
, persist
, merge
, and remove
methods to CompanyDao
.
Generics allow the compiler to check type correctness at compile-time. This improves code robustness. A good explanation of Java generics can be found at http://docs.oracle.com/javase/tutorial/extra/generics/index.html.
In a similar way, we can now refactor the ProjectDao
interface:
package com.gieman.tttracker.dao; import com.gieman.tttracker.domain.Company; import com.gieman.tttracker.domain.Project; import java.util.List; public interface ProjectDao extends GenericDao<Project, Integer>{ public List<Project> findAll(); }
Let's continue with the missing interfaces in the same manner.
The TaskDao interface
Apart from the common generic methods, we will once again need a findAll
method. This interface looks like the following code:
package com.gieman.tttracker.dao; import com.gieman.tttracker.domain.Project; import com.gieman.tttracker.domain.Task; import java.util.List; public interface TaskDao extends GenericDao<Task, Integer>{ public List<Task> findAll(); }
The UserDao interface
We will need a list of all the users in the system as well as a few finder methods to identify a user by different parameters. These methods will be required when we develop our frontend user interfaces and service layer functionality. The UserDao
interface looks like the following code:
package com.gieman.tttracker.dao; import com.gieman.tttracker.domain.User; import java.util.List; public interface UserDao extends GenericDao<User, String> { public List<User> findAll(); public User findByUsernamePassword(String username, String password); public User findByUsername(String username); public User findByEmail(String email); }
Note that the UserDao
interface extends GenericDao
with a String
ID type. This is because the User
domain entity has a String
primary key type.
The TaskLogDao interface
The TaskLogDao
interface will also need a few additional methods to be defined in order to allow different views into the task log data. These methods will once again be required when we develop our frontend user interfaces and service layer functionality.
package com.gieman.tttracker.dao; import com.gieman.tttracker.domain.Task; import com.gieman.tttracker.domain.TaskLog; import com.gieman.tttracker.domain.User; import java.util.Date; import java.util.List; public interface TaskLogDao extends GenericDao<TaskLog, Integer>{ public List<TaskLog> findByUser(User user, Date startDate, Date endDate); public long findTaskLogCountByTask(Task task); public long findTaskLogCountByUser(User user); }
Note that our finder methods for the TaskLogDao
interface have descriptive names that identify the purpose of the method. Each finder method will be used to retrieve a subset of task log entries that are appropriate for the business needs of the application.
This covers all the required interfaces for our application. It is now time to define the implementations for each of our interfaces.