Exploring DAO and DTO in Java: Key Concepts and Usage
For Developers

June 18, 2024

Exploring DAO and DTO in Java: Key Concepts and Usage

An essential distinction between an inexperienced programmer and an experienced one is in their adeptness in effectively incorporating patterns.

While many individuals may create a functional implementation, a seasoned developer understands that once the task is completed, not only will the requirements likely change, but there will also be a need to maintain the codebase.

He will exert greater effort to produce a piece of writing that is coherent, maintainable, and extensible, even at the expense of speed. The experienced developer prioritises optimization only when there is a definite necessity for it.

Based on the above introduction, let us now examine the specific contexts in which the DTO and DAO patterns are applicable.

Work from anywhere, get paid well: Apply for remote Java projects on Index.dev. Sign up now →

 

Why Are These Patterns Important for Java Developers to Know?

The DAO and DTO patterns are important for Java workers to understand because they help them make apps that are scalable, easy to manage, and reliable. To write clean, fast code that follows best practices in software engineering, you need to understand these patterns. Because business apps are so complicated, they need developers who can use design patterns to separate issues, make testing easier, and make it easier to make changes in the future. Because of this, it's difficult to hire Java developers because there are so many people who need their skills. 

Read more: How to hire Java developers? The ultimate guide

Companies in many different fields need Java developers to build and manage important applications. This makes skilled developers who know these patterns very popular. Their knowledge is very important for making sure that applications not only work, but are also set up in the best way for long-term upkeep and expansion.

 

Data Access Object (DAO) Design Pattern 

The Data Access Object Pattern, sometimes known as the DAO pattern, is a method of structuring code to manage the interaction between a computer and a database. It aids in maintaining code cleanliness and segregates the logic for data interaction from the remaining parts of your program.

What Is the DAO Design Pattern?

The DAO (Data Access Object) design pattern is a blueprint for managing data in software, similar to an architect's plan. A blueprint is utilised by developers to establish a methodical and organised strategy for retrieving information from a data source, such as a database. The DAO design serves as a guide, abstracting the intricacies of data storage and retrieval, and providing a straightforward method to interact with the data.

Functionalities of the DAO Design Pattern

The DOA pattern facilitates efficient and consistent interaction with a database by abstracting the underlying difficulties.

The DAO design provides an abstraction layer that encapsulates the specific implementation details of data storage, retrieval, updating, and deletion in a database. This abstraction protects the rest of the program from the particular database implementation.

It consolidates all code related to databases into specialised DAO classes. This implies that the remaining parts of the program do not have to disperse database actions throughout their coding; instead, they communicate with DAO methods.

The DAO pattern facilitates code reusability by offering a collection of generic methods that may be utilised for various entities. Consequently, you may employ comparable database access patterns throughout different sections of your program.

Find high-paying, long-term remote Java jobs with top US, UK, and EU companies at Index.dev. Join now!

Why Do We Need the DAO Design Pattern?

Let us understand the necessity of the DOA pattern by examining an instance of an e-commerce application:

Suppose you are in the process of creating an e-commerce application. Throughout your application, it is necessary to engage with a database in order to manage user information.

Without using the DAO design, you may have the issue of spreading database-related code, lack of consistency, and scalability problems over various sections of your application. However, by utilising the DAO pattern, you may establish a centralised collection of methods that handle database activities specifically related to user entities. This technique has several advantages:

  • Structure: The source is structured and easily navigable by centralising all database-related code in the UserDao.
  • Consistency: The utilisation of a defined interface, known as the DAO interface, guarantees a uniform method of engaging with the database, hence minimising the probability of mistakes arising from disparate techniques.
  • Easy Maintenance: The UserDaoImpl class allows for easy maintenance by facilitating changes to the database structure or technology with minimal impact on the rest of the application.
  • Facilitating Testing: Unit testing becomes easier due to the presence of a well-defined set of methods to test within the UserDao.
  • Scalability: With the growth of the application, it is possible to incorporate more DAO interfaces and implementations, thus providing an architecture that can handle increased demands and is easy to maintain.

Read more: How an app commerce SaaS platform Poq hired senior front-end and QA talent remotely with Index.dev 

What Are the Key Components of the DAO Design Pattern?

The DAO pattern facilitates the attainment of separation of concerns, enhancing code organisation, maintainability, and adaptability to alterations in data storage or access logic through the utilisation of the following primary components:

1. BusinessObject

The BusinessObject serves as a representation of the client's data. The object necessitates access to the data source in order to acquire and store data. Business Objects serve as the fundamental elements in your program and are frequently employed to contain business logic. They can engage with Transfer Objects for the purpose of transferring data.

2. DataAccessObject

The DataAccessObject serves as the central component of this design pattern. The DataAccessObject serves as an abstraction layer for the BusinessObject, allowing seamless access to the data source by hiding the details of the underlying data access technology. It offers a consistent interface for executing CRUD operations on entities.

DAOs serve as a means to encapsulate the code that is special to the database. They enable the rest of the application to interact with data entities without needing to worry about the details of how the data is saved or retrieved.

3. Data source

This is an implementation of a data source. The Data Source is responsible for handling the connection to the underlying database. It is utilised by the DAOs to acquire connections and perform queries. It simplifies the process by hiding specific information such as connection pooling, database URL, and passwords.

4. TransferObject

This is a Transfer Object that serves as a data carrier. It is employed for transmitting data between a client and a server, or between several levels of an application. The DataAccessObject may also accept data from the client in a Transfer Object to modify the data in the data source. Transfer Objects facilitate the reduction of method calls between the client and server by consolidating various data fields into a singular object.

How components interact with one another:

  • The Business Object contains the fundamental business logic and communicates with the movement Object to facilitate the movement of data.
  • The DAO interfaces with both the Business Object and the Transfer Object. The system utilises the Transfer Object to facilitate the exchange of data between the database and the application. It may also collaborate with Business Objects to transform the data from the transfer format to the internal representation used by the application.
  • The DAO utilises the Data Source to acquire database connections required for conducting queries and modifications.
data access object design pattern

A Simple Implementation

Let's make a simple example to see how the DAO pattern works.

Okay, let's say we want to make an app that handles people. We don't want the application's domain model to know anything about the database. For that reason, we'll make a simple DAO class that will keep these parts nicely separate from one another.

The Domain Class

Since we want our app to work with people, we only need to describe one class to implement its domain model:

public class User {
    
    private String name;
    private String email;
    
    // constructors / standard setters / getters
}

The User class doesn't do anything else that's worth mentioning because it's just a store for user info.

The important design decision here is, of course, how to keep the app that uses this class separate from any persistence system that could be made.

That is exactly the problem that the DAO pattern tries to solve.

The DAO API

Let us set up a simple DAO layer to see how it can keep the domain model separate from the storage layer.

The DAO API looks like this:

public interface Dao<T> {
    
    Optional<T> get(long id);
    
    List<T> getAll();
    
    void save(T t);
    
    void update(T t, String[] params);
    
    void delete(T t);
}

At a glance, the Dao interface clearly describes an overall API that handles CRUD actions on T-type objects.

Interfaces offer a high level of abstraction, which makes it simple to make a real, fine-grained solution that works with User objects.

The UserDao Class

Let's talk about how to build the Dao interface for a particular user:

public class UserDao implements Dao<User> {
    
    private List<User> users = new ArrayList<>();
    
    public UserDao() {
        users.add(new User("John", "[email protected]"));
        users.add(new User("Susan", "[email protected]"));
    }
    
    @Override
    public Optional<User> get(long id) {
        return Optional.ofNullable(users.get((int) id));
    }
    
    @Override
    public List<User> getAll() {
        return users;
    }
    
    @Override
    public void save(User user) {
        users.add(user);
    }
    
    @Override
    public void update(User user, String[] params) {
        user.setName(Objects.requireNonNull(
          params[0], "Name cannot be null"));
        user.setEmail(Objects.requireNonNull(
          params[1], "Email cannot be null"));
        
        users.add(user);
    }
    
    @Override
    public void delete(User user) {
        users.remove(user);
    }
}

All the functions needed to get, change, and delete User items are built into the UserDao class.

To keep things simple, the users List is set up like an in-memory database and given a few User objects in the constructor.

For sure, it's simple to change the other ways so they can work with a relational database, for example.

The User class and the UserDao class can both live in the same application, but we need to figure out how the UserDao class can be used to hide the storage layer from the application logic:

public class UserApplication {

    private static Dao<User> userDao;

    public static void main(String[] args) {
        userDao = new UserDao();
        
        User user1 = getUser(0);
        System.out.println(user1);
        userDao.update(user1, new String[]{"Jake", "[email protected]"});
        
        User user2 = getUser(1);
        userDao.delete(user2);
        userDao.save(new User("Julie", "[email protected]"));
        
        userDao.getAll().forEach(user -> System.out.println(user.getName()));
    }

    private static User getUser(long id) {
        Optional<User> user = userDao.get(id);
        
        return user.orElseGet(
          () -> new User("non-existing user", "no-email"));
    }
}

Even though the case is made up, it gives you a general idea of why the DAO pattern exists. In this case, the main method only uses a UserDao instance to get, set, change, or delete a few User objects.

What's important about this process is that UserDao hides from the program all the low-level details of how the objects are saved, changed, and removed.

Calling Java Developers: Get top remote jobs with US, UK, and EU companies. Sign up at Index.dev!

 

Data Transfer Object (DTO) in Java

Enterprise application design models are very important when you have to deal with a lot of complicated data. These are the usual ways to fix problems that happen a lot with big systems. We can change, show, and keep huge amounts of data with enterprise applications. Before we start working on business apps, we should make sure that they don't have too many tight connections and that the data is safe and secure.

Data Transfer Object is a design technique that is what DTO stands for. It's an EPA pattern that we use when we need to send objects that hold and group data. A DTO is like a data structure, but it doesn't have any business logic inside it like a data structure does. It has tools for both serialising and deserializing data. We can keep data from a single source or from a number of different sources in DTO. We can store either all of the info from a source or just a small part of it.

If DTOs were built into the code, it means that data is sent from one system to another. The DTO is mostly used to cut down on expensive long-distance calls. The assembler object was set up so that data could be changed between the DTO and any entity objects. However, we are now using mappers to change data.

Implementing a DTO

It is possible to make a DTO out of a POJO or a Java Bean. The most important thing about a DTO is that it keeps things like the display layer and the domain model separate.

To show this, let's look at a small rest service. Let's say we have a coffee shop where people buy coffee and talk. They both belong to different domains in the system. If I want to find out what a customer's favourite coffee is, I'll make an API that gives me the FavoriteCoffeDTO info as a whole.

The code looks like this:

public class Coffee {
   private Long id;
   private String name;
   private List<String> ingredients;
   private String preparation;
}
public class Customer {
   private Long id;
   private String firstName;
   private String lastName;
   private List<Coffee> coffees;
}
public class FavoriteCoffeeDTO {
   private String name;
   private List<String> coffees;
}

In the above layout, I split the domain layer from the presentation layer. This lets my controller map the two domain entities to the DTO.

I made the fields private in this case, which means I need to write getter and setter methods to get to them. For DTO, users often choose to follow the JavaBean standard in whole or in part. You don't have to do this, of course; you can pick any way that works for you. You could also make all fields public and access them directly, like in the example below, or you could use an all-args constructor and some getter methods to make the object unchangeable.

public class FavoriteCoffeeDTO {

   public String name;
   public List<String> coffees;

}

String name = favCoffeeDTO.name;

One last thing: if you recently upgraded Java, you may want to use Java records for your DTOs. Records in Java are simple, unchanging classes that give you an all-args constructor, access methods, toString(), and hashCode() without you having to define them. You can read your code better because it is less complex. It is important to note that records do not follow the Java Bean definition because the entry methods do not start with "get" or “set.”

public record FavoriteCoffeeDTO(String name, List<String> coffees) {}

String name = favoriteCoffeeDTO.name();

Key Characteristics and Benefits

Data Transfer Objects (DTOs) include certain essential attributes that render them valuable for transferring data across different layers inside a Java application:

  • Immutability: DTOs are commonly constructed with the characteristic of being immutable, which implies that their attributes are unable to be altered once they have been formed. This feature ensures that the program is thread-safe and minimises the risk of data corruption.
  • Serializability: This refers to the ability of DTOs to be readily transformed into a format that can be stored or communicated over a network. This enables seamless transmission of data across different levels, from a service layer to a display layer.
  • Simplicity: DTOs are intentionally devoid of any business logic in order to maintain simplicity and minimise their weight. This enables its utilisation for data transport without introducing any additional intricacy to the program.
  • Accessors and mutators: Data Transfer Objects (DTOs) commonly consist of accessors and mutators for each property, enabling the retrieval and modification of their properties.
  • Separation of Data Handling Concerns: DTOs normally do not include methods for storing or retrieving data from a database, therefore they are not concerned with persistence. This effectively segregates the issues related to transferring data from the issues related to persisting data.
  • Tailored Data Representation: DTOs are specifically developed to offer an appropriate representation of data that caters to the specific requirements of each tier. For instance, the service layer may necessitate a more intricate depiction of data compared to the presentation layer, which may just require a fraction of the data.

DTOs enhance the speed, scalability, and maintainability of Java programs by segregating data transfer problems from business logic concerns.

The Pattern

DTOs, which stand for "Data Transfer Objects," are things that move data from one process to another so that there are fewer method calls. Martin Fowler first wrote about the pattern in his book EAA.

Fowler said that the pattern's main goal is to cut down on server calls by combining several inputs into a single call. In these kinds of remote activities, this cuts down on the network overhead.

One more benefit is that the logic behind serialisation is contained. This is the process that changes the structure of objects and data into a file that can be saved and sent. It gives you a single place to change the details of printing. It also separates the display layer from the topic models, so each can change on its own.

How to Use It?

Most of the time, DTOs are made as POJOs. They are flat data structures that don't have any business logic in them. Only storage, accessors, and ultimately means for serialisation or parsing are in them.

The domain models and DTOs are linked with data. This is usually done by a mapping component in the display or facade layer.

As you can see in the picture below, the parts work together:

When to Use It?

In systems with remote calls, DTOs are helpful because they cut down on the number of calls that need to be made.

DTOs are also useful when the domain model has a lot of different items and the presentation model needs all of their data at once. They can even cut down on the number of times data has to be sent back and forth between the client and server.

We can use DTOs to create different views from our domain models. This lets us make different versions of the same domain that meet the needs of our clients without changing the design of the domain. Being so flexible is a great way to deal with tough issues.

 

Key differences between DTO and DAO

Most of the time, DAO has CRUD actions like save, update, and remove. DTO is just a thing that stores information. Like any other JavaBean, it has setters and getters for instance variables.

When you think of a DAO as a proxy, the business tier is like its client. The business tier can get values from any resource, like an RDMS, LDAP server, XML store, OODB, flat files, and so on. This is how it looks:

your code -> DAO -> JDBC

The Value Object (VO) DTO is used to show a group of values in a way that looks like a bean. This is a simple way to send information over a network or between different levels of a program. 

The DTO will be sent to the DAO layer as a value object, and the DAO layer will use this object to store data using its CRUD operations.

If you need to change the persistent method underneath, you only need to change the DAO layer. You don't need to change all the places in the domain logic where the DAO layer is used. Most of the time, the DAO layer has fewer classes than the domain logic classes that use it. If you need to change something that happens behind the scenes in the DAO layer, it won't take as long to do because it will only affect that layer. 

The DTO layer is helpful because it gives the service layer and, by extension, the design of the whole program more freedom. If DTOs are used, for instance, a change in the needs that causes a move to a different amount of data doesn't affect the service layer or even the domain. You add a new feature to the DTO class in question, but you don't change the service layer's overall interface.

 

Conclusion

A sign of an expert engineer is that they can understand and use patterns like DAO and DTO in Java. The DAO pattern keeps the logic for accessing data separate from the logic for running the business. This makes the code easier to manage, scale, and test. It hides how the database works so that the CRUD tasks can be done in a clean and uniform way. The DTO pattern, on the other hand, makes it easy to send data between layers, which improves speed and cuts down on method calls in remote apps. DTOs keep things separate by making sure that they can't be changed and can be serialised. This makes the presentation model and the domain model very clear. 

Together, the DAO and DTO patterns help make the codebase organised, easy to manage, and customizable, which is very important for making business applications that work well. Using these patterns, developers can make systems that are easier to test, change, and add to. This makes software solutions more reliable and effective in the long run.

If you’re a senior software Java developer looking for high-end projects in the US, UK, and EU join Index.dev for a rewarding remote career.

Looking to hire high-performing Java developers remotely? See how Index.dev can help you:

🎯5% acceptance
🎯3x higher retention
🎯13+ months avg engagement
🎯97% trial-to-hire conversion rate
🎯30-day payback, 0$ until you hire

Transform your Java development with vetted experts. Get 3 to 5 interview-ready candidates in 48hrs!